基于WSAEventSelect模型的socket通信

该文章描述了一个C++程序,使用Winsock库实现在一个服务器上监听多个客户端连接,记录并显示不同客户端的端口号,以及在断开连接时的状态。
摘要由CSDN通过智能技术生成

        在多客户端连接发送信息的条件下,能够显示不同客户端的端口号,在断开连接后显示断开客户端的端口号。

#pragma warning(disable:4996)
#include<iostream>
#include<WinSock2.h>
#include<tchar.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
	//事件句柄和socket句柄表
	WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];
	SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS];
	int nEventTotal = 0;
	USHORT nPort = 9990;//监听端口号

	//初始化环境
	WSADATA ws;
	WSAStartup(MAKEWORD(2, 2), &ws);
	//创建监听
	SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//设置地址绑定socket
	sockaddr_in sin;
	
	sin.sin_family = AF_INET;
	sin.sin_port = htons(nPort);
	sin.sin_addr.S_un.S_addr = INADDR_ANY;
	::bind(sListen, (struct sockaddr*)&sin, sizeof(struct sockaddr));
	::listen(sListen, 5);
	cout << "成功监听端口" << ntohs(sin.sin_port) << endl;
	//创建事件对象,并注册网络事件
	WSAEVENT myevent = ::WSACreateEvent();
	::WSAEventSelect(sListen, myevent, FD_ACCEPT | FD_CLOSE);
	eventArray[nEventTotal] = myevent;
	sockArray[nEventTotal] = sListen;
	nEventTotal++;

	//处理网络事件
	while (TRUE)
	{
		//在所有事件对象上等待,只要有一个事件对象变为已授信,则函数返回
		int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);
		
		nIndex = nIndex - WSA_WAIT_EVENT_0;
		//对每个事件调用wsawaitformultipleevents函数,以确定其状态
		for (int i = nIndex; i < nEventTotal; i++)
		{
			int ret;
			ret = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE);
			if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
			{
				continue;
			}
			else
			{
				WSANETWORKEVENTS event1;
				::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &event1);
				if (event1.lNetworkEvents & FD_ACCEPT)
				{
					if (event1.iErrorCode[FD_ACCEPT_BIT] == 0)
					{
						//连接太多,暂不处理
						if (nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
						{
							printf("太多连接!");
							continue;
						}
						
						int len = sizeof(sockaddr_in);
						SOCKET sNew = ::accept(sockArray[i], (sockaddr*)&sin, &len);

						cout << "接受一个客户端" <<inet_ntoa(sin.sin_addr)<<":["<<sin.sin_port << "]" << endl;
						WSAEVENT newEvent = ::WSACreateEvent();//为新套接字创建对象
						::WSAEventSelect(sNew, newEvent, FD_READ | FD_CLOSE | FD_WRITE);
						//将新建的事件netEvent保存到eventArray数组中
						eventArray[nEventTotal] = newEvent;
						sockArray[nEventTotal] = sNew;
						nEventTotal++;
					}
				}
				else if (event1.lNetworkEvents & FD_READ)
				{
					if (event1.iErrorCode[FD_READ_BIT] == 0)
					{
						char szText[256];
						int nRecv = ::recv(sockArray[i], szText, strlen(szText), 0);
						int ss = sockArray[i];
						int l = sizeof(sockaddr_in);
						if (nRecv > 0)
						{
							
							szText[nRecv] = '\0';
							SYSTEMTIME st;
							GetLocalTime(&st);
							char sDateTime[30];
							getpeername(ss,reinterpret_cast<sockaddr*>(&sin),&l );
							sprintf_s(sDateTime, "%4d-%2d-%2d %2d:%2d:%2d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
							//打印输出信息
							printf("%s接收到来自客户端[%s:%d]:%s \n",sDateTime,inet_ntoa(sin.sin_addr),sin.sin_port,szText );
							strcpy(szText, "msg 接收到消息");
							int nSnd = ::send(sockArray[i], szText, strlen(szText), 0);
						}
					}
				}
				else if (event1.lNetworkEvents & FD_CLOSE)//处理close消息
				{
					//关闭socket,删除组内对应记录
					SYSTEMTIME st;
					GetLocalTime(&st);
					char sDateTime[30];
					sprintf_s(sDateTime, "%4d-%2d-%2d %2d:%2d:%2d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
					printf("%s客户端[%s:%d]:下线 \n", sDateTime, inet_ntoa(sin.sin_addr), sin.sin_port, 0);
					if (event1.iErrorCode[FD_CLOSE_BIT] == 0)
					{
						
						::closesocket(sockArray[i]);						
						for (int j = i; j < nEventTotal - 1; j++)
						{
							eventArray[j] = eventArray[j + 1];
							sockArray[j] = sockArray[j + 1];

						}
						nEventTotal--;
					}
				}
				else if (event1.lNetworkEvents & FD_WRITE)
				{
					char szText[256] = "msg 接收消息";
					int nSnd = ::send(sockArray[i], szText, strlen(szText), 0);
					cout << "客户端连接允许写入数据" << endl;
				}
			}
		}
	}
}

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值