TCP Socket服务器之select模型

TCP Socket服务器之select模型

小结

之前写过一个服务器,后来发现那个只能在窗口模式下运行,不然没有窗口句柄,怎么才能获取窗口消息呢,想想也是,又写了一个在控制台下的运行类吧。

继续造

  1. **WSAStartup ** ,windows异步套接字的启动;还是老套路
    DWORD dwCode;
	WSADATA wsaData;

	dwCode = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (dwCode != 0)
	{
		MessageBox(NULL, L"Can't find find a usable WinSock DLL", L"Error", 0);
		return;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		MessageBox(NULL, L"Can't find the socket version required.", L"Error", 0);
		return;
	}
  1. 创建socket,绑定socket端口和IP,创建线程,注意这里用的是C++11,用的线程比较方便
    m_sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_sServer == INVALID_SOCKET)
	{
		std::cout << "Can't create the socket, error code" << WSAGetLastError() << std::endl;
		return FALSE;
	}

	SOCKADDR_IN sockAddr;
	sockAddr.sin_family = AF_INET;
	sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);//ADDR_ANY;
	//sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	sockAddr.sin_port = htons(port);
	int dwCode = ::bind(m_sServer, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
	if (dwCode == SOCKET_ERROR)
	{
		closesocket(m_sServer);
		std::cout << "Can't bind the socket, error code:" << WSAGetLastError() << std::endl;
		return FALSE;
	}

	if (listen(m_sServer, maxlink) == SOCKET_ERROR)
	{
		std::cout << "Can't listen the socket, error code:" << WSAGetLastError() << std::endl;
		return FALSE;
	}

	m_hAccepthEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
	thread _threadRecv(ThreadRecv, this);
	_threadRecv.detach();
  1. 管理socket,接受消息;
    fd_set server_fd_set; // 管理所有 socket 的集合  
	timeval tv;
	FD_ZERO(&server_fd_set);
		FD_SET(m_sServer, &server_fd_set);  // 将监听 socket 添加到集合里

		tv.tv_sec = 0;
		tv.tv_usec = 0;

		int ret = select(0, &server_fd_set, NULL, NULL, &tv); // 等待监听集合里所有的句柄   

		if (ret < 0) {
			printf("Select error\n");
		}

		// 监听端口进来的连接信息   
		if (FD_ISSET(m_sServer, &server_fd_set)) { // 监听端口有可读数据    
			// 将端口监听进来    
			//struct sockaddr_in c_addr;
			//int len = sizeof(c_addr);
			//SOCKET client_socket = accept(m_sServer, (struct sockaddr*)&c_addr, &len); 
			SOCKADDR_IN addrServer = { 0 };
			int iaddrLen = sizeof(addrServer);
			SOCKET client_socket = accept(m_sServer, (SOCKADDR*)&addrServer, &iaddrLen); 

			if (client_socket != INVALID_SOCKET) {
				char str[INET_ADDRSTRLEN];
				inet_ntop(AF_INET, &addrServer.sin_addr, str, sizeof(str));
				TcpClient _tcp(client_socket, str, true);
				m_TcpClient.push_back(_tcp);
				printf("accept a connection from IP: %s,Port: %d\n", str, htons(addrServer.sin_port));
			}

		}
  1. 直接上代码,TcpServer头文件;
#pragma once
#include <string>
#include <map>
#include <vector>
#include <thread>
#include <mutex>
#include <WS2tcpip.h>
using namespace std;

#include <WinSock2.h>
#pragma comment( lib, "ws2_32.lib" )

struct	TcpClient						
{
	SOCKET			socket;					// SOCKET
	char			addr[INET_ADDRSTRLEN];	// 地址
	bool			bConnect;				// 是否连接

	TcpClient() {
		socket = INVALID_SOCKET;
		bConnect = false;
		ZeroMemory(addr, sizeof(addr));
	}

	TcpClient(SOCKET s, char* _addr, bool c) {
		socket = s;
		strcpy_s(addr, _addr);
		bConnect = c;
	}
};

class TcpServer
{
public:
	TcpServer();
	~TcpServer();

	BOOL InitSocket(int port, int maxlink = 255);

	static void ThreadRecv(TcpServer* pThis);
	void ESThreadRecv();

	void send_msg(PVOID pM);
	void send_msg(PVOID pM, int len);

private:
	SOCKET				m_sServer;                              
	HANDLE				m_hAccepthEvent;
	vector<TcpClient>	m_TcpClient;
	mutex				m_mtx;
};


  1. 继续上代码,TcpServer源文件;
#include "TcpServer.h"
#include <iostream>

TcpServer::TcpServer() : 
	m_sServer(INVALID_SOCKET),
	m_hAccepthEvent(INVALID_HANDLE_VALUE)
{
	DWORD dwCode;
	WSADATA wsaData;

	dwCode = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (dwCode != 0)
	{
		MessageBox(NULL, L"Can't find find a usable WinSock DLL", L"Error", 0);
		return;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		MessageBox(NULL, L"Can't find the socket version required.", L"Error", 0);
		return;
	}
}

TcpServer::~TcpServer()
{
	CloseHandle(m_hAccepthEvent);
	m_hAccepthEvent = INVALID_HANDLE_VALUE;

	Sleep(1000);

	//关闭socket
	::closesocket(m_sServer);
	for (int i = 0; i < m_TcpClient.size(); i++)
	{
		::closesocket(m_TcpClient[i].socket);
	}

	m_TcpClient.clear();

	WSACleanup();
}

BOOL TcpServer::InitSocket(int port,int maxlink)
{
	m_sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_sServer == INVALID_SOCKET)
	{
		std::cout << "Can't create the socket, error code" << WSAGetLastError() << std::endl;
		return FALSE;
	}

	SOCKADDR_IN sockAddr;
	sockAddr.sin_family = AF_INET;
	sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);//ADDR_ANY;
	//sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	sockAddr.sin_port = htons(port);
	int dwCode = ::bind(m_sServer, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
	if (dwCode == SOCKET_ERROR)
	{
		closesocket(m_sServer);
		std::cout << "Can't bind the socket, error code:" << WSAGetLastError() << std::endl;
		return FALSE;
	}

	if (listen(m_sServer, maxlink) == SOCKET_ERROR)
	{
		std::cout << "Can't listen the socket, error code:" << WSAGetLastError() << std::endl;
		return FALSE;
	}

	m_hAccepthEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
	thread _threadRecv(ThreadRecv, this);
	_threadRecv.detach();

	return TRUE;
}

void TcpServer::ThreadRecv(TcpServer* pThis)
{
	pThis->ESThreadRecv();
}

void TcpServer::ESThreadRecv()
{
	fd_set server_fd_set; // 管理所有 socket 的集合  
	timeval tv;
	int retRead;
	char pBuffer[2048];
	while (true)
	{
		DWORD dwCode = WaitForSingleObject(m_hAccepthEvent, INFINITE);

		if (dwCode != WAIT_OBJECT_0)
			break;

		FD_ZERO(&server_fd_set);
		FD_SET(m_sServer, &server_fd_set);  // 将监听 socket 添加到集合里

		tv.tv_sec = 0;
		tv.tv_usec = 0;

		int ret = select(0, &server_fd_set, NULL, NULL, &tv); // 等待监听集合里所有的句柄   

		if (ret < 0) {
			printf("Select error\n");
		}

		// 监听端口进来的连接信息   
		if (FD_ISSET(m_sServer, &server_fd_set)) { // 监听端口有可读数据    
			// 将端口监听进来    
			//struct sockaddr_in c_addr;
			//int len = sizeof(c_addr);
			//SOCKET client_socket = accept(m_sServer, (struct sockaddr*)&c_addr, &len); 
			SOCKADDR_IN addrServer = { 0 };
			int iaddrLen = sizeof(addrServer);
			SOCKET client_socket = accept(m_sServer, (SOCKADDR*)&addrServer, &iaddrLen); // 创建和客户端对应的 socket    

			if (client_socket != INVALID_SOCKET) {
				char str[INET_ADDRSTRLEN];
				inet_ntop(AF_INET, &addrServer.sin_addr, str, sizeof(str));
				TcpClient _tcp(client_socket, str, true);
				m_TcpClient.push_back(_tcp);
				printf("accept a connection from IP: %s,Port: %d\n", str, htons(addrServer.sin_port));
			}

		}

		//查询是否有新的数据
		fd_set readfdr;
		for (int i = m_TcpClient.size() - 1; i >= 0; i--)
		{
			if (i >= (int)(m_TcpClient.size())) break;
			if (!m_TcpClient[i].bConnect) continue;

			FD_ZERO(&readfdr);
			FD_SET(m_TcpClient[i].socket, &readfdr);

			tv.tv_sec = 0;
			tv.tv_usec = 0;
			ret = select(0, &readfdr, NULL, NULL, &tv);

			if ((ret > 0 && FD_ISSET(m_TcpClient[i].socket, &readfdr)))
			{
				ZeroMemory(pBuffer, sizeof(pBuffer));
				retRead = recv(m_TcpClient[i].socket, pBuffer, 2048, 0);
				
				if (retRead <= 0)
				{
					std::lock_guard<std::mutex> lock(m_mtx);
					m_TcpClient[i].bConnect = false;
					closesocket(m_TcpClient[i].socket);
					m_TcpClient.erase(m_TcpClient.begin() + i);
					continue;
				}
				else if (retRead > 0)
				{
					pBuffer[retRead] = '\0';

					std::cout << "recv a message from IP: " << m_TcpClient[i].addr << std::endl 
						<< "content is: " << pBuffer << std::endl;
				}
			}
		}
	}
}

void TcpServer::send_msg(PVOID pM)
{
	char* msg = (char*)pM;
	std::lock_guard<std::mutex> lock(m_mtx);
	for (unsigned int i = 0; i < m_TcpClient.size(); i++)
		::send(m_TcpClient[i].socket, msg, strlen(msg), 0);
}

void TcpServer::send_msg(PVOID pM, int len)
{
	std::lock_guard<std::mutex> lock(m_mtx);
	for (unsigned int i = 0; i < m_TcpClient.size(); i++)
		::send(m_TcpClient[i].socket, (char*)pM, len, 0);
}

结尾

写的还是比较粗糙,应该能用,希望有所帮助吧!## 标题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值