Windows Socket编程TCP范例之四(WSAEventSelect)

    Winsock提供了另一种有用的异步事件通知 I/O 模型——WSAEventSelect模型。这个模型与WSAAsyncSelect模型类似,允许应用程序在一个或者多个套接字上接收基于事件的网络通知。它与 WSAAsyncSelect模型类似是因为它也接收 FD_XXX 类型的网络事件,不过并不是依靠Windows 的消息驱动机制,而是经由事件对象句柄通知。 
    以下示例还是实现和前两篇文章(之一&之二&之三)一样的功能:Client端连接Server端,向Server端发送数据。Server端接受Client端发送过来的数据并输出。Server端还是单线程处理多Client端连接的情况。
Server.cpp-----------------------------

#include <iostream>
#include <winsock2.h>
#include <windows.h>

using namespace std;

#pragma comment(lib, "Ws2_32.lib")

#define PORT_NO 6000
#define BACKLOG 10

// 记录Socket及对应Event的数组及数组大小
WSAEVENT	eventArray[WSA_MAXIMUM_WAIT_EVENTS] = {0};
SOCKET		sockArray[WSA_MAXIMUM_WAIT_EVENTS] = {0};
int nEventTotal = 0;

// 请空序号为nIndex的数组元素:Socket以及Event
void ClearIndex(int nIndex);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	int ret;
	WORD wVersionRequested = MAKEWORD(2, 2);
	SOCKET sockSrv;
	SOCKADDR_IN addrSrv;

	// 初始化Windows Socket------
	ret = WSAStartup(wVersionRequested, &wsaData);
	if (ret != 0) 
	{
		cout << "WSAStartup() failed:" << WSAGetLastError() << endl;
		return -1;
	}

	// 创建Socket------
	sockSrv = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == sockSrv)
	{
		cout << "socket() failed:" << WSAGetLastError() << endl;
		WSACleanup();
		return -1;
	}
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(PORT_NO);

	// Bind Socket------
	ret = bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
	if (SOCKET_ERROR == ret)
	{
		cout << "bind() failed:" << WSAGetLastError() << endl;
		closesocket(sockSrv);
		WSACleanup();
		return -1;
	}

	// 监听------
	ret = listen(sockSrv, BACKLOG);
	if (SOCKET_ERROR == ret)
	{
		cout << "listen() failed:" << WSAGetLastError() << endl;
		closesocket(sockSrv);
		WSACleanup();
		return -1;
	}

	cout<< "Server started......" << endl;

	// 创建与sockSrv对应的WSAEvent并加入数组------
	WSAEVENT wsaEvt = WSACreateEvent();
	WSAEventSelect(sockSrv, wsaEvt, FD_ACCEPT | FD_CLOSE);
	eventArray[nEventTotal] = wsaEvt;
	sockArray[nEventTotal] = sockSrv;	
	nEventTotal++;

	// Socket事件响应------
	while (1)
	{
		int nIndex = WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);
		if (WSA_WAIT_FAILED == nIndex)
		{
			cout << "WSAWaitForMultipleEvents failed:" << WSAGetLastError() << endl;
			break;
		}

		nIndex = nIndex - WSA_WAIT_EVENT_0;
		SOCKET sock = sockArray[nIndex];
		WSAEVENT wsaEvt = eventArray[nIndex];

		WSANETWORKEVENTS netWorkEvts;
		WSAEnumNetworkEvents(sock, wsaEvt, &netWorkEvts);

		if(netWorkEvts.lNetworkEvents & FD_ACCEPT)    // ---FD_ACCEPT
		{
			if(netWorkEvts.iErrorCode[FD_ACCEPT_BIT] == 0)
			{
				SOCKADDR_IN  addrClient;
				int len = sizeof(SOCKADDR);
				SOCKET sockConn = accept(sock, (SOCKADDR*)&addrClient, &len);
				if (INVALID_SOCKET == sockConn)  
				{  
					cout << "accept() failed:" << WSAGetLastError() << endl;  
					continue;  
				}

				cout << "接受到新连接:" << inet_ntoa(addrClient.sin_addr) << endl;
					
				if(nEventTotal >= WSA_MAXIMUM_WAIT_EVENTS)
				{
					cout << "Too many connections!" << endl;
					closesocket(sockConn);
					continue;
				}

				WSAEVENT sockConnEvt = WSACreateEvent();
				WSAEventSelect(sockConn, sockConnEvt, FD_READ | FD_CLOSE | FD_WRITE);
				eventArray[nEventTotal] = sockConnEvt;
				sockArray[nEventTotal] = sockConn;	
				nEventTotal++;
			}
		}
		else if(netWorkEvts.lNetworkEvents & FD_READ)			// ---FD_READ
		{
			if(netWorkEvts.iErrorCode[FD_READ_BIT] == 0)
			{
				char recvBuffer[MAX_PATH] = {0};    
                int ret = recv(sock, recvBuffer, sizeof(recvBuffer), 0);  
                if (ret == 0)    
                {
					cout << "Connection has been gracefully closed." << endl;     
                    closesocket(sock);  
					CloseHandle(wsaEvt);
					ClearIndex(nIndex);
					continue;
                }    
                else if (ret == SOCKET_ERROR)     
                {    
                    cout << "Connection has been closed ungracefully." << endl;     
                    closesocket(sock); 
					CloseHandle(wsaEvt);
					ClearIndex(nIndex);
					continue;
                }    
    
                cout << "Receive Data from Client:" << recvBuffer << endl;    
				
			}
		}
		else if(netWorkEvts.lNetworkEvents & FD_CLOSE)		// ---FD_CLOSE
		{
			if(netWorkEvts.iErrorCode[FD_CLOSE_BIT] == 0)
			{
				cout << "Connection has been gracefully closed." << endl;
			}
			else
			{
				cout << "Connection has been closed ungracefully." << endl;
			}
			closesocket(sock); 
			CloseHandle(wsaEvt);
			ClearIndex(nIndex);
		}
		else if(netWorkEvts.lNetworkEvents & FD_WRITE)		// ---FD_WRITE
		{
		}
	}

	// 清理资源------
	for (int i=0; i<nEventTotal; ++i)
	{
		closesocket(sockArray[i]); 
		CloseHandle(eventArray[i]);
	}
	WSACleanup();

	cout << "exit..." << endl;
	return 0;
}

// 请空序号为nIndex的数组元素:Socket以及Event
void ClearIndex(int nIndex)
{
	// 参数有效性检查   0 =< nIndex <= WSA_MAXIMUM_WAIT_EVENTS-1
	if (nIndex < 0 
		|| nIndex >= WSA_MAXIMUM_WAIT_EVENTS 
		|| nIndex >= nEventTotal)
		return;

	// 清空最后一个元素,则直接赋0
	if (nIndex == nEventTotal - 1)
	{
		eventArray[nIndex] = 0;
		sockArray[nIndex] = 0;
		nEventTotal--;
		return;
	}
    
	// 往前移
	for (int i=nIndex; i<=nEventTotal-2; ++i)
	{
		eventArray[i] = eventArray[i+1];
		sockArray[i] = sockArray[i+1];
		nEventTotal--;
	}
}

Client.cpp-----------------------------

#include <iostream>
#include <windows.h>

using namespace std;

#pragma comment(lib, "Ws2_32.lib")

#define PORT_NO 6000
#define SRV_IP_ADDR "127.0.0.1"

int main(int argc, char* argv[])
{
	int ret;
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD(2, 2);
	SOCKET sockClient;
	SOCKADDR_IN addrSrv;
	
	// 初始化Windows Socket------
	ret = WSAStartup(wVersionRequested, &wsaData);
	if (ret != 0) 
	{
		cout << "WSAStartup() failed:" << WSAGetLastError() << endl;
		return -1;
	}

    // 创建Socket------
	sockClient = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == sockClient)
	{
		cout << "socket() failed:" << WSAGetLastError() << endl;
		WSACleanup();
		return -1;
	}

	addrSrv.sin_addr.S_un.S_addr = inet_addr(SRV_IP_ADDR);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(PORT_NO);

	// 连接------
	ret = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
	if (SOCKET_ERROR == ret)
	{
		cout << "connect() failed:" << WSAGetLastError() << endl;
		closesocket(sockClient);
		WSACleanup();
		return -1;
	}
	else
	{
		cout << "connect() successfully." << endl;
	}

	// 发送数据------
	char sendBuf[MAX_PATH] = {0};
	while (1)
	{
		cin.getline(sendBuf, sizeof(sendBuf));
		if (strcmp(sendBuf, "exit") == 0)
		{
			break;
		}
		ret = send(sockClient, sendBuf, strlen(sendBuf)+1, 0);
		if (SOCKET_ERROR == ret)
		{
			cout << "send() failed:" << WSAGetLastError() << endl;
			closesocket(sockClient);
			WSACleanup();
			return -1;
		}
	}

	// 清理资源-------
	closesocket(sockClient);
	WSACleanup();

	cout << "exit..." << endl;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值