WINSOCK_UDP一对多+多线程_第1页
WINSOCK_UDP一对多+多线程_第2页
WINSOCK_UDP一对多+多线程_第3页
WINSOCK_UDP一对多+多线程_第4页
WINSOCK_UDP一对多+多线程_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

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

文档简介

1、WINSOCK_UDP一对多实现多个客户端通信Client / Server 架构作者 曹亚帅Contents关于 C / S 架构对等网 :采用分散管理的方式,网络中的每台计算机既作为客户机,又可作为服务器来工作,每个用户都管理自己机器上的资源。Client/Server :在客户机/服务器网络中,服务器是网络的核心,而客户机是网络的基础,客户机依靠服务器获得所需要的网络资源,而服务器为客户机提供网络必须的资源。简单点说,数据库资料全部存储在服务器上,服务器要是完蛋了,大伙也就没什么好访问的了,所有客户端应用程序全部报错,网络瘫痪.比如银行,如果中心服务器和备用服务器全部当掉,那么银行只能停

2、止办理业务. 第一部分图文结合UDP C/S编程的步骤服务端没有设置监听和等待连接的过程。客户端没有连接服务端的过程。基于UDP的通信时不可靠地,面向无连接的,发送的数据无法确切知道对方收到没有,就算对方根本不存在,也可以发送数据出去。这样的通信通常用在对可靠性、安全性要求不高的地方,比如语音通信(没听清楚可以让对方再说一遍)。关于 C / S 架构第二部分实现思路实现思路一个服务器对应多个客户端,或者对应多个请求,我们采用的是线程+select的方式来解决此问题。该程序的默认设置为:1.各参与者IP地址为127.0.0.1(环回地址,用于单机测试)。2.编号为i的参与者端口号为不同值。由于是

3、采取本机测试,所以每个进程的IP地址都是127.0.0.1,所以需要使用不同的端口,从而区分出不同的进程,这样才能保证网络数据发送到正确的参与者处理。关于 多线程WINSOCK_UDP一对多多路复用select() 功能:用来检测一个或多个套接字状态。 第二部分实现思路实现思路一个服务器对应多个客户端,或者对应多个请求:服务器在一个线程中向指定客户端地址发消息,在主线程中接受客户端发过来的消息。每当UDP客户端发数据过来时,把这个客户端加入文件描述符集中,监听它。过来一个客户端就把它加入文件描符述集中,然后监听。关于 多线程一个连接由(server_ip, server_port)和(clie

4、nt_ip, client_port)唯一确定。第二部分实现思路实现思路一个线程中要处理多个客户(多个套接字),我们在套接字A、B上顺序调用recv企图依次接收数据。若客户A始终不发数据,则recv(A)函数将可能永远无法返回,在这期间我们错失了客户B发送来的数据。时间选择通过select函数来实现(所谓非阻塞方式,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)关于 多线程处理UDP中recv的阻塞问题由于某些原因, 会使得函

5、数永远处于阻塞模式。(比如:客户用UDP给服务器传送数据的数据丢失,使得服务器端的recvfrom函数始终处于阻塞模式)这就需要调用某些函数使这些函数不再阻塞。第二部分思路流程sendto主线程recvselect服务器端recv主线程send客户端第三部分服务器代码/* 服务器端 cpp */ 时间:2015年10月4日晒 程序第四部分代码排版/* 服务器端 cpp */#include #include /winsocket2 的头文件 #pragma comment (lib, Ws2_32.lib) /winsocket2需要的库文件static char ip_cli50; / IP

6、 地址字符串 static int thread; /线程 int CLIport; /端口值晒 程序第四部分代码排版/程序有一个主线程,入口函数main/在主线程上可以创建新线程,入口函数定义成如下形式:DWORD WINAPI ThreadProc( LPVOID lpParam ) /线程函数声明 /格式不正确将无法调用成功,函数名称无限制,只要符合命名规则就行 /线程函数必须是全局函数,不能在类中声明和定义 int nStatus; unsigned long cmd; /阻塞或者非阻塞的模式 SOCKET socket_send; /创建一个套接字 socket_send=socke

7、t(AF_INET,SOCK_DGRAM,0); /创建socket服务,使用UDP协议 nStatus=ioctlsocket(socket_send,FIONBIO,&cmd); / 设置为非阻塞模式FIONBIO:允许或禁止套接口s的非阻塞模式。cmd为非零是:非阻塞模式;为零是:阻塞模式。晒 程序第四部分代码排版if(socket_send=INVALID_SOCKET) /设置失败 printf(socket Error!); char sbuf200; /发送数据包 int len=sizeof(sockaddr); / sockaddr 结构的长度;初始化这个参数,这个参

8、数必须被初始化晒 程序第四部分代码排版while(1) sockaddr_in addr_send; / 定义一个Sockaddr_in型的结构变量 /输入填充sockaddr_in结构 printf(请输入要回复的【客户端】 IP:); scanf(%s,&ip_cli); addr_send.sin_addr.S_un.S_addr=inet_addr(ip_cli); addr_send.sin_family=AF_INET; printf(请输入【客户端】端口号:); scanf(%d,&CLIport); addr_send.sin_port=ntohs(CLIpor

9、t); 晒 程序第四部分代码排版while(1) printf(-n); printf(*本地推送*:nn); scanf(%s,sbuf); if(strcmp(sbuf,“end”)=0) break; /服务端输入end终止向某一客户的发送 sendto(socket_send,sbuf,strlen(sbuf)+1,0,(sockaddr*)&addr_send,sizeof(addr_send); /向某一客户端提交字符串数据包 晒 程序第四部分代码排版int main() /主线程 WSADATA wsaData; / 用来返回Winsock 实现的细节信息 DWORD d

10、wThreadId; /线程的 ID int Ret = WSAStartup(MAKEWORD(2,2),&wsaData); / 调用初始化函数 printf(=Server=n); if(Ret != 0) printf(n无法初始化winsock.n); WSACleanup(); /表示申请WSAStartup失败 else printf(“n初始化winsock成功!nn”); /表示成功初始化 晒 程序第四部分代码排版CreateThread(NULL, 0, ThreadProc, 0, 0, &dwThreadId); /创建发送线程 SOCKET socke

11、t_recv; /创建套接字 unsigned long cmd; int nStatus; socket_recv=socket(AF_INET,SOCK_DGRAM,0); /创建socket服务,使用UDP协议 nStatus=ioctlsocket(socket_recv,FIONBIO,&cmd); / 设置为非阻塞模式 if(nStatus=SOCKET_ERROR) printf(n设置非阻塞模式失败!n); 晒 程序第四部分代码排版sockaddr_in addr_recv; /填充sockaddr_in结构 addr_recv.sin_addr.S_un.S_addr

12、=(INADDR_ANY); addr_recv.sin_family=AF_INET; addr_recv.sin_port=ntohs(8888); if(socket_recv=INVALID_SOCKET) printf(socket Error!); int rs= bind(socket_recv,(sockaddr*)&addr_recv,sizeof(sockaddr); /将套接字绑定到指定的网络地址,对&addr_recv进行了强制类型转换 if (rs!=0) printf(bind failed!); return 1; 晒 程序第四部分代码排版 cha

13、r temp300; /临时存储区 char rebuf200; /接收数据包 int len=sizeof(sockaddr); / sockaddr 结构的长度;初始化这个参数,这个参数必须被初始化 timeval tv; 晒 程序struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。struct timeval long tv_sec; /秒 long tv_usec; /微秒 第四部分代码排版while (1) fd_set fdread; /一组文件描述字(fd)的集合 FD_ZERO(&fdread);

14、/清空集合 FD_SET(socket_recv,&fdread); /将client加入到读取集合中 tv.tv_sec=0; tv.tv_usec=100; /设置select()的超时时间 select(0,&fdread,NULL,NULL,&tv); /如果该套接字没有数据需要接收,select函数会把该套接字从可读性检查队列中删除掉 if(FD_ISSET(socket_recv,&fdread) /检查socket_recv是否在这个集合里面即是否网络上有数据,如果有,接受网络数据recvfrom(socket_recv,rebuf,sizeof(

15、rebuf),0,(sockaddr*)&addr_recv,&len);晒 程序使用Select就可以完成非阻塞方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况读写或是异常。select()函数主要是建立在fd_set类型的基础上的。fd_set()是一组文件描述字(fd)的集合,它用一位来表示一个fd,对于fd_set类型通过下面四个宏来操作: fd_set是一个SOCKET队列,以下宏可以对该队列进行操作:FD_CLR( s, *set) 从队列set删除句柄s;FD_ISSET( s, *set) 检查句柄s是否存在与队列set中;FD_SET( s, *s

16、et )把句柄s添加到队列set中;FD_ZERO( *set ) 把set队列初始化成空队列.第四部分代码排版if(!strcmp(quit, rebuf) printf(%d已退出程序!n,(int)(ntohs(addr_recv.sin_port); /接收到quit打印相应信息else printf(n*n); sprintf(temp,%d say :n%s,(int)(ntohs(addr_recv.sin_port),rebuf); printf(“%sn”,temp); /打印客户端发来消息 else Sleep(100); 晒 程序第四部分代码排版 closesocket(

17、socket_recv); WSACleanup(); /关闭socket,释放网络 printf(exitn); return 0; 晒 程序第三部分服务器代码/* 客户端 cpp */ 时间:2015年10月4日晒 程序第四部分代码排版/* 客户端 cpp */#include #include #pragma comment (lib, Ws2_32.lib) static char ServerIp128; / IP 地址 static int thread; int CLIport; /端口晒 程序第四部分代码排版DWORD WINAPI ThreadProc( LPVOID lpP

18、aram ) /线程函数声明 SOCKET socket_recv; /创建套接字 unsigned long cmd; int nStatus; socket_recv=socket(AF_INET,SOCK_DGRAM,0); /UDP nStatus=ioctlsocket(socket_recv,FIONBIO,&cmd); / 设置为非阻塞模式 if(nStatus=SOCKET_ERROR) /设置失败 printf(设置非阻塞模式失败!n); 晒 程序第四部分代码排版sockaddr_in addrcl;addrcl.sin_addr.S_un.S_addr=inet_a

19、ddr(ServerIp); addrcl.sin_family=AF_INET; addrcl.sin_port=ntohs(CLIport); /设置地址和端口 if(socket_recv=INVALID_SOCKET) printf(socket Error!); /地址与套接字绑定 int rs= bind(socket_recv,(sockaddr*)&addrcl,sizeof(addrcl); if (rs!=0) printf(bind failed!); return 1; 晒 程序第四部分代码排版 char temp300; /临时数据 char rebuf200

20、; /接收数据 int len=sizeof(sockaddr); /sockaddr 结构的长度 timeval tv;晒 程序第四部分代码排版while (1) fd_set fdread; FD_ZERO(&fdread); FD_SET(socket_recv,&fdread); tv.tv_sec=0; tv.tv_usec=100; /设置select()的超时时间 select(0,&fdread,NULL,NULL,&tv);晒 程序/* 每次调用select前都要重新设置文件描述符和时间,因为事件发生后,文件描述符和时间都会被内核修改 */ 第

21、四部分代码排版if(FD_ISSET(socket_recv,&fdread) /接收服务器端发送的信息 recvfrom(socket_recv,rebuf,sizeof(rebuf),0,(SOCKADDR*)&addrcl,&len);printf(n*n);sprintf(temp,%s say :n%s,ServerIp,rebuf); printf(“%sn”,temp); /打印接收到的服务器发送的消息 else Sleep(100); 晒 程序第四部分代码排版 closesocket(socket_recv); /关闭socket,释放网络 WSACle

22、anup(); printf(exitn); return 0; 晒 程序第四部分代码排版int main() WSADATA wsaData; DWORD dwThreadId; /用于存储线程的ID;线程的 ID 是唯一的;而句柄可能不只一个 SOCKET socket_send; unsigned long cmd; int nStatus; int Ret = WSAStartup(MAKEWORD(2,2),&wsaData); printf(=Client=n); if(Ret != 0) printf(n无法初始化winsock.n); WSACleanup(); else printf(n初始化winsock成功!nn); 晒 程序第四部分代码排版printf(请输入 【服务器端】 IP:); scanf(%s,ServerIp); printf(请输入协议端口号:); scanf(%d,&CLIport); CreateThread(NULL, 0, ThreadProc, 0, 0, &dwThreadId); /创建接收线程 socket_send=socket(AF_INET,SOCK_DGRAM,0); nStatus=ioc

温馨提示

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

评论

0/150

提交评论