📄 dosnetworkprogrammimg.txt
字号:
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中的服务端。具体代码请参考董琐英编写的过电压下位机程序。有了上面这些知识,然后仿照过电压程序将会很容易开发出自己的网络应用。
----------------------------------
杨志朋 2008年4月7日
yzp3646@163.com
yzp3646@sina.com
河北旭辉电气股份有限公司
第13页 / 共15页
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -