WinSocket的select函数的用法(windows套接字比较研究)

总体上来说select函数的作用:

确定一个或多个套接口的状态,本函数用于确定一个或多个套接口的状态,对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息,用fd_set结构来表示一组等待检查的套接口,在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足条件的套接口的数目。


简单来说select用来填充一组可用的socket句柄,当满足下列之一条件时:

1.可以读取的sockets。当这些socket被返回时,在这些socket上执行recv/accept等操作不会产生阻塞;
2.可以写入的sockets。当这些socket被返回时,在这些socket上执行send等不会产生阻塞;
3.返回有错误的sockets。

select的处理函数一般有:

同时和select配对使用的还有:
FD_CLR(s, *set) 
Removes the descriptor s from set. 
FD_ISSET(s, *set) 
Nonzero if s is a member of the set. Otherwise, zero. 
FD_SET(s, *set) 
Adds descriptor s to set. 
FD_ZERO(*set) 
Initializes the set to the null set. 


应该都知道,accept()函数是一个阻塞的函数,当有客户端请求连接服务端时才返回。如下面的例子

server端:

#include <iostream>
#include <Windows.h>
using namespace std;
#define  PORT 4000
#define  IP_ADDRESS "127.0.0.1"
#pragma comment(lib, "WS2_32.lib")
int main(int argc, char* argv[])
{
	WSADATA  Ws;
	SOCKET ServerSocket, CientSocket;
	struct sockaddr_in LocalAddr, ClientAddr;
	int Ret = 0;
	int AddrLen = 0;
	HANDLE hThread = NULL;

	//Init Windows Socket
	if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
	{
		cout << "Init Windows Socket Failed::" << GetLastError() << endl;
		return -1;
	}

	ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (ServerSocket == INVALID_SOCKET)
	{
		cout << "Create Socket Failed::" << GetLastError() << endl;
		return -1;
	}

	LocalAddr.sin_family = AF_INET;
	LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
	LocalAddr.sin_port = htons(PORT);
	memset(LocalAddr.sin_zero, 0x00, 8);

	Ret = bind(ServerSocket, (struct sockaddr*)&LocalAddr, sizeof(LocalAddr));
	if (Ret != 0)
	{
		cout << "Bind Socket Failed::" << GetLastError() << endl;
		return -1;
	}

	Ret = listen(ServerSocket, 10);
	if (Ret != 0)
	{
		cout << "listen Socket Failed::" << GetLastError() << endl;
		return -1;
	}
	cout << "服务端已经启动" << endl;

	char RecvBuffer[MAX_PATH];
	while (true)
	{
		AddrLen = sizeof(ClientAddr);
		CientSocket = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &AddrLen);//阻塞的
		if (CientSocket == INVALID_SOCKET)
		{
			cout << "Accept Failed::" << GetLastError() << endl;
			break;
		}
		cout << "客户端连接::" << inet_ntoa(ClientAddr.sin_addr) << ":" << ClientAddr.sin_port << endl;
		int Ret = 0;
		while (true)
		{
			memset(RecvBuffer, 0x00, sizeof(RecvBuffer));
			Ret = recv(CientSocket, RecvBuffer, MAX_PATH, 0);
			if (Ret == 0 || Ret == SOCKET_ERROR)
			{
				cout << "客户端退出!" << endl;
				break;
			}
			cout << "接收到客户信息为:" << RecvBuffer << endl;
		}
		CloseHandle(hThread);
	}
	closesocket(ServerSocket);
	closesocket(CientSocket);
	WSACleanup();

	return 0;
}

client端:

#include <iostream>
#include <Windows.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;

#define  PORT 4000
#define  IP_ADDRESS "127.0.0.1"

int main(int argc, char* argv[])
{
	WSADATA  Ws;
	SOCKET CientSocket;
	struct sockaddr_in ServerAddr;
	int Ret = 0;
	int AddrLen = 0;
	HANDLE hThread = NULL;
	char SendBuffer[MAX_PATH];

	//Init Windows Socket
	if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
	{
		cout << "Init Windows Socket Failed::" << GetLastError() << endl;
		return -1;
	}

	//Create Socket
	CientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (CientSocket == INVALID_SOCKET)
	{
		cout << "Create Socket Failed::" << GetLastError() << endl;
		return -1;
	}

	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
	ServerAddr.sin_port = htons(PORT);
	memset(ServerAddr.sin_zero, 0x00, 8);

	Ret = connect(CientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));
	if (Ret == SOCKET_ERROR)
	{
		cout << "Connect Error::" << GetLastError() << endl;
		return -1;
	}
	else
	{
		cout << "连接成功!" << endl;
	}

	while (true)
	{
		cin.getline(SendBuffer, sizeof(SendBuffer));
		Ret = send(CientSocket, SendBuffer, (int)strlen(SendBuffer), 0);
		if (Ret == SOCKET_ERROR)
		{
			cout << "Send Info Error::" << GetLastError() << endl;
			break;
		}
	}

	closesocket(CientSocket);
	WSACleanup();

	return 0;
}
上面的例子一个客户端只能接收处理一个客户端的请求!

加上select的应用,就能让一个server处理来自多个客户端的请求:

改写后的server端

#include <WinSock2.h>
#include <stdio.h>
#pragma  comment(lib,"ws2_32.lib")
#define PORT 4000

bool InitAndListern(SOCKET &sListen)
{
	WSADATA wsaData;
	sockaddr_in local;
	WORD version = MAKEWORD(2, 0);
	int ret = WSAStartup(version, &wsaData);
	if (ret != 0)
	{
		printf("WSAStarup failed\n");
		return 0;
	}
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = INADDR_ANY;
	local.sin_port = htons((u_short)PORT);

	sListen = socket(AF_INET, SOCK_STREAM, 0);
	if (sListen == INVALID_SOCKET)
	{
		printf("Initial socket failed\n");
		return 0;
	}

	if (bind(sListen, (sockaddr*)&local, sizeof(local)) != 0)
	{
		printf("Bind socket failed\n");
		return 0;
	}

	if (listen(sListen, 10) != 0)
	{
		printf("Listen socket failed\n");
		return 0;
	}
	return 1;
}
int main()
{
	SOCKET sListen;
	if (InitAndListern(sListen) == 0)
		return 0;
	printf("Server wait for client connect...\n");
	fd_set fdSocket;
	FD_ZERO(&fdSocket);
	FD_SET(sListen, &fdSocket);//将sListen添加进该集合
	while (true)
	{
		fd_set fdRead = fdSocket;
		int nRet = select(NULL, &fdRead, NULL, NULL, NULL);

		if (nRet <= 0)
			break;
		for (int i = 0; i < (int)fdSocket.fd_count; ++i)
		{
			if (FD_ISSET(fdSocket.fd_array[i], &fdRead))
			{
				if (fdSocket.fd_array[i] == sListen)
				{
					sockaddr_in addrRemote;
					int nAddrLen = sizeof(addrRemote);
					SOCKET sNew = ::accept(sListen, (sockaddr*)&addrRemote, &nAddrLen);
					FD_SET(sNew, &fdSocket);
					printf("Clietn %s connected\n", inet_ntoa(addrRemote.sin_addr));
				}
				else
				{
					char buffer[1024];
					memset(buffer, 0, 1024);
					int nRecev = recv(fdSocket.fd_array[i], buffer, 1024, 0);

					if (nRecev > 0)
					{
						printf("Received Client Msg:%s\n", buffer);
						send(fdSocket.fd_array[i], buffer, strlen(buffer), 0);
					}
					else
					{
						closesocket(fdSocket.fd_array[i]);
						FD_CLR(fdSocket.fd_array[i], &fdSocket);
					}
				}
			}
		}
	}
	return 0;
}


WinSocket的select函数的用法(windows套接字比较研究)

上一篇:Delphi项目构成之项目文件DPR


下一篇:DelphiXE7 FMX 退出程序写法!