TCP流式套接字的事件对象I/O管理WSAEventSelect编程

98 阅读3分钟

WSAEventSelect()函数和WSAAsyncSelect()函数类似,它们的区别在于当一个FD_XXX网络事件发生时,WSAEventSelect()函数将导致一个应用程序指定的事件对象将被设置,即将网络事件投递到一个事件对象句柄,而不是将网络事件(消息)投递至一个窗口句柄。

WSAEventSelect()函数原型如下:

int WSAEventSelect(
    SOCKET s,  //一个标识套接口的描述字
    WSAEVENT hEventObject,  //是一个由WSACreateEvent(?)函数创建的事件对象句柄,用于标识与所提供的FD_XXX网络事件集合相关的一个事件对象
    long lNetworkEvents //指定应用程序感兴趣的各种网络事件(FD_XXX)的组合
);

以下为测试WSAEventSelect()函数的程序,一个服务器端两个客户端

下面是服务器端程序:

/************************************************************************/ 
/* 事件对象I/O管理程序实例                                              */ 
/************************************************************************/ 
 
#include <WINSOCK2.H> 
#pragma comment(lib,"ws2_32"
#include <stdio.h> 
 
int main() 
{ 
    printf("服务端程序\n"); 
    //------①加载---------- 
    WSADATA wsaData; 
    if (WSAStartup(MAKEWORD(2,2),&wsaData)!=0) 
    { 
        printf("WSAStartup Failed,Error=【%d】\n",WSAGetLastError()); 
        return 1; 
    } 
    else 
        printf("①加载成功\n"); 
    //-------②创建流式套接字------------ 
    SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
    if (s==INVALID_SOCKET) 
    { 
        printf("socket() Failed,Error=【%d】\n",WSAGetLastError()); 
        return 1; 
    } 
    else 
        printf("②已创建监听套接口:【%d】\n",s); 
    //将套接口s置于”非阻塞模式“ 
    u_long u1=1; 
    ioctlsocket(s,FIONBIO,(u_long*)&u1); 
    //-----------③绑定本地地址--------------------- 
    struct sockaddr_in Sadd; 
    Sadd.sin_family=AF_INET; 
    Sadd.sin_port=htons(5555); 
    Sadd.sin_addr.S_un.S_addr=inet_addr("192.168.31.1"); 
    if (bind(s,(sockaddr*)&Sadd,sizeof(Sadd))==SOCKET_ERROR) 
    { 
        printf("bind() Failed,Error=【%d】\n",WSAGetLastError()); 
        return 1; 
    } 
    else 
        printf("③绑定成功,本地IP地址:【%s】,端口号:【%d】\n",inet_ntoa(Sadd.sin_addr),ntohs(Sadd.sin_port)); 
    //--------------④进入监听状态----------------- 
    if (listen(s,5)==SOCKET_ERROR) 
    { 
        printf("listen Failed,Error=【%d】\n",WSAGetLastError()); 
        return 1; 
    } 
    else 
        printf("④进入监听状态\n"); 
    //--------------⑤创建事件对象----------------- 
    WSAEVENT NewEvent=WSACreateEvent(); 
    if (NewEvent==WSA_INVALID_EVENT) 
    { 
        printf("WSACreateEvent() Failed,Error=【%d】\n",WSAGetLastError()); 
        return 1; 
    } 
    else 
        printf("⑤创建一个事件对象,返回的事件对象句柄NewEvent=%d\n",NewEvent); 
    //--------------⑥网络事件注册------------ 
    int WESerror=WSAEventSelect(s,NewEvent,FD_ACCEPT|FD_CLOSE); 
    if (WESerror==INVALID_SOCKET) 
    { 
        printf("WSAEventSelect() Failed,Error=【%d】\n",WSAGetLastError()); 
        return -1; 
    } 
    else 
        printf("⑥套接口【%d】、事件对象【%d】和网络事件FD_ACCEPT|FD_CLOSE已关联\n",s,NewEvent); 
    //-----------准备工作--------------- 
    int t=1; 
    WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS]; 
    SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS]; 
    int n=0; 
    eventArray[n]=NewEvent; 
    sockArray[n]=s; 
    n++; 
    //------------循环处理------------- 
    while (true) 
    { 
        //---------------⑦等待事件对象-------------- 
        int nIndex=WSAWaitForMultipleEvents(n,eventArray,FALSE,40000,FALSE); 
        printf("nIndex=%d\n",nIndex); 
        if (nIndex==WSA_WAIT_FAILED)//------7.1调用失败--------- 
        { 
            printf("调用失败\n"); 
            break; 
        } 
        else if (nIndex==WSA_WAIT_TIMEOUT)//-------7.2超时--------- 
        { 
            if (t<3) 
            { 
                printf("第【%d】次超时\n",t); 
                t++; 
                continue; 
            } 
            else 
            { 
                printf("第【%d】次超时,退出\n",t); 
                break; 
            } 
        } 
        //---------------7.3网络事件触发事件对象句柄的工作状态-------- 
        else 
        { 
            WSANETWORKEVENTS event;//该结构记录网络事件和对应出错代码 
            //---------⑧网络事件查询----------- 
            WSAEnumNetworkEvents(sockArray[nIndex-WSA_WAIT_EVENT_0],NULL,&event); 
            WSAResetEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]); 
            if ((event.lNetworkEvents&FD_ACCEPT)!=0)       //------8.1处理FD_ACCEPT通知消息 
            { 
                if (event.iErrorCode[FD_ACCEPT_BIT]==0) 
                { 
                    if (n>WSA_MAXIMUM_WAIT_EVENTS) 
                    { 
                        printf("Too many connections!\n"); 
                        break; 
                    } 
                    SOCKET sNew=accept(sockArray[nIndex-WSA_WAIT_EVENT_0],NULL,NULL); 
                    NewEvent=WSACreateEvent(); 
                    WSAEventSelect(sNew,NewEvent,FD_READ|FD_CLOSE); 
                    eventArray[n]=NewEvent; 
                    sockArray[n]=sNew; 
                    n++; 
                } 
            } 
            else if (event.lNetworkEvents&FD_READ)    //-------8.2处理FD_READ通知消息 
            { 
                if (event.iErrorCode[FD_READ_BIT]==0) 
                { 
                    char buf[256]; 
                    memset(buf,0,256); 
                    int nRecv=recv(sockArray[nIndex-WSA_WAIT_EVENT_0],buf,sizeof(buf),0); 
                    if (nRecv>0) 
                    { 
                        printf("接收到客户端【%d】数据:%s\n",sockArray[nIndex-WSA_WAIT_EVENT_0],buf); 
                    } 
                } 
            } 
            else if (event.lNetworkEvents&FD_CLOSE)  //---------8.3处理FD_CLOSE通知消息 
            { 
                if (event.iErrorCode[FD_CLOSE_BIT]==0) 
                { 
                    closesocket(sockArray[nIndex-WSA_WAIT_EVENT_0]); 
                    WSACloseEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]); 
                    printf("套接字为【%d】的已关闭连接\n",sockArray[nIndex-WSA_WAIT_EVENT_0]); 
                } 
                else 
                { 
                    if (event.iErrorCode[FD_CLOSE_BIT]==10053) 
                    { 
                        closesocket(sockArray[nIndex-WSA_WAIT_EVENT_0]); 
                        WSACloseEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]); 
                        printf("客户端【%d】非法关闭连接\n",sockArray[nIndex-WSA_WAIT_EVENT_0]); 
                    } 
                } 
                for (int j=nIndex-WSA_WAIT_EVENT_0;j<n-1;j++) 
                { 
                    sockArray[j]=sockArray[j+1]; 
                    eventArray[j]=eventArray[j+1]; 
                } 
                n--; 
            } 
        }// end 网络事件触发 
    }//end while 
    printf("退出服务器程序\n"); 
    closesocket(s); 
    WSACleanup(); 
    return 0; 
} 

客户端程序有两个,代码差不多,只是IP变了,这里给出一个

//Client 1
#include <winsock2.h>
#pragma comment(lib,"WS2_32")
#include <stdio.h>
int main()
{  	printf("客户端程序/n"); 	
//----------------①加载---------------------------------------------------------
    WSADATA wsaData;
    WORD wVersionRequested=MAKEWORD(2,2);  
	if(WSAStartup(wVersionRequested,&wsaData)!=0)  			
	{   printf("WSAStartup() Failed,Error=/n",WSAGetLastError());	
		return 1;
	}
	else 
		printf("①加载成功/n");		
//----------------②创建流式套节字------------------------------------------------	
	SOCKET c1;
	c1 = socket(AF_INET, SOCK_STREAM,0);
	if(c1 == INVALID_SOCKET)	
	{   printf("socket() Failed,Error=/n",WSAGetLastError());
		return 1;
	}
	else 
		printf("②已创建连接套接字:【 %d 】/n",c1);	
//----------------绑定本地地址------------------------------------------------
    struct  sockaddr_in  C1add;  
    C1add.sin_family=AF_INET;
	C1add.sin_port=htons(2222);
	C1add.sin_addr.s_addr=inet_addr("192.168.31.2");	
	if(bind(c1,(sockaddr*)&C1add,sizeof(C1add))==SOCKET_ERROR)	
	{   printf("bind() Failed,Error=/n",WSAGetLastError());	
		return 1;
	}
	else 
		printf("绑定成功,本地IP地址:【 %s 】,端口号:【 %d 】/n",inet_ntoa(C1add.sin_addr),ntohs(C1add.sin_port));	
//------------------③连接请求------------------------------------------------		
	struct  sockaddr_in  Sadd;  
	Sadd.sin_family=AF_INET;
	Sadd.sin_port=htons(5555);  
	Sadd.sin_addr.s_addr=inet_addr("192.168.31.1");	
	if(connect(c1, (sockaddr*)&Sadd, sizeof(Sadd)) == -1)
	{   printf(" Failed connect(),Error=【 %d 】/n",WSAGetLastError());		
		return 1;
	}
	else //*************************连接成功,可以 开始发送、接收*************************
	{	printf("③连接成功,服务器IP地址:【 %s 】,端口号:【 %d 】/n",inet_ntoa(Sadd.sin_addr),ntohs(Sadd.sin_port));
	    int a;
	    printf("希望发送数据吗?(键入“1”发送)");
		scanf("%d",&a);	 			
	    while(a==1)  //------循环处理------
		{   //-----发送------     			    
            char S_buf[] = "Hello!I am a client 1";	
		    int  isend=send(c1,S_buf,strlen(S_buf),0);		
		    if(isend==SOCKET_ERROR)	
			{  printf("Failed  send(),Error=【 %d 】,或者服务器意外关闭/n",WSAGetLastError());
		   	   return 1;
			}
		    else if(isend!=0)
			   printf("信息【 %s 】已发送/n",S_buf);			 
		    else
			   printf("信息无法发送给服务端/n");

			printf("希望继续发送数据吗?(键入“1”继续发送)");
				scanf("%d",&a);
    	        if(a!=1)			
		        break;
		} //------end 循环处理------	   
	} //*************************end 开始发送、接收*************************
//------------------④关闭、释放------------------------------------------------	
    closesocket(c1);		
	WSACleanup();
	printf("④与服务器连接完毕/n");
	return 0;
	
}

先启动服务器端,再启动1号客户端,接着启动2号客户端,就会看到如图所示:

0_1275015429v6Vi.gif