简单的 winsock select模式

// WinSelectModel.cpp : 定义控制台应用程序的入口点。
//
//server side ,select model
/*
Syntax: 
int select(
_In_    int                  nfds,//Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.
_Inout_ fd_set               *readfds,//An optional pointer to a set of sockets to be checked for readability.
_Inout_ fd_set               *writefds,//An optional pointer to a set of sockets to be checked for writability.
_Inout_ fd_set               *exceptfds,//An optional pointer to a set of sockets to be checked for errors.
_In_    const struct timeval *timeout//The maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.
)
功能:简单的讲就是一个socket“复用器”(不必为第个客户建立新线程,但需要不断轮询fd_set检测,fd_set的大小固定64),它能够检测报告一个或多个socket状态 ,但不如WSAASyncSelct()那么细致,第次调用,select ()把一组socket作为输入参数,而且它是阻塞的(可设置),也就是说该函数能使你同时检测多个socket的状态,它需要通过返回值来带回执行结果
Return value:成功返回当前状态与设定状态相匹配的socket的总数,超时返回0(timeout参数),失败返回SOCKET_ERROR 
remarks:它与BSD兼容 ,也可以使用WSAAsyncSelect() ,timeout非零则阻塞,零则不阻塞(但不要传NULL为无限阻塞)
应用:网络连接数不大的程序 
与select配合使用的宏
FD_ZERO(*set) :对fd_set初始化
FD_SET(s, *set):添加指定s套接字至集合
FD_CLR(s, *set):删除指定s套接字至集合
FD_ISSET(s, *set):检查指定s套接字是否集合
FD_SETSIZE: 64 
*/
#include "stdafx.h"
#include<iostream>
#include<WinSock2.h>                    // socket 所需要的头文件
#pragma comment(lib,"WS2_32.lib")// link socket 库
#define PORT 6666 
#define BUFLEN 1024
using namespace std;
fd_set g_fdClientSocket;
//fd_set 
DWORD WINAPI ThreadProc(LPVOID lpParameter);
int main()
{

	DWORD dwThreadID;
	sockaddr_in addrClient;
	int addrClientLen = sizeof(addrClient);
	SOCKET sServer = INVALID_SOCKET;
	SOCKET sClient;
	int nClientCount = 0;
	// 1 启动并初始化winsock(WSAStarup)
	WSADATA wsaData;
	if (WSAStartup(0x202, &wsaData))//成功返回0
	{
		return FALSE;
	}
	//2 创建套接字(socket)
	 sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sServer)
	{
		WSACleanup();
		return FALSE;
	}
	//3 准备通信地址
	SOCKADDR_IN addrServer;
	addrServer.sin_family = AF_INET;
	addrServer.sin_port = htons(PORT);//服务器用于监听的端口号,客户端需要知道这个端口进行连接
	addrServer.sin_addr.s_addr =  INADDR_ANY;
	//4 绑定地址与socket(bind)
	if (SOCKET_ERROR == bind(sServer, (const sockaddr*)&addrServer, sizeof(SOCKADDR_IN)))
	{
		closesocket(sServer);
		WSACleanup();
		return FALSE;
	}
	//5 监听 (listen)
	if (SOCKET_ERROR == listen(sServer, SOMAXCONN))
	{
		closesocket(sServer);
		WSACleanup();
	}
	//6 线程利用select()处理可读的客户端
	CreateThread(NULL, 0, ThreadProc, NULL, 0, &dwThreadID);
	// 7 等待多个客户端连接(accpet)
	while (nClientCount<FD_SETSIZE) 
	{
		 sClient= accept(sServer, (sockaddr *)&addrClient, &addrClientLen);
		if (INVALID_SOCKET == sClient)
		{
			cout << WSAGetLastError() << endl;
			//或根据错误码进行其他操作
			closesocket(sServer);
			closesocket(sClient);
			WSACleanup();
			return FALSE;
		}
		printf("Accepted client:%s:%d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));
		FD_SET(sClient,& g_fdClientSocket);
		nClientCount++;
	}
	system("pause");
	return TRUE;
}
// 只处理是可读状态的socket
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	fd_set fdRead;
	fd_set fdWrite;
	FD_ZERO(&fdRead);
	FD_ZERO(&fdWrite);

	int nRet = 0;

	char recvBuffer[1024] = {};

	struct timeval stTimeOut = { 1,0 };

	while (true)
	{
		fdRead = g_fdClientSocket;
		fdWrite = g_fdClientSocket;
		nRet = select(0, &fdRead, &fdWrite, NULL, &stTimeOut);
 		if (SOCKET_ERROR != nRet)
		{
			for (int i = 0; i < g_fdClientSocket.fd_count; i++)
			{
				if (FD_ISSET(g_fdClientSocket.fd_array[i], &fdRead))
				{
					memset(recvBuffer, 0, sizeof(recvBuffer));
					SOCKADDR_IN stAddrTemp;
					int nTempLen = sizeof(stAddrTemp);
					nRet = recvfrom(g_fdClientSocket.fd_array[i], recvBuffer, sizeof(recvBuffer), 0, (sockaddr*)&stAddrTemp, &nTempLen);
					if (SOCKET_ERROR == nRet)
					{
						closesocket(g_fdClientSocket.fd_array[i]);
						FD_CLR(g_fdClientSocket.fd_array[i], &g_fdClientSocket);
					}
					else
					{
						cout << "the client(" << inet_ntoa(stAddrTemp.sin_addr) << ":" << ntohs(stAddrTemp.sin_port) << ") :" << recvBuffer << "(message size is " << nTempLen << ")" << endl;
					}
				}
				if (FD_ISSET(g_fdClientSocket.fd_array[i], &fdWrite))
				{
					nRet = send(g_fdClientSocket.fd_array[i], "hello Client", sizeof("hello Client"), 0);
					if (SOCKET_ERROR == nRet)
					{
						int nErrorNo = WSAGetLastError();
						cout << "send error code is " << nErrorNo << endl;//10038  socket handle parameter did not reference a valid socket, or for select, a member of an fd_set was not valid.
						closesocket(g_fdClientSocket.fd_array[i]);
						FD_CLR(g_fdClientSocket.fd_array[i], &g_fdClientSocket);
					}
					else
					{
						continue;
					}
				}
			}
		}
	}
}

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

#include "stdafx.h"
#include<WinSock2.h>
//客户端 
#include<iostream>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
#define BUFLEN 1024
#define PORT 6666
void getIP(char*szHostaddress);
void SendProc();
SOCKET sHost;
int main()
{
	WSADATA wsaData;
	// 1 启动并初始化winsock(WSAStarup)
	if (WSAStartup(MAKEWORD(2, 2), &wsaData))//成功返回0
	{
		return FALSE;
	}
	//2 创建套接字(socket)
	sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sHost)
	{
		closesocket(sHost);
		WSACleanup();
		return FALSE;
	}
	//3 准备通信地址
	char szHostaddress[200];
	getIP(szHostaddress);
	SOCKADDR_IN addrServer;
	addrServer.sin_family = AF_INET;
	addrServer.sin_port = htons(PORT);
	addrServer.sin_addr.s_addr = inet_addr(szHostaddress);
	//4 连接服务器(connect)
	if (SOCKET_ERROR == connect(sHost, (const sockaddr*)&addrServer, sizeof(addrServer)))//连接到指定的地址
	{
		closesocket(sHost);
		WSACleanup();
		return FALSE;
	}
	//5 发送数据 (send)
	char sendBuf[BUFLEN] = "你好服务器!";
	if (SOCKET_ERROR == send(sHost, sendBuf, sizeof(sendBuf), 0))
	{
		closesocket(sHost);
		WSACleanup();
		return FALSE;
	}
	cout << "客户端发送消息成功!" << endl;
	//7 接收数据(recv)
	char recvBuf[BUFLEN];
	ZeroMemory(recvBuf, sizeof(recvBuf));
	if (SOCKET_ERROR == recv(sHost, recvBuf, sizeof(recvBuf), 0))
	{
		closesocket(sHost);
		WSACleanup();
		return FALSE;
	}
	cout << "服务器发来的消息:" << recvBuf << endl;
	while (true)
	{
		SendProc();
		Sleep(1000);
	}
	system("pause");
}
void SendProc(
	)
{
	char sendBuf[BUFLEN] = "你好服务器!";
	if (SOCKET_ERROR == send(sHost, sendBuf, sizeof(sendBuf), 0))
	{
		closesocket(sHost);
		WSACleanup();
		return ;
	}
	cout << "客户端发送消息成功!" << endl;
}

void getIP(char*szHostaddress) {
	char szHostname[100];
	if (gethostname(szHostname, sizeof(szHostname)) != SOCKET_ERROR)//先得到主机名
	{
		HOSTENT *pHostEnt = gethostbyname(szHostname);//通过名字拿到地址
		if (pHostEnt != NULL)
		{
			sprintf(szHostaddress, "%d.%d.%d.%d",
				(pHostEnt->h_addr_list[0][0] & 0x00ff),
				(pHostEnt->h_addr_list[0][1] & 0x00ff),
				(pHostEnt->h_addr_list[0][2] & 0x00ff),
				(pHostEnt->h_addr_list[0][3] & 0x00ff));
		}
	}
	else
		return;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值