winsock select模型实现

select模型的原理是在指定的socket 的数组中轮询的采集是否可写、可读、有异常的信息,然后将可以操做的套接字 保存在指定的数组中。

// selectsocket.cpp : 定义控制台应用程序的入口点。
//
//socket select 模型 缺点实时性不太强

#include "stdafx.h"
#include <iostream>
#include <WinSock2.h>

using namespace std;
#pragma comment(lib,"ws2_32.lib")

struct clientInfo{
	SOCKET connectSock;
	SOCKADDR_IN clientAddr;
};

clientInfo _arrayClientInfo[FD_SETSIZE];
int _curCount = 0;

DWORD WINAPI WorkThread(LPVOID lpParameter)
{
	//接受和回复 消息
	FD_SET readAry;
	FD_ZERO(&readAry);
	
	//超时时间
	timeval outTime;
	outTime.tv_sec = 5;
	outTime.tv_usec = 0;

	char buf[1024] ={0};
	while(true)
	{
		FD_ZERO(&readAry);
		for(int i = 0;i < _curCount;i++)
		{
			if(_arrayClientInfo[i].connectSock != -1)
			{
				FD_SET(_arrayClientInfo[i].connectSock,&readAry);
			}
		}

		int rtValue = select(0,&readAry,NULL,NULL,&outTime);
		if(rtValue != 0 && rtValue != SOCKET_ERROR)
		{
			//输出收到的数据并且回复
			for(int i = 0; i< _curCount;i++)
			{
				if(FD_ISSET(_arrayClientInfo[i].connectSock,&readAry))
				{
					if(0 == recv(_arrayClientInfo[i].connectSock,buf,sizeof(buf),0))
					{
						_arrayClientInfo[i].connectSock = -1;
						continue;
					}
					
					cout << "recv data by:" << inet_ntoa(_arrayClientInfo[i].clientAddr.sin_addr) <<" " << buf << endl;

					send(_arrayClientInfo[i].connectSock,"hello",sizeof("hello"),0);
				}
			}
		}
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	//1 初始化windows socket库
	WSADATA wsaData;
	if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))
	{
		cout << "WSAStartup failed" <<endl;
		return -1;
	}

	//2 创建套接字
	SOCKET listenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == listenSocket)
	{
		cout << "socket failed errcode:" << WSAGetLastError() << endl;
		return -1;
	}
	//3 绑定套接字
	SOCKADDR_IN serAddr;
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");	//将字符串转成数字并且是网络字节序
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8000);

	if(0 !=bind(listenSocket,(sockaddr*)&serAddr,sizeof(sockaddr)))
	{
		cout << "bind failed errcode:" << WSAGetLastError() << endl;
		return -1;
	}

	//4 监听
	if(SOCKET_ERROR == listen(listenSocket,10))
	{
		cout << "listen failed errcode:" << WSAGetLastError() << endl;
		return -1;
	}

	cout << "开启监听" << endl;
	//创建线程以应答 客户端的请求
	CreateThread(NULL,NULL,WorkThread,NULL,0,NULL);

	//接受链接请求
	SOCKET connectSock;
	SOCKADDR_IN clientAddr;
	int iSizeofAddr = sizeof(sockaddr);
	while (_curCount < FD_SETSIZE)
	{
		connectSock = accept(listenSocket,(sockaddr*)&clientAddr,&iSizeofAddr);
		if(INVALID_SOCKET != connectSock)
		{
			//建立连接成功
			_arrayClientInfo[_curCount].clientAddr = clientAddr;
			_arrayClientInfo[_curCount].connectSock = connectSock;
			_curCount++;
		}
	}
	//当前达到64 个链接的时候 关闭 监听套接字 本程序只接受64个连接
	closesocket(listenSocket);
	cout << "链接已满" << endl;
	system("pause");
	return 0;
}


缺点是 需要不断的轮询,处理不够及时。

客户端代码:

// sockclient.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <WinSock2.h>

using namespace std;
#pragma comment(lib,"ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2),&wsaData);

	SOCKADDR_IN serAddr;
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8000);

	SOCKET clientSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	connect(clientSocket,(sockaddr*)&serAddr,sizeof(sockaddr));

	char buf[1024] ={0};
	send(clientSocket,"hello server",sizeof("hello server"),0);
	recv(clientSocket,buf,sizeof(buf),0);
	cout << buf << endl;
	closesocket(clientSocket);
	WSACleanup();
	system("pause");
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用winsock库的select函数的示例代码: ```c++ #include <winsock2.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib") //链接ws2_32.lib库文件 int main() { WSADATA wsaData; int iRet = 0; iRet = WSAStartup(MAKEWORD(2,2), &wsaData); //初始化winsock库 if(iRet != NO_ERROR) { printf("WSAStartup() failed with error: %d\n", iRet); return 1; } SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建TCP socket if(sockServer == INVALID_SOCKET) { printf("socket() failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } sockaddr_in addrSrv; addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); addrSrv.sin_addr.s_addr = htonl(INADDR_ANY); iRet = bind(sockServer, (const sockaddr *)&addrSrv, sizeof(addrSrv)); //绑定socket到指定IP和端口号 if(iRet == SOCKET_ERROR) { printf("bind() failed with error: %ld\n", WSAGetLastError()); closesocket(sockServer); WSACleanup(); return 1; } iRet = listen(sockServer, 5); //开始监听 if(iRet == SOCKET_ERROR) { printf("listen() failed with error: %ld\n", WSAGetLastError()); closesocket(sockServer); WSACleanup(); return 1; } fd_set fdRead; //定义读文件描述符集合 FD_ZERO(&fdRead); //清空读文件描述符集合 FD_SET(sockServer, &fdRead); //将监听socket加入读文件描述符集合 while(true) { fd_set fdReadBackup = fdRead; //备份读文件描述符集合,因为select会改变集合内容 iRet = select(0, &fdReadBackup, NULL, NULL, NULL); //等待读文件描述符集合有数据可读 if(iRet == SOCKET_ERROR) { printf("select() failed with error: %ld\n", WSAGetLastError()); break; } else if(iRet > 0) { if(FD_ISSET(sockServer, &fdReadBackup)) //监听socket有数据可读,表示有新连接请求 { sockaddr_in addrClient; int addrClientLen = sizeof(addrClient); SOCKET sockConn = accept(sockServer, (sockaddr *)&addrClient, &addrClientLen); //接受连接请求 if(sockConn == INVALID_SOCKET) { printf("accept() failed with error: %ld\n", WSAGetLastError()); break; } printf("New client connected: %s:%d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port)); FD_SET(sockConn, &fdRead); //将连接socket加入读文件描述符集合 } else //连接socket有数据可读,表示客户端发送了数据 { for(int i = 0; i < fdReadBackup.fd_count; i++) //遍历读文件描述符集合中的所有连接socket { SOCKET sockConn = fdReadBackup.fd_array[i]; if(sockConn != sockServer) //排除监听socket { char szBuf[1024] = {0}; iRet = recv(sockConn, szBuf, sizeof(szBuf), 0); //接收数据 if(iRet == SOCKET_ERROR) { printf("recv() failed with error: %ld\n", WSAGetLastError()); break; } else if(iRet == 0) //对方关闭连接 { printf("Client disconnected.\n"); closesocket(sockConn); FD_CLR(sockConn, &fdRead); //从读文件描述符集合中移除连接socket } else //正常接收到数据 { printf("Received data from client: %s\n", szBuf); send(sockConn, szBuf, iRet, 0); //回复相同的数据 } } } } } } closesocket(sockServer); WSACleanup(); return 0; } ``` 该代码实现了一个简单的TCP服务器,通过select函数实现了多路复用,可以同时处理多个客户端连接。在主循环中,使用select函数等待读文件描述符集合中的socket有数据可读,当有新连接请求时,将连接socket加入读文件描述符集合,当连接socket有数据可读时,接收数据并回复相同的数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值