使用阻塞套接字模式,Winsock网络编程_第1页
使用阻塞套接字模式,Winsock网络编程_第2页
使用阻塞套接字模式,Winsock网络编程_第3页
使用阻塞套接字模式,Winsock网络编程_第4页
使用阻塞套接字模式,Winsock网络编程_第5页
已阅读5页,还剩16页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 Winsock网络编程使用阻塞套接字模式【ITjob课程资料】Windows网络编程的三种模式阻塞套接字模式、异步套接字模式(基于消息的非阻塞套接字模式)、MFC异步套接字模式使用阻塞套接字模式进行Windows网络编程利用TCP进行通信程序开发TCP进行通信开发有点类似于步话机(对讲机)对话的方式,那么步骤是首先要找到对方对讲机的位置,然后根据此对讲机的位置进行呼叫,对方接受到呼叫信号之后按下应答按钮,发送回应信号,呼叫方收到回应信号后,两人连线成功,进行通话。我们也可以称之为通信领域的三次握手。这种需要通过握手确认建立通道进行通讯的方式就是TCP通信。下面我们来编写服务器端的代码:初始化

2、套接字库创建服务端套接字绑定套接字开始监听客户端接受客户端套接字向客户端发送数据接受客户端数据关闭客户端套接字关闭服务端套接字释放套接字库资源一、引入头文件#include <Winsock2.h>#include <stdio.h>并引入动态链接库#pragma comment (lib, "ws2_32.lib")二、初始化套接字库.WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &wsaData

3、);if (err != 0)return;SOCKET编程的概念如何通讯?最直观的方式就是记录两边的通讯地址(就像两个电话号码一样),但是在计算机中每个机器不仅仅有IP地址,还有很多端口,分别负责不同的任务,比如:80端口就是负责IIS默认的HTTP服务的,而21端口则是负责FTP通讯,在操作系统中将这些内容封装到一个结构体中,用于描述IP地址和端口。这个就是Socket(也称作“套接字”),当然还要包括传输协议(TCP或UDP)。从技术的底层来看,操作系统通过套接字描述符来针对网络通讯的点对点的关系进行描述,每个进程的进程空间里都有一个套接字描述符表,比如:套接字描述符表socket描述地

4、址1952内核位置的内存地址1内核位置的内存地址2内核缓冲地址具体描述内核位置的内存地址16000TCP内核位置的内存地址26000TCP该表中存放着套接字描述符和套接字结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字结构的地址,因此根据套接字描述符就可以找到其对应的套接字结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字结构都是在操作系统的内核缓冲里。c+中通过socket函数在内核中建立一个socket空结构,并返回整数类型的套接字描述符,对应这个socket结构。所以我们知道socket描述符后就能

5、明确知道对方的地址和端口,以备发送信息。WSAStartup函数WSAStartup函数用于初始化网络资源,并建立一个套接字库。参数说明:wVersionRequested:用于指定准备加载的Winsock库的版本。高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。可用MAKEWORD(x,y)(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值。lpWSAData:是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。WSADATA结构定义如下:struct WSAData WORDwVersion

6、;WORDwHighVersion;charszDescriptionWSADESCRIPTION_LEN+1;charszSystemStatusWSASYS_STATUS_LEN+1;unsigned short iMaxSockets;unsigned short iMaxUdpDg;char FAR *lpVendorInfo;wVersion:打算使用的Winsock版本。wHighVersion :现有的Winsock库的最高版本。szDescription:Winsock库的具体描述。szSystemStatus:sock状态。iMaxSockets:同时最多可打开多少套接字iM

7、axUdpDg:数据报的最大长度lpVendorInfo:为Winsock实施方案有关的指定厂商信息预留的。一般用不到。三、创建服务端套接字SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET = sockSrv)WSACleanup();return;socket函数c+中通过socket函数在内核中建立一个socket空结构,并返回整数类型的套接字描述符,对应这个socket结构。所以我们知道socket描述符后就能明确知道对方的地址和端口,以备发送信息。函数原型:SOCKET socket(intaf,in

8、ttype,intprotocol); 此函数用于建立一个新的套接字结构,并分配一个套接字描述符。参数描述:af:一种地址格式描述。对于TCP/IP协议的套接字,它只能是AF_INET。type:要建立的套接字的类型描述,类型有两种:SOCK_STREAM(TCP传输),SOCK_DGRAM(UDP传输)protocol:套接字使用的特定协议,如果调用者不希望指定协议,则置为0,使用默认的连接模式。返回值: 如果没有错误发生,socket()返回一个与建立的套接字相关的描述符。否则它返回-1四、绑定套接字到具体的地址和端口SOCKADDR_IN addrTemp;addrTemp.sin_ad

9、dr.S_un.S_addr = htonl(INADDR_ANY);addrTemp.sin_port = htons(6000);addrTemp.sin_family = AF_INET;err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (err = -1) WSACleanup();return;bind函数函数原型:int bind(SOCKETs,const struct sockaddr *name,intnamelen); 将套接字句柄与地址、端口进行绑定。参数描述:s:数据报或流套接字的描

10、述符。name: 赋给套接字的地址,具体内容用一个结构体sockaddr表示:struct sockaddr u_short sa_family; char sa_data14;字段描述:sa_family:指定该地址家族,在这里因为是tcp,udp,所以设定为AF_INET。sa_data :表示所有的不同socket地址结构的最大尺寸。注意:实际编程中,我们一般用sockaddr_in结构替换sockaddr,因为sockaddr_in比sockaddr清晰(注意,系统支持自动转换)。struct sockaddr_in short sin_family; u_short sin_port

11、; struct in_addr sin_addr; char sin_zero8;而struct in_addr的定义如下:struct in_addr union struct u_char s_b1,s_b2,s_b3,s_b4; S_un_b; struct u_short s_w1,s_w2; S_un_w; u_long S_addr; S_un;SOCKADDR_IN结构的字段描述:sin_family:置为AF_INETsin_port:连接的端口号。这里用了addrTemp.sin_port = htons(6000);下面解释htnos函数:htons函数此函数将一个u_s

12、hort类型数(16位无符号整数)从主机字节顺序转换成TCP/IP网络字节顺序。hostshort参数:主机字节顺序表示的16位无符号整数。返回值: htons()返回一个TCP/IP网络字节顺序表示的16位值。它的逆向函数为:ntohs()htons 是把你机器上的整数转换成“网络字节序”, 网络字节序是 大字节存储次序,也就是整数的高位字节存放在内存的低地址处。 而我们常用的 x86 CPU (intel, AMD) 电脑是 小字节存储次序,也就是整数的低位字节放在内存的低字节处。例如: port是 0x1234, 在网络字节序里 这个port放到内存中就应该显示成 addr,addr+1

13、:0x12,0x34;而在x86电脑上,0x1234放到内存中实际是:addr,addr+1: 0x34,0x12。htons 的用处就是把实际内存中的整数存放方式调整成“网络字节序”的方式。相关的函数有:htonl函数此函数将一个u_long类型数(32位无符号整数)从主机字节顺序转换成TCP/IP网络字节顺序。hostlong :主机字节顺序表示的32位无符号整数。返回值: htonl()返回一个TCP/IP网络字节顺序表示的32位值。它的逆向函数为:ntohl()第三个重要的参数为sin_addr,即IP地址。这是一个32位的无符号整数,addrTemp.sin_addr. s_addr

14、 =inet_addr(""); 或者addrTemp.sin_addr.s_addr =inet_addr("60");假设地址为60。下面解释inet_addr函数简述:将一个点间隔地址转换成一个sin_addr.S_un.S_addr。如果我们只想让套接字使用多个IP中的一个地址,就必须指定实际地址,要做到这一点,可以用inet_addr()函数,这个函数需要一个字符串作为其参数,该字符串指定了以点分十进制格式表示的IP地址(如6)。而且inet_addr()函数会

15、返回一个适合分配给S_addr的u_long类型的数值。它的逆向函数为:inet_ntoa()函数会完成相反的转换,它接受一个in_addr结构体类型的参数并返回一个以点分十进制格式表示的IP地址字符串。inet_ntoa函数语法: char * inet_ntoa ( IN struct in_addr in ); 此函数将一个网际地址转换成点分十进制表示法表示的字符串。它接受由参数in指定的网际地址结构,返回以点分表示法如“a.b.c.d”表示的地址的ASCII字符串。参数描述:in:表示主机网际地址的结构。返回值: 如果没有错误发生,inet_ntoa()返回一个字符指针,该指针指向含有

16、以点分十进制表示法表示的正文地址的静态缓冲区。但是现在语句表示为:addrTemp.sin_addr. s_addr = INADDR_ANY;关于INADDR_ANY宏INADDR_ANY的定义如下:#defineINADDR_ANY(unsigned long int) 0x00000000)如果服务器有多个网卡(每个网卡上有不同的IP地址),当用任何一个IP地址都可以的时候,将IP地址指定为INADDR_ANY。sin_zero:是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节,用bzero函数置为8个零, bzero(&(my_addr.s

17、in_zero),8);回到bind函数,最后一个参数namelen,表示地址缓冲区长度。返回值: 如果没有错误发生,bind()返回0。五、 费了很长的时间终于绑定了socket,那么下面开始进行监听listen(sockSrv, SOMAXCONN);listen函数listen ( IN SOCKET s, IN int backlog ); 此函数只用于流套接字,它执行两个操作:1、若没有为s调用过bind(),则listen()完成套接字s所必须的连接。2、建立长度为backlog的连接请求队列来存放即将到来的连接请求。 参数描述:s:标识一个已绑扎、没有连接的套接字的描述符。bac

18、klog:未处理连接队列的最大长度。返回值: 如果没有错误发生,listen()返回0。六、下面开始监听客户端,代码如下SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); SOCKET sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &len);accept函数语法: SOCKET WSAAPI accept ( IN SOCKET s, OUT struct sockaddr FAR* addr, OUT int FAR* addrlen ); 这个函数是一个阻塞函数

19、,用于接受客户端的socket信息。accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。当然accept函数发生在三次握手之后:第一次握手:客户端发送syn包(syn=j)到服务器。第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个ASK包(ask=k)。第三次握手:客户端收到服务器的SYNACK包,向服务器发送确认包ACK(ack=k+1)。三次握手完成后,客户端和服务器就建立了tcp连接。这时可以调用accept函数获得此连接 sockfd是被监听的socket描述符,add

20、r通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数返回-1并置相应的errno值。首先,当accept函数监视的 socket收到连接请求时,socket执行体将建立一个新的socket,执行体将这个新socket和请求连接进程的地址联系起来,收到服务请求的 初始socket仍可以继续在以前的 socket上监听,同时可以在新的socket描述符上进行数据传输操作。七、接收到了客户端的socke

21、t信息之后,则可以通过recv函数来获得数据。char recvBuf100;char sendBuf100;while (1)recv(sockClient, recvBuf, 100, 0);printf("%s:%sn",inet_ntoa (addrClient.sin_addr),recvBuf);recv函数语法: int WSAAPI recv ( IN SOCKET s, OUT char FAR* buf, IN int len, IN int flags ); 此函数用于在参数s指定的已连接的数据报或流套接字上读取输入数据。参数描述:s:已连接的套接字描

22、述符。buf:指向接收输入数据缓冲区的指针。len:buf参数所指缓冲区的长度。flags:指定调用的方式,它可用来与套接字相关的选项一起影响函数的功能。就是说,recv()函数的意义由套接字选项和flags参数共同决定。flags可取下列值: MSG_OOB 读取套接字上的带外数据。MSG_PEEK 查看输入数据,数据被拷入缓冲区中, 但不从输入队列中清除。返回值: 如果没有错误发生,recv()返回收到的字节数。如果连接被关闭,返回0。八、接受到数据之后,向用户发送数据:gets(sendBuf);send(sockClient, sendBuf, 100, 0);send函数语法: in

23、t WSAAPI send ( IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags ); 此函数用于在参数s指定的已连接的数据报或流套接字上发送输出数据。参数描述:s:已连接的套接字描述符。buf:指向存有发送数据的缓冲区的指针。len:缓冲区buf中数据长度。flags:指定调用的方式,它可用来与套接字相关的选项一起影响函数的功能。就是说,send()函数的意义由套接字选项和flags参数共同决定。flags可取下述值: MSG_DONTROUTE 指出数据不提交给路由选择。MSG_OOB 发送带外数据。返回值: 如果

24、没有错误发生,send()返回总共发送的字节数(注意,这可能比len指示的长度小)。九、关闭套接字,关闭网络资源。/8. 关闭客户端套节字closesocket(sockClient);/9.关闭服务端套接字closesocket(sockSrv);/10.释放套接字库资源WSACleanup();closesocket函数语法: int WSAAPI closesocket ( IN SOCKET s );函数关闭套接字s,并释放分配给该套接字的资源,以后对s 的引用都将产生错误WSAENOTSOCK。如果s涉及一个打开的TCP连接,该连接被释放。参数描述:s:待关闭的套接字描述符。返回值:

25、如果没有错误发生,closesocket()返回0。十、下面我们开始写客户端:10.1 包含头文件#include <Winsock2.h>#include <stdio.h>#pragma comment (lib, "ws2_32.lib")10.2创建套接字资源库WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;10.3 创建客户端套接

26、字SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);10.4指明服务器SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr = inet_addr("");addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(6000);/4.发出连接请求connect(sockClient, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR_IN);connect函数当用socket建立

27、了套接口后,可以调用connect为这个套接字指明远程端的地址;如果是字节流套接口,connect就使用三次握手建立一个连接;如果是数据报套接口,connect仅指明远程端地址,而不向它发送任何数据。返回:0表示成功-1表示失败 参数解释:第一个参数是socket函数返回的套接口描述字;第二和第三个参数分别是一个指向套接口地址结构的指针和该结构的大小。10.5向服务器端发送数据char sendBuf100;char recvBuf100;while(1) gets(sendBuf);send(sockClient, sendBuf, 100, 0);10.6 接收接受服务端数据recv(so

28、ckClient,recvBuf,100,0);printf("%s:%sn",inet_ntoa(addrSrv.sin_addr),recvBuf);10.7关闭客户端套接字和套节字库资源closesocket(sockClient); WSACleanup();下面解释上面的代码中提及的概念和函数:服务器端和客户端的创建过程如下表:TCP服务端伪代码TCP客户端伪代码1创建服务端套接字socket()创建连接套接字socket()2绑定套接字bind()指明服务器,并发出连接请求connect()3开始监听客户端listen()4接受客户端套接字accept() 阻塞

29、等待5接受客户端数据recv() 阻塞等待向服务端发送数据send()6向客户端发送数据send()接受服务端数据recv() 阻塞等待7关闭客户端套接字closesocket()关闭连接套接字closesocket()8关闭服务端套接字closesocket()9释放套接字库资源WSACleanup()释放套接字库资源WSACleanup()利用UDP进行通信程序开发的基本步骤UDP通讯是不需要握手确认的,就好像两个人直接会话,比较形象的比喻是发送电报,而这种方式也不需要任何的确认过程。服务器端代码如下:#include <Winsock2.h>#include <stdi

30、o.h>void main()/1. 初始化套接字库WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;/2. 创建服务端套接字SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);/3. 绑定套接字SOCKADDR_IN addrTemp;addrTemp.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

31、addrTemp.sin_family = AF_INET;addrTemp.sin_port = htons(6000);err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (0 != err)WSACleanup();return;char recvBuf100;char sendBuf100;SOCKADDR_IN addrClient;int len = sizeof(SOCKADDR);while (1)/4.接受客户端数据recvfrom(sockSrv, recvBuf, 100, 0, (SO

32、CKADDR*)&addrClient, &len);/阻塞printf("%s:%sn",inet_ntoa(addrClient.sin_addr),recvBuf);/5.发数据到客户端gets(sendBuf);sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrClient, len);/6. 关闭服务端套接字closesocket(sockSrv);/7.释放套接字库资源WSACleanup();客户端代码如下:#include <Winsock2.h&g

33、t;#include <stdio.h>void main()/1. 初始化套接字库WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(1, 1);err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;/2. 创建客户端套接字SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);/3.指明服务器SOCKADDR_IN addrSrv;addrSrv.

34、sin_addr.S_un.S_addr = inet_addr("");addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(6000);char recvBuf100;char sendBuf100;int len = sizeof(SOCKADDR);while (1)/4.发数据到服务器端gets(sendBuf);sendto(sockClient, sendBuf, 100, 0, (SOCKADDR*)&addrSrv, len);/5.接受服务端数据recvfrom(sockCl

35、ient, recvBuf, 100, 0, (SOCKADDR*)&addrSrv, &len);printf("%s:%sn", inet_ntoa(addrSrv.sin_addr),recvBuf);/6. 关闭客户端套接字closesocket(sockClient);/7.释放套接字库资源WSACleanup();UDP服务端伪代码UDP客户端伪代码1初始化套接字库WSAStartup()初始化套接字库WSAStartup()2创建服务端套接字socket(SOCK_DGRAM)创建客户端套接字socket(SOCK_DGRAM)3绑定套接字bi

36、nd()4接受客户端数据recvfrom() 阻塞发数据到服务端sendto()5发数据到客户端sendto()接受服务端数据recvfrom() 阻塞6关闭服务端套接字closesocket()关闭客户端套接字closesocket()7释放套接字库资源WSACleanup()释放套接字库资源WSACleanup()TCP与UDP的对比TCP是可靠传输,UDP是不可靠传输TCP是面向连接的,UDP是无连接的TCP被广泛应用在文件传输、远程连接等需要数据被可靠传输的领域;UDP比TCP相对简单且容易管理,它被应用在一些局域网系统的应用程序中。UDP非常快,具有低开销要求,TCP比较慢,具有更高

37、的开销要求tcp和udp的应用如下:应 用应用层协议用来支撑的传输协议电子邮件SMTP ( RFC82 )TCP远程终端访问TelenetTCPWEBHTTPTCP文件传送FTPTCP远程文件服务器NFSTCP 或 UDP流多媒体专属UDP 或 TCP因特网电话专属一般为 UDP多线程应用注意,刚刚的程序有一个特点就是就是每次需要接受到信息以后才能再次发送信息,不能连续多次发送信息,这是因为程序结构导致:while (1)/4.接受数据进入等待recvfrom();/5.发送数据sendto();这段程序在主线程中完成。是一种同步方式,需要先接收再发送。下面我们采用多线程的方式就可以解决这个问

38、题,单独开辟一个线程用于接受信息。服务器端程序如下:#include <Winsock2.h>#include <stdio.h>SOCKADDR_IN addrClient; /客户端地址SOCKET sockSrv;BOOL bEnableSend=FALSE;DWORD WINAPI RecvProc(LPVOID lpParameter)/1. 初始化套接字库WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &ws

39、aData);if (err != 0) return 0;/2. 创建服务端套接字sockSrv = socket(AF_INET, SOCK_DGRAM, 0);/3. 绑定套接字SOCKADDR_IN addrTemp;addrTemp.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrTemp.sin_family = AF_INET;addrTemp.sin_port = htons(6000);err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (0 !=

40、err)WSACleanup();return 0;char recvBuf100;intlen = sizeof(SOCKADDR_IN);while (1)/4.接受客户端数据recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);printf("%s:%sn",inet_ntoa(addrClient.sin_addr),recvBuf);bEnableSend=TRUE;/6. 关闭服务端套接字closesocket(sockSrv);/7.释放套接字库资源WSACleanup

41、();return 0;void main()HANDLE m_hThread;m_hThread = :CreateThread(NULL, 0, RecvProc, NULL, 0, NULL);char sendBuf100;while (1)/5.发数据到客户端if (bEnableSend)gets(sendBuf);int a=sendto(sockSrv, sendBuf, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR);客户端程序如下:#include <Winsock2.h>#include <std

42、io.h>SOCKET sockClient;SOCKADDR_IN addrSrv;/服务器地址BOOLEAN bEnableRev=FALSE;/是否可以接收DWORD WINAPI RecvProc(LPVOID lpParameter)char recvBuf100;intlen = sizeof(SOCKADDR_IN);while (1)if (bEnableRev)/5.接受服务器数据recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR*)&addrSrv, &len);printf("%s:%sn&quo

43、t;,inet_ntoa(addrSrv.sin_addr),recvBuf);void main()HANDLE m_hThread;m_hThread = :CreateThread(NULL, 0, RecvProc, NULL, 0, NULL);/1. 初始化套接字库WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(1, 1);err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;/2. 创建客户

44、端套接字sockClient = socket(AF_INET, SOCK_DGRAM, 0);/3.指明服务器地址addrSrv.sin_addr.S_un.S_addr = inet_addr("");addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(6000);char sendBuf100;int len = sizeof(SOCKADDR);while (1)/4.发数据到服务器gets(sendBuf);sendto(sockClient, sendBuf, 100, 0, (SOCKA

45、DDR*)&addrSrv, len);bEnableRev=TRUE;/6. 关闭客户端套接字closesocket(sockClient);/7.释放套接字库资源WSACleanup();用对话框实现UDP多线程对话在这个程序中,我们建立一个基于UDP多线程的对话框程序,其中一个线程用于接收信息,另外一个用于发送信息,界面如下:设定两个全局变量SOCKET sockSrv;/服务端WSADATA wsaData;/套接字库(1) 首先在Dlg的OnInitDialog事件中完成初始化工作:/1. 初始化套接字库WORD wVersionRequested = MAKEWORD(1,

46、 1);int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return 0;/2. 创建服务端套接字sockSrv = socket(AF_INET, SOCK_DGRAM, 0);/3. 绑定套接字/注意:一个端口只能绑定一个,所以控制程序只能启动一次SOCKADDR_IN addrTemp;addrTemp.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrTemp.sin_family = AF_INET;addrTemp.sin_port = htons(600

47、0);err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (0 != err)WSACleanup();return 0;HANDLE m_hThread;m_hThread = :CreateThread(NULL, 0, RecvProc, this, 0, NULL);SetDlgItemText(IDC_EDIT_IP, "");SetDlgItemText(IDC_EDIT_SEND, "hello");(2)在Dlg的OnDestroy事件

48、中释放资源:/6. 关闭服务端套接字closesocket(sockSrv);/7.释放套接字库资源WSACleanup();(3)做一个线程启动接收数据DWORD WINAPI RecvProc(LPVOID lpParameter)CUDPChatDlg *pDlg = (CUDPChatDlg *)lpParameter;char recvBuf100;intlen = sizeof(SOCKADDR_IN);SOCKADDR_IN addrClient; /客户端地址while (1)/4.接受客户端数据recvfrom(sockSrv, recvBuf, 100, 0, (SOCKA

49、DDR*)&addrClient, &len);/显示数据CString str;str.Format("%s: %s", inet_ntoa(addrClient.sin_addr), recvBuf);pDlg->m_list.AddString(str);:SendMessage(pDlg->m_list.m_hWnd, WM_VSCROLL, SB_BOTTOM, NULL);return 0;(4)在Button点击按钮利用主线程传送数据:CString sIP, sSend;GetDlgItemText(IDC_EDIT_IP, sI

50、P);GetDlgItemText(IDC_EDIT_SEND, sSend);SOCKADDR_IN addrClient; /客户端地址/3.指明服务器地址addrClient.sin_addr.S_un.S_addr = inet_addr(sIP);addrClient.sin_family = AF_INET;addrClient.sin_port = htons(6000);sendto(sockSrv, sSend, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR);m_list.AddString(sSend);SetDl

51、gItemText(IDC_EDIT_SEND, "");:SendMessage(m_list.m_hWnd, WM_VSCROLL, SB_BOTTOM, NULL);使用异步套接字模式进行Windows网络编程上面我们使用了阻塞套接字模式,并发现利用阻塞套接字的时候单线程已经不能解决问提了,微软提供了另外一种方法,即异步套接字模式。所谓异步套接字,是指基于消息的、非阻塞模式的套接字,在这种模式下我们是可以采用单线程解决问题的。该异步策略主要是通过Windows Sockets的异步选择函数WSAAsyncSelect()来实现的,该函数提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论