select 模型

本文介绍了一个基于Select模型的TCP服务器实现,该服务器能够监听并处理多个客户端连接请求。文章详细展示了如何使用Winsock库在Windows环境下创建、配置及管理套接字,包括初始化Winsock、设置监听端口、接收客户端连接以及处理客户端数据等关键步骤。


参考图书:网络与通信程序设计

使用VC6.0学习,后续使用Visual Studio 其他版本;

//////////////////////////////////////////////////////
// select.cpp

#include "../common/initsock.h"
#include <stdio.h>	

CInitSock initSock;		// 初始化Winsock库

int main()
{
	USHORT nPort = 4567;	// 此服务器监听的端口号

	// 创建监听套节字
	SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(nPort);
	sin.sin_addr.S_un.S_addr = INADDR_ANY;

	// 绑定套节字到地址、端口
	if (::bind(sListen, (sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) 
	{
		printf("Failed bind() \n");

		return -1;
	}

	// 进入监听模式
	::listen(sListen, 5);

	// select模型处理过程
	// 1)初始化一个套接字集合fdSocket,添加监听套接字句柄到这个集合
	fd_set fdSocket;
	FD_ZERO(&fdSocket);				// 初始化集合fd_set
	FD_SET(sListen, &fdSocket);		// 添加套接字到集合

	while (TRUE) 
	{
		// 2)将fdSocket集合的一个拷贝fdRead传递给select函数,
		// 当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套节字句柄,然后返回。
		// 当select调用完成后在确定socket是否依然还在readfds集合中,如果还在,说明socket可读了;
		fd_set fdRead = fdSocket;
		int nRet = ::select(0, &fdRead, NULL, NULL, NULL);
		if (nRet > 0) 
		{
			// 3)通过将原来fdSocket集合与select处理过的fdRead集合比较,
			// 确定都有哪些套节字有未决I/O,并进一步处理这些I/O。
			for(int i = 0; i < (int)fdSocket.fd_count; i++)
			{
				if (FD_ISSET(fdSocket.fd_array[i], &fdRead)) 
				{
					if (fdSocket.fd_array[i] == sListen)	// (1)监听套节字接收到新连接
					{
						if (fdSocket.fd_count < FD_SETSIZE) 
						{
							sockaddr_in addrRemote;
							int nAddrLen = sizeof(addrRemote);
							SOCKET sNew = ::accept(sListen, (SOCKADDR *)&addrRemote, &nAddrLen);
							FD_SET(sNew, &fdSocket);
							printf("接受到连接(%s) \n", ::inet_ntoa(addrRemote.sin_addr));
						}
						else
						{
							printf("Too much connections! \n");
							continue;
						}
					}
					else
					{
						char szText[256];
						int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);

						if (nRecv > 0)			// (2)可读
						{
							szText[nRecv] = '\0';
							printf("接受到数据:%s\n", szText);
						}
						else					// (3)连接关闭、重启或者中断
						{
							::closesocket(fdSocket.fd_array[i]);
							FD_CLR(fdSocket.fd_array[i], &fdSocket);
						}
					}
				}
			}
		}
		else
		{
			printf("Failed select() \n");
			break;
		}

	}

	return 0;
}


客户端测试程序:

暂时不会开线程连接;

//////////////////////////////////////////////////////
// TCPClient.cpp

#include "../common/initsock.h"

#include <stdio.h>

CInitSock initSock;		// 初始化Winsock库

int main()
{
	// 创建套接字
	SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (s == INVALID_SOCKET) 
	{
		printf("Failed socket() \n");
		return 0;
	}

	// 也可以在这里调用bind函数绑定一个本地地址
	// 否则系统将会自动安排
	
	// 填写远程地址信息
	sockaddr_in servAddr;
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(4567);
	// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址
	// 如果你的计算机没有联网,直接使用127.0.0.1即可
	servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	if (::connect(s, (sockaddr *) &servAddr, sizeof(servAddr)) == -1) 
	{
		printf("Failed connect() \n");

		return 0;
	}

	// 发送数据
	char szText[] = "TCP Server Demo! \r\n";
	::send(s, szText, strlen(szText), 0);
	
	/*
	// 接受数据
	char buff[256];
	int nRecv = ::recv(s, buff, 256, 0);

	if (nRecv > 0) 
	{
		buff[nRecv] = '\0';
		printf(" 接收到数据:%s", buff);
	}
	*/

	// 关闭套接字
	::closesocket(s);
	return 0;
}


///////////////////////////////////////////
// initsock.h

#include <winsock2.h>

#pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib

class CInitSock
{
public:
	CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
	{	// 初始化WS_32.dll
		WSADATA wsaData;
		WORD sockVersion = MAKEWORD(minorVer, majorVer);
		if(::WSAStartup(sockVersion, &wsaData) != 0)
		{ 
			exit(0);
		}
	}

	~CInitSock()
	{
		::WSACleanup();
	}
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值