自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(0)
  • 资源 (7)
  • 收藏
  • 关注

空空如也

PHP小舍微信拼团独立商城系统v6.1多商户版

PHP小舍微信拼团独立商城系统v6.1多商户版,非常不错的独立微信拼团商城,可以团购也可以直接购买,并且带有微信分销分佣功能!但是此程序只能适用微信端的团购活动,喜欢简单的朋友可以试试,如果又想用在微信和电脑端的,建议选择其余综合型的商城系统,此程序只适用于微信端!

2019-04-29

基于PHP的GPS定位

一个简单的PHP+MySQL+JS+GoogleMapAPI

2018-01-30

DOS编程工具包

DOS下的网络编程 第一章 安装网卡的DOS驱动程序 网卡的DOS驱动程序一般由硬件的生产厂商提供,微软的DOS网络安装包<Microsoft Network Client Version3.zip>在本目录下,里面也包含了很多早期的网卡驱动。在本目录有一个WAFER-C400小主板上网卡的驱动程序<网卡RTL8139驱动(DOS).rar>,非常通用,我们大部分计算机都可以使用它。 <Microsoft Network Client Version3.zip>解压后,把里面的文件都拷贝到DOS系统盘c:\netsetup目录下,把<网卡RTL8139驱动(DOS).rar>解压后拷贝到c:\netsetup\driver目录下。 运行c:\netsetup\setup.exe,当然,是在MS-DOS系统中运行,按照步骤操作。 在下面的列表中选择自己的网卡,因为这些都是早期的型号,我们使用的网卡在这里一般是不会找到的,所以选择“Network adapter not shown on list below ...”。 然后输入网卡驱动所在的目录“c:\netsetup\driver”,程序自己就会找到目录下的驱动,回车进行下一步,随意输入个用户名,例如“yangzhpeng”。到这一步要对自己的网络细致地设置了,不能贸然进行下一步。 其实我们要设置的项也不多。修改用户名,机器名,工作组和域不用管。 修改网络配置,把NWLink协议去掉,添加上TCP/IP协议。并且修改TCP/IP协议的设置。 把Disable Automatic Configuration设置成1(很重要),修改IP地址和掩码。 现在就可以放心地安装了。安装之后重启计算机。 第二章 检查DOS下的网络配置 在上一章安装完成之后,有三个文件对我们很重要。第一个是c:\config.sys文件,有句话“device=C:\NET\ifshlp.sys”,是安装程序给写上的。 第二个文件是c:\autoexec.bat,安装程序在文件的最后添加了这些话: C:\NET\netbind.com C:\NET\umb.com C:\NET\tcptsr.exe C:\NET\tinyrfc.exe C:\NET\nmtsr.exe C:\NET\emsbfr.exe C:\NET\net start 我们把修改成: C:\NET\net initialize C:\NET\netbind.com C:\NET\umb.com C:\NET\tcptsr.exe C:\NET\tinyrfc.exe C:\NET\nmtsr.exe C:\NET\emsbfr.exe C:\NET\sockets.exe 网络驱动安装完毕,下一步要编写程序。 第三章 编程环境 需要4步:(1)假定使用Borland C++ 4.5,假定安装在c:\bc45。 本目录下面有一个Microsoft TCPIP Sockets Development Kit Version 1.0的压缩包<MSTCPSDK.rar>。解压后,把MSTCPSDK\INCLUDE\中的全部文件拷贝到c:\bc45\INCLUDE\中。 (2)如果编写的是C++程序,还要修改\INCLUDE\SOCKDEFS.H文件,把全部内容用extern "C" {……}圈起来。 (3)程序中应该包括下面的头文件: #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> (4)还应该把MSTCPSDK\LIB\DOS_SOCK.LIB文件加入到程序工程中。 如果觉得这样做比较麻烦,那就使用我整理的文件,在本目录中的dos_sock.rar。只有两个文件dos_sock.h和dos_sock.lib,把这两个文件加入工程中,然后#include “dos_sock.h”就可以了。 第四章 SOCKET概述 1 TCP/IP网络模型 TCP/IP是Transmission Control Protocol / Internet Protocol(传输控制协议/网际协议)的缩写。它最初是在20世纪70年代由美国国防部出资为ARPA(美国高级研究项目局)开发的。经过多年的演变,以TCP/IP协议为基础构建的ARPA网逐渐成了今天的Internet。 TCP/IP协议的核心协议包括TCP,UDP和IP协议, 运行于传输层和Internet层上,其中TCP和UDP协议是以IP协议为基础而封装的,这两种协议提供了不同方式的数据通信服务。 如果说IP协议是道路,那么下一层网络访问层的各种协议就相当于不同的铺路材料,而上一层的TCP和UCP协议就相当于路上跑的不同类型的车辆;再上层应用层的各种协议就车上丰富多彩的货物,它们都是以TCP和UDP协议为载体完成的。比如,HTTP协议适使用TCP协议传输网页,POP3协议使用TCP协议传输邮件,而DNS协议使用UDP协议来传输域名和IP地址的翻译信息。 2 IP地址和端口 在使用TCP和UDP协议通信时,必须同时指定IP地址和端口号才能完整地标识一个通信地址,在编程中通常将这两个参数一起定义在一个sockaddr_in结构中来使用。 struct sockaddr_in { short sin_family; //地址格式 unsigned short sin_port; //端口号(使用网络字节顺序) struct in_addr sin_addr; //IP地址(使用网络字节顺序) char sin_zero[8]; //空字节 }; 结构中的sin_family字段用来指定地址格式,在不同的操作系统下,取值可以指定为AF_UNSPEC,AF_UNIX或AF_OSI等不同的值,但是在这里我们只能使用AF_INET。 sin_addr字段是个in_addr结构,这个结构实际上就是4个字节。 3 网络字节顺序 不同的处理器对字节顺序的处理方式不同,有的是高位在前,有的是低位在前。TCP/IP协议统一规定使用高位在前的方式传输数据,很遗憾,这与Intel80x86系列处理器使用的方式不同,所以在80x86平台下的socket编程中,当需要在协议中使用参数时,必须首先将它们转换为Internet顺序。 在填写sockaddr_in结构的sin_port字段和sin_addr字段时,必须首先进行转换。下面就是一些字节转换函数: 16位: unsigned shorthtons(unsigned short); //主机顺序-->网络顺序 unsigned shortntohs(unsigned short); //网络顺序-->主机顺序 32位: unsigned long htonl(unsigned long); //主机顺序-->网络顺序 unsigned long ntohl(unsigned long); //网络顺序-->主机顺序 4 IP地址转换函数 unsigned long inet_addr(char far *); 将“aa.bb.cc.dd”类型的十进制字符串转换成32位的IP地址。如果失败,返回INADDR_NONE。 char far * inet_ntoa(struct in_addr); 将网络字节顺序的32位IP地址转换成字符串。返回一个指针,指向转换后的IP地址字符串,这个字符串位于socket接口的内部缓冲区,所以,在调用inet_ntoa后必须马上把字符串拷贝到自己定义的缓冲区中。 5 套接字 两个主机之间进行网络传输,首先必须建立一个用来通信的对象,这个对象就称为套接字(socket),套接字的定义是“通信的一端”,在通信的另一端必定有另一个套接字与之相对应,以便互相传递数据。仅从编程的角度来看,套接字就是一个整数标识符而已,但使用socket这个称呼好像更贴切。 套接字的种类有很多种,最主要的是流套接字(stream socket)和数据报套接字(datagram socket)。由于流套接字使用传输层的TCP协议进行通信,所以它具有TCP协议所拥有的各种特征,比如:它是面向连接的、稳定的,以及数据包是按顺序发送的;而数据包套接字使用UDP协议进行通信,所以数据包可能丢失,可能重复,以及可能不按顺序到达等。一般将这两种套接字更直观地称为TCP套接字和UDP套接字。 还有一种原始套接字(raw socket),可以直接在Internet层上处理IP数据包首部,所以可以用它是个各种特殊的功能,如伪造发送者地址等。 由于我们在工作实际应用中使用的是TCP协议,所以在本篇也只讨论TCP套接字。对应于dos_sock.lib文件,我们把MS-DOS编程中使用的socket叫做dos_sock。 6 套接字的创建和关闭 创建套接字使用socket函数: int socket(int af, int type, int protocol); 函数使用的第一个参数af用来指定套接字使用的地址格式,和sockaddr_in结构的sin_family字段的定义是一样的,唯一可以使用的值是AF_INET。 第二个参数type用来指定套接字的类型。正如前面介绍的,套接字有流套接字(SOCK_STREAM)、数据报套接字(SOCK_DGRAM)和原始套接字(SOCK_RAW)等。 protocol参数配合type参数的使用,用来指定使用的协议类型,当type参数指定为SOCK_STREAM或者SOCK_DGRAM的时候,系统已经明确使用TCP和UDP协议来工作,所以,这时这个参数并没有什么意义,可以指定为0。但是当type参数指定为SOCK_RAW类型的时候,使用protocol参数可以更详细地指定原始套接字的工作方式。 当套接字被成功创建的时候,函数将返回一个套接字句柄,否则函数将返回INVALID_SOCKET(定义为-1或者~0)。 当不需要使用套接字的时候,使用closesocket函数将它关闭: int closesocket(int s); s就是创建套接字时返回的套接字句柄。 7 套接字的工作模式 socket的使用分为两种模式:阻塞模式和非阻塞模式。阻塞模式也称为同步模式,socket函数会等待操作完成之后才返回。比如,使用recv函数接收数据时,如果函数被调用时没有数据可收,那么函数不会返回,直到收到一些数据为止。再比如使用send函数发送n字节的数据时,在全部n字节发送完之前,函数不会返回,所以这时,程序处在等待状态。 非阻塞模式又称为异步模式,同样是前面的情况,recv和send函数执行后会立即返回,等到数据数据到达或者链路空闲可以继续发送数据时,socket接口会通过某种形式通知应用程序。 Windows中使用的Socket(WinSock2)默认是阻塞模式,不知道dos_sock的默认状态是阻塞还是非阻塞,没有找到相关资料。设置阻塞模式的函数是: int ioctl(int s, int cmd, char far *pArgument); s是套接字句柄,cmd是要执行的操作,pArgument是cmd要设置的参数。函数执行成功返回0,出错返回-1。 设置dos_sock为非阻塞模式: DWORD value = 1; //非0非阻塞 int result = ioctl(s, FIONBIO, (char far *)(&value)); 设置dos_sock为阻塞模式: DWORD value = 0; //是0是阻塞 int result = ioctl(s, FIONBIO, (char far *)(&value)); 8 使用setsockopt设置套接字的属性 函数的原形是: int setsockopt( int s, //套接字的句柄; int level, //属性的分类; int optname, //要设置的属性; char far *optval, //要设置的属性值; int optlen //属性值参数的长度。 ); 比如,允许套接字绑定到已经使用的端口: DWORD value = 1; int result = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char far *)(&value), sizeof(value)); 更详细的内容请参考本目录MSTCPSDK.rar中的winsock.txt。或者查看MSDN,内容大致相同。 9 错误代码errno errno是Borland C++编译器定义的一个整数型全局变量,很多函数把执行时发生的错误写到errno变量中,也会把执行中执行后的状态写到errno变量中,经常遇到的有以下这些: #define EISCONN 118 //socket已经连接上 #define ENOTCONN 119 //socket没有连接上 #define EINPROGRESS 126 //函数正在执行中 第五章 TCP编程模型 和UDP协议有所区别,TCP协议的特点在于: TCP采用超时重传及机制来保证不丢失数据,当一个TCP发送一个数据包后,它启动一个定时器,等待对端确认收到这个包,如果在指定的时间内没有得到确认,将重发这个包。而接收数据包的时候,它将发送一个确认,如果检测到数据包有错,TCP协议丢弃这个数据包,并且不发送确认,那么对端会因为超时而重新发送这个数据包。 一组有序的数据包,到达对端时可能会有后发的数据先到的情况,TCP协议在包首部保存数据包序号,如果有必要,它将对收到的数据重新排序,并以正确的顺序交给应用层。 1 客户端和服务器端的工作模型 TCP的工作方式用上图的客户端/服务器端模型来描述,通信的发起方称为客户端(Client),通信的等待方称为服务器端(Server)。客户端可以随时使用connect函数连接到服务器端,服务器检测到这个连接后,需要使用accept函数接受这个连接,当物务器接受连接后,一个稳定的连接就建立了,双方可以开始互相通过send和recv函数收发数据了,这时通信的两端并没有任何区别。 DOS系统通常用作客户端,服务端通常用Window系统,所以本文主要写DOS操作系统下的TCP客户端编程。 2 连接到服务端 使用connect函数: int connect(int s, struct sockaddr far *name, int namelen); s是套接字的句柄;name是服务端的地址;namelen是name数据结构的长度;函数执行成功返回0,不成功返回SOCKET_ERROR(-1),然后使用errno变量得到具体的出错原因。 struct sockaddr和struct sockaddr_in的内容一样,长度一样,所以使用struct sockaddr_in定义变量name,在调用connect函数的时候强制转换成struct sockaddr。 当套接字工作在非阻塞模式下的时候,不管连接成功与否,connect函数会马上返回并返回SOCKET_ERROR(-1),这时并不意味着连接失败,而是表示函数返回的时候连接尚未成功。这时查询errno变量如果不等于EINPROGRESS(126,表示操作正在进行中),才表示连接失败。 非阻塞模式下的连接: struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(6000); addr.sin_addr.s_addr = inet_addr(“10.0.0.1”); connect(s, (struct sockaddr far *)(&addr), sizeof(addr)); delay(100); if(errno != 118) printf(“fail to connect”); 3 发送数据 使用send函数: int send(int s, char far *pBuf, int len, int flags); s是套接字句柄;pBuf是数据缓冲区;len是要发送的数据长度;flags是发送选项,这个参数一般指定为0。如果发送失败,函数返回SOCKET_ERROR(-1),否则返回成功发送的字节数。 dos_sock为每个socket分配一个发送缓冲区和接受缓冲区,用send函数发送数据时,数据并没有马上在网络上进行传递,而是先放到socket的发送缓冲区中,数据会在合时的时候被发送出去。所以前面的“成功发送”指的是成功放入发送缓冲区而已。 函数在阻塞模式和非阻塞模式下的表现有些不同,下面已send函数发送n字节数据为例说明。 在阻塞模式下,如果发送缓冲区的空闲空间足够大,能容纳n字节的数据,这时函数会将数据全部放入发送缓冲区,然后马上返回;如果缓冲区不够大,函数会一边放入数据一边等待,直到把全部数据放入缓冲区为止。在这两种情况下,返回值都是实际发送的字节数n。这时程序比较简单: int m = send(s, pBuf, n, 0); //m = n 在非阻塞模式下,如果发送缓冲区的空闲空间也能容纳n字节的数据,这时函数也会将数据全部放入发送缓冲区,然后马上返回,返回值就是实际发送的字节数n。当缓冲区不够大,函数也不会等待,而是把一部分数据放入缓冲区后马上返回,这时返回值是实际发送的字节数m。程序可以这样写。 while(n > 0) {   m = send(s, pBuf, n, 0);   if(m == SOCKET_ERROR)   {    printf(“send error”);    break;   }   pBuf += m; //移动指针,指向剩余的数据   n -= m; //剩余的长度   delay(10); } 4 接收数据 使用recv函数: int recv(int s, char far *pBuf, int len, int flags); s是套接字句柄;pBuf是用来返回数据的缓冲区;len是要接受的数据长度;flags是接收选项,一般也指定为0。如果接收失败,返回SOCKET_ERROR(-1),否则返回实际接收的字节数。 在阻塞模式下,函数等待直到有数据到达为止(接收缓冲区不为空),有多少数据到达就返回多少数据。要接收n字节长度的数据,程序如下: while(n > 0) {   m = recv(s, pBuf, n, 0);   if(m == SOCKET_ERROR)   {    printf(“recv error”);    break;   }   pBuf += m; //移动指针,指向剩余的数据   n -= m; //剩余的长度 } 在非阻塞模式下,如果接收缓冲区中已经有数据,recv的表现方式和阻塞模式相同,函数会马上返回,并视缓冲区中的数据数量返回1到n之间的数据。如果接收缓冲区空,函数不会等待,而是马上返回SOCKET_ERROR(-1)。 5 TCP服务端的介绍 TCP服务端一般应用在Windows系统下,所以下面描述的函数都来自MSDN,并且可以在VC++6.0中使用。 服务端在创建了socket后需要绑定到本地的一个端口上,等待客户端连接到这个端口。使用bind函数: struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(6000); //本机上的一个端口 addr.sin_addr.s_addr = INADDR_ANY; //表示本机 bind(s, (struct sockaddr far *)(&addr), sizeof(addr)); 在绑定之后使用listen函数使TCP套接字进入监听状态: listen(s, 5); 能够可以同时发现5个客户端连接而不遗漏。然后套接字就处于等待连接进入的状态了。 当有客户端向监听中的套接字发起连接后,必须对监听中的套接字调用accept函数,连接才最后被确认。accept函数将新建一个套接字并返回它的句柄,这个新套接字还是和客户端连接的,程序以后可以使用它来和客户端之间收发数据了。 while(1) {   SOCKET newSocket = accept(s, 0, 0);   if(newSocket != INVALID_SOCKET)   {    //创建一个新线程    AfxBeginThread(newThread, LPVOID(newSocket));   } } DWORD WINAPI newThread(LPVOID lParam) { SOCKET hSocket = (SOCKET)lParam;   // recv(hSocket, pBuf, len, 0);   // send(hSocket, pBuf, len, 0); } 总结 文章详细介绍了在MS-DOS操作系统下开发网络客户端程序,也介绍了Windows中的服务端。具体代码请参考董琐英编写的过电压下位机程序。有了上面这些知识,然后仿照过电压程序将会很容易开发出自己的网络应用。

2016-01-19

dos开发工具包

DOS下的网络编程 第一章 安装网卡的DOS驱动程序 网卡的DOS驱动程序一般由硬件的生产厂商提供,微软的DOS网络安装包<Microsoft Network Client Version3.zip>在本目录下,里面也包含了很多早期的网卡驱动。在本目录有一个WAFER-C400小主板上网卡的驱动程序<网卡RTL8139驱动(DOS).rar>,非常通用,我们大部分计算机都可以使用它。 <Microsoft Network Client Version3.zip>解压后,把里面的文件都拷贝到DOS系统盘c:\netsetup目录下,把<网卡RTL8139驱动(DOS).rar>解压后拷贝到c:\netsetup\driver目录下。 运行c:\netsetup\setup.exe,当然,是在MS-DOS系统中运行,按照步骤操作。 在下面的列表中选择自己的网卡,因为这些都是早期的型号,我们使用的网卡在这里一般是不会找到的,所以选择“Network adapter not shown on list below ...”。 然后输入网卡驱动所在的目录“c:\netsetup\driver”,程序自己就会找到目录下的驱动,回车进行下一步,随意输入个用户名,例如“yangzhpeng”。到这一步要对自己的网络细致地设置了,不能贸然进行下一步。 其实我们要设置的项也不多。修改用户名,机器名,工作组和域不用管。 修改网络配置,把NWLink协议去掉,添加上TCP/IP协议。并且修改TCP/IP协议的设置。 把Disable Automatic Configuration设置成1(很重要),修改IP地址和掩码。 现在就可以放心地安装了。安装之后重启计算机。 第二章 检查DOS下的网络配置 在上一章安装完成之后,有三个文件对我们很重要。第一个是c:\config.sys文件,有句话“device=C:\NET\ifshlp.sys”,是安装程序给写上的。 第二个文件是c:\autoexec.bat,安装程序在文件的最后添加了这些话: C:\NET\netbind.com C:\NET\umb.com C:\NET\tcptsr.exe C:\NET\tinyrfc.exe C:\NET\nmtsr.exe C:\NET\emsbfr.exe C:\NET\net start 我们把修改成: C:\NET\net initialize C:\NET\netbind.com C:\NET\umb.com C:\NET\tcptsr.exe C:\NET\tinyrfc.exe C:\NET\nmtsr.exe C:\NET\emsbfr.exe C:\NET\sockets.exe 网络驱动安装完毕,下一步要编写程序。 第三章 编程环境 需要4步:(1)假定使用Borland C++ 4.5,假定安装在c:\bc45。 本目录下面有一个Microsoft TCPIP Sockets Development Kit Version 1.0的压缩包<MSTCPSDK.rar>。解压后,把MSTCPSDK\INCLUDE\中的全部文件拷贝到c:\bc45\INCLUDE\中。 (2)如果编写的是C++程序,还要修改\INCLUDE\SOCKDEFS.H文件,把全部内容用extern "C" {……}圈起来。 (3)程序中应该包括下面的头文件: #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> (4)还应该把MSTCPSDK\LIB\DOS_SOCK.LIB文件加入到程序工程中。 如果觉得这样做比较麻烦,那就使用我整理的文件,在本目录中的dos_sock.rar。只有两个文件dos_sock.h和dos_sock.lib,把这两个文件加入工程中,然后#include “dos_sock.h”就可以了。 第四章 SOCKET概述 1 TCP/IP网络模型 TCP/IP是Transmission Control Protocol / Internet Protocol(传输控制协议/网际协议)的缩写。它最初是在20世纪70年代由美国国防部出资为ARPA(美国高级研究项目局)开发的。经过多年的演变,以TCP/IP协议为基础构建的ARPA网逐渐成了今天的Internet。 TCP/IP协议的核心协议包括TCP,UDP和IP协议, 运行于传输层和Internet层上,其中TCP和UDP协议是以IP协议为基础而封装的,这两种协议提供了不同方式的数据通信服务。 如果说IP协议是道路,那么下一层网络访问层的各种协议就相当于不同的铺路材料,而上一层的TCP和UCP协议就相当于路上跑的不同类型的车辆;再上层应用层的各种协议就车上丰富多彩的货物,它们都是以TCP和UDP协议为载体完成的。比如,HTTP协议适使用TCP协议传输网页,POP3协议使用TCP协议传输邮件,而DNS协议使用UDP协议来传输域名和IP地址的翻译信息。 2 IP地址和端口 在使用TCP和UDP协议通信时,必须同时指定IP地址和端口号才能完整地标识一个通信地址,在编程中通常将这两个参数一起定义在一个sockaddr_in结构中来使用。 struct sockaddr_in { short sin_family; //地址格式 unsigned short sin_port; //端口号(使用网络字节顺序) struct in_addr sin_addr; //IP地址(使用网络字节顺序) char sin_zero[8]; //空字节 }; 结构中的sin_family字段用来指定地址格式,在不同的操作系统下,取值可以指定为AF_UNSPEC,AF_UNIX或AF_OSI等不同的值,但是在这里我们只能使用AF_INET。 sin_addr字段是个in_addr结构,这个结构实际上就是4个字节。 3 网络字节顺序 不同的处理器对字节顺序的处理方式不同,有的是高位在前,有的是低位在前。TCP/IP协议统一规定使用高位在前的方式传输数据,很遗憾,这与Intel80x86系列处理器使用的方式不同,所以在80x86平台下的socket编程中,当需要在协议中使用参数时,必须首先将它们转换为Internet顺序。 在填写sockaddr_in结构的sin_port字段和sin_addr字段时,必须首先进行转换。下面就是一些字节转换函数: 16位: unsigned shorthtons(unsigned short); //主机顺序-->网络顺序 unsigned shortntohs(unsigned short); //网络顺序-->主机顺序 32位: unsigned long htonl(unsigned long); //主机顺序-->网络顺序 unsigned long ntohl(unsigned long); //网络顺序-->主机顺序 4 IP地址转换函数 unsigned long inet_addr(char far *); 将“aa.bb.cc.dd”类型的十进制字符串转换成32位的IP地址。如果失败,返回INADDR_NONE。 char far * inet_ntoa(struct in_addr); 将网络字节顺序的32位IP地址转换成字符串。返回一个指针,指向转换后的IP地址字符串,这个字符串位于socket接口的内部缓冲区,所以,在调用inet_ntoa后必须马上把字符串拷贝到自己定义的缓冲区中。 5 套接字 两个主机之间进行网络传输,首先必须建立一个用来通信的对象,这个对象就称为套接字(socket),套接字的定义是“通信的一端”,在通信的另一端必定有另一个套接字与之相对应,以便互相传递数据。仅从编程的角度来看,套接字就是一个整数标识符而已,但使用socket这个称呼好像更贴切。 套接字的种类有很多种,最主要的是流套接字(stream socket)和数据报套接字(datagram socket)。由于流套接字使用传输层的TCP协议进行通信,所以它具有TCP协议所拥有的各种特征,比如:它是面向连接的、稳定的,以及数据包是按顺序发送的;而数据包套接字使用UDP协议进行通信,所以数据包可能丢失,可能重复,以及可能不按顺序到达等。一般将这两种套接字更直观地称为TCP套接字和UDP套接字。 还有一种原始套接字(raw socket),可以直接在Internet层上处理IP数据包首部,所以可以用它是个各种特殊的功能,如伪造发送者地址等。 由于我们在工作实际应用中使用的是TCP协议,所以在本篇也只讨论TCP套接字。对应于dos_sock.lib文件,我们把MS-DOS编程中使用的socket叫做dos_sock。 6 套接字的创建和关闭 创建套接字使用socket函数: int socket(int af, int type, int protocol); 函数使用的第一个参数af用来指定套接字使用的地址格式,和sockaddr_in结构的sin_family字段的定义是一样的,唯一可以使用的值是AF_INET。 第二个参数type用来指定套接字的类型。正如前面介绍的,套接字有流套接字(SOCK_STREAM)、数据报套接字(SOCK_DGRAM)和原始套接字(SOCK_RAW)等。 protocol参数配合type参数的使用,用来指定使用的协议类型,当type参数指定为SOCK_STREAM或者SOCK_DGRAM的时候,系统已经明确使用TCP和UDP协议来工作,所以,这时这个参数并没有什么意义,可以指定为0。但是当type参数指定为SOCK_RAW类型的时候,使用protocol参数可以更详细地指定原始套接字的工作方式。 当套接字被成功创建的时候,函数将返回一个套接字句柄,否则函数将返回INVALID_SOCKET(定义为-1或者~0)。 当不需要使用套接字的时候,使用closesocket函数将它关闭: int closesocket(int s); s就是创建套接字时返回的套接字句柄。 7 套接字的工作模式 socket的使用分为两种模式:阻塞模式和非阻塞模式。阻塞模式也称为同步模式,socket函数会等待操作完成之后才返回。比如,使用recv函数接收数据时,如果函数被调用时没有数据可收,那么函数不会返回,直到收到一些数据为止。再比如使用send函数发送n字节的数据时,在全部n字节发送完之前,函数不会返回,所以这时,程序处在等待状态。 非阻塞模式又称为异步模式,同样是前面的情况,recv和send函数执行后会立即返回,等到数据数据到达或者链路空闲可以继续发送数据时,socket接口会通过某种形式通知应用程序。 Windows中使用的Socket(WinSock2)默认是阻塞模式,不知道dos_sock的默认状态是阻塞还是非阻塞,没有找到相关资料。设置阻塞模式的函数是: int ioctl(int s, int cmd, char far *pArgument); s是套接字句柄,cmd是要执行的操作,pArgument是cmd要设置的参数。函数执行成功返回0,出错返回-1。 设置dos_sock为非阻塞模式: DWORD value = 1; //非0非阻塞 int result = ioctl(s, FIONBIO, (char far *)(&value)); 设置dos_sock为阻塞模式: DWORD value = 0; //是0是阻塞 int result = ioctl(s, FIONBIO, (char far *)(&value)); 8 使用setsockopt设置套接字的属性 函数的原形是: int setsockopt( int s, //套接字的句柄; int level, //属性的分类; int optname, //要设置的属性; char far *optval, //要设置的属性值; int optlen //属性值参数的长度。 ); 比如,允许套接字绑定到已经使用的端口: DWORD value = 1; int result = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char far *)(&value), sizeof(value)); 更详细的内容请参考本目录MSTCPSDK.rar中的winsock.txt。或者查看MSDN,内容大致相同。 9 错误代码errno errno是Borland C++编译器定义的一个整数型全局变量,很多函数把执行时发生的错误写到errno变量中,也会把执行中执行后的状态写到errno变量中,经常遇到的有以下这些: #define EISCONN 118 //socket已经连接上 #define ENOTCONN 119 //socket没有连接上 #define EINPROGRESS 126 //函数正在执行中 第五章 TCP编程模型 和UDP协议有所区别,TCP协议的特点在于: TCP采用超时重传及机制来保证不丢失数据,当一个TCP发送一个数据包后,它启动一个定时器,等待对端确认收到这个包,如果在指定的时间内没有得到确认,将重发这个包。而接收数据包的时候,它将发送一个确认,如果检测到数据包有错,TCP协议丢弃这个数据包,并且不发送确认,那么对端会因为超时而重新发送这个数据包。 一组有序的数据包,到达对端时可能会有后发的数据先到的情况,TCP协议在包首部保存数据包序号,如果有必要,它将对收到的数据重新排序,并以正确的顺序交给应用层。 1 客户端和服务器端的工作模型 TCP的工作方式用上图的客户端/服务器端模型来描述,通信的发起方称为客户端(Client),通信的等待方称为服务器端(Server)。客户端可以随时使用connect函数连接到服务器端,服务器检测到这个连接后,需要使用accept函数接受这个连接,当物务器接受连接后,一个稳定的连接就建立了,双方可以开始互相通过send和recv函数收发数据了,这时通信的两端并没有任何区别。 DOS系统通常用作客户端,服务端通常用Window系统,所以本文主要写DOS操作系统下的TCP客户端编程。 2 连接到服务端 使用connect函数: int connect(int s, struct sockaddr far *name, int namelen); s是套接字的句柄;name是服务端的地址;namelen是name数据结构的长度;函数执行成功返回0,不成功返回SOCKET_ERROR(-1),然后使用errno变量得到具体的出错原因。 struct sockaddr和struct sockaddr_in的内容一样,长度一样,所以使用struct sockaddr_in定义变量name,在调用connect函数的时候强制转换成struct sockaddr。 当套接字工作在非阻塞模式下的时候,不管连接成功与否,connect函数会马上返回并返回SOCKET_ERROR(-1),这时并不意味着连接失败,而是表示函数返回的时候连接尚未成功。这时查询errno变量如果不等于EINPROGRESS(126,表示操作正在进行中),才表示连接失败。 非阻塞模式下的连接: struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(6000); addr.sin_addr.s_addr = inet_addr(“10.0.0.1”); connect(s, (struct sockaddr far *)(&addr), sizeof(addr)); delay(100); if(errno != 118) printf(“fail to connect”); 3 发送数据 使用send函数: int send(int s, char far *pBuf, int len, int flags); s是套接字句柄;pBuf是数据缓冲区;len是要发送的数据长度;flags是发送选项,这个参数一般指定为0。如果发送失败,函数返回SOCKET_ERROR(-1),否则返回成功发送的字节数。 dos_sock为每个socket分配一个发送缓冲区和接受缓冲区,用send函数发送数据时,数据并没有马上在网络上进行传递,而是先放到socket的发送缓冲区中,数据会在合时的时候被发送出去。所以前面的“成功发送”指的是成功放入发送缓冲区而已。 函数在阻塞模式和非阻塞模式下的表现有些不同,下面已send函数发送n字节数据为例说明。 在阻塞模式下,如果发送缓冲区的空闲空间足够大,能容纳n字节的数据,这时函数会将数据全部放入发送缓冲区,然后马上返回;如果缓冲区不够大,函数会一边放入数据一边等待,直到把全部数据放入缓冲区为止。在这两种情况下,返回值都是实际发送的字节数n。这时程序比较简单: int m = send(s, pBuf, n, 0); //m = n 在非阻塞模式下,如果发送缓冲区的空闲空间也能容纳n字节的数据,这时函数也会将数据全部放入发送缓冲区,然后马上返回,返回值就是实际发送的字节数n。当缓冲区不够大,函数也不会等待,而是把一部分数据放入缓冲区后马上返回,这时返回值是实际发送的字节数m。程序可以这样写。 while(n > 0) {   m = send(s, pBuf, n, 0);   if(m == SOCKET_ERROR)   {    printf(“send error”);    break;   }   pBuf += m; //移动指针,指向剩余的数据   n -= m; //剩余的长度   delay(10); } 4 接收数据 使用recv函数: int recv(int s, char far *pBuf, int len, int flags); s是套接字句柄;pBuf是用来返回数据的缓冲区;len是要接受的数据长度;flags是接收选项,一般也指定为0。如果接收失败,返回SOCKET_ERROR(-1),否则返回实际接收的字节数。 在阻塞模式下,函数等待直到有数据到达为止(接收缓冲区不为空),有多少数据到达就返回多少数据。要接收n字节长度的数据,程序如下: while(n > 0) {   m = recv(s, pBuf, n, 0);   if(m == SOCKET_ERROR)   {    printf(“recv error”);    break;   }   pBuf += m; //移动指针,指向剩余的数据   n -= m; //剩余的长度 } 在非阻塞模式下,如果接收缓冲区中已经有数据,recv的表现方式和阻塞模式相同,函数会马上返回,并视缓冲区中的数据数量返回1到n之间的数据。如果接收缓冲区空,函数不会等待,而是马上返回SOCKET_ERROR(-1)。 5 TCP服务端的介绍 TCP服务端一般应用在Windows系统下,所以下面描述的函数都来自MSDN,并且可以在VC++6.0中使用。 服务端在创建了socket后需要绑定到本地的一个端口上,等待客户端连接到这个端口。使用bind函数: struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(6000); //本机上的一个端口 addr.sin_addr.s_addr = INADDR_ANY; //表示本机 bind(s, (struct sockaddr far *)(&addr), sizeof(addr)); 在绑定之后使用listen函数使TCP套接字进入监听状态: listen(s, 5); 能够可以同时发现5个客户端连接而不遗漏。然后套接字就处于等待连接进入的状态了。 当有客户端向监听中的套接字发起连接后,必须对监听中的套接字调用accept函数,连接才最后被确认。accept函数将新建一个套接字并返回它的句柄,这个新套接字还是和客户端连接的,程序以后可以使用它来和客户端之间收发数据了。 while(1) {   SOCKET newSocket = accept(s, 0, 0);   if(newSocket != INVALID_SOCKET)   {    //创建一个新线程    AfxBeginThread(newThread, LPVOID(newSocket));   } } DWORD WINAPI newThread(LPVOID lParam) { SOCKET hSocket = (SOCKET)lParam;   // recv(hSocket, pBuf, len, 0);   // send(hSocket, pBuf, len, 0); } 总结 文章详细介绍了在MS-DOS操作系统下开发网络客户端程序,也介绍了Windows中的服务端。具体代码请参考董琐英编写的过电压下位机程序。有了上面这些知识,然后仿照过电压程序将会很容易开发出自己的网络应用。

2016-01-19

dos下的 WEB服务器

dos下的 WEB服务器 需加载packet driver

2012-04-21

html+txt静态网

html+txt静态网,比较好的 适合建企业站.

2011-11-23

JAVA人脸识别系统

人脸识别,JAVA的很不错,在国外有人在应用。

2008-12-11

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除