一个简易TCP服务器的多种实现

作为对上一篇文章的相关代码补充点击打开链接

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

#include <MSTCPiP.h>

//#define BOOST_ALL_DYN_LINK			//使用动态库链接

#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

#include <boost/smart_ptr.hpp>

#pragma comment(lib,"WS2_32.lib")

void handle_msg(SOCKET con_socket)
{
	if (con_socket == INVALID_SOCKET)
	{
		return;
	}
	char server_text[] = "TCP server demo!";
	char szMessage[1024] = {0};
	send(con_socket, server_text, strlen(server_text), 0);
	int errorcode;
	while(true)
	{
		int ret_size = recv(con_socket, szMessage, 1024, 0);
		if ( (ret_size == 0) || (ret_size == SOCKET_ERROR) )
		{
			errorcode = WSAGetLastError();
			if (errorcode == WSAEWOULDBLOCK)
			{
				std::cout << "等待接收数据" << std::endl;
				Sleep(1000);
				continue;
			}
			else
			{
				printf("Client has closed the connection\n"); 
				return;
			}
			
		}
		else
		{
			szMessage[ret_size] = '\0';
			std::cout << boost::this_thread::get_id() << "接收到数据:" << szMessage <<std::endl;
		}
		send(con_socket, server_text, strlen(server_text), 0);
	}
}

//同步服务器,利用winsock2和ws2_32.lib,利用boost::threadgroup(线程池)实现较为高效的处理,阻塞模式,主线程负责接收客户端的连接请求,子线程负责socket业务通信
int SyncSocketSvr() 
{
	WSADATA     wsaData;
	//初始化socket环境
	if(WSAStartup(0x0202, &wsaData) != 0)
	{
		std::cout << "初始化socket lib环境失败" << std::endl;
		return -1;
	}

	//创建socket,默认是阻塞的
	SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listen_sock == INVALID_SOCKET)
	{
		std::cout << "创建socket失败" << std::endl;
		return -1;
	}
	//将socket设置为非阻塞模式
	int iMode = 1; //0:阻塞
	if (ioctlsocket(listen_sock, FIONBIO, (u_long FAR*) &iMode) == SOCKET_ERROR)//非阻塞设置
	{
		std::cout << "set asyn I/O失败!" << std::endl;
		return -1;
	}
	

	//初始化服务器的IP地址
	sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(sockaddr_in));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(2000);

	//绑定IP到socket上
	if(bind(listen_sock, (LPSOCKADDR)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
	{
		std::cout << "bind failed!" << std::endl;
		return -1;
	}

	//开启监听
	if (listen(listen_sock, 3) == SOCKET_ERROR)
	{
		std::cout << "listen failed" << std::endl;
	}

	//循环接收客户端的连接请求
	sockaddr_in remote_addr;
	memset(&remote_addr, 0, sizeof(sockaddr_in));
	int addr_len = sizeof(sockaddr_in);
	SOCKET connect_sock;
	char server_text[] = "TCP server demo!";
	boost::thread_group tg;
	int errorcode;
	fd_set listen_set;
	timeval time_out;
	while(true)
	{
		FD_ZERO(&listen_set);//初始化set集合
		FD_SET(listen_sock, &listen_set);//设置set集合
		time_out.tv_sec = 1;
		time_out.tv_usec = 0;
		int ret = select(0, &listen_set, NULL, NULL, &time_out);
		if (ret <= 0)
		{
			if (ret < 0)
			{
				std::cout << "监听时select返回错误:" << WSAGetLastError() << std::endl;
			}
			else
				continue;//超时
		}
		if (FD_ISSET(listen_sock, &listen_set))//listen_sock是不是有可读数据
		{
			connect_sock = accept(listen_sock, (LPSOCKADDR)&remote_addr, &addr_len);
			if (connect_sock == INVALID_SOCKET)
			{
				errorcode = WSAGetLastError();
				if (errorcode == WSAEWOULDBLOCK)//表示没有客户端发起连接,继续循环  
				{
					Sleep(100);
					continue;
				}
				else
				{
					std::cout << "connect failed!" << std::endl;
					return -1;
				}

			}
			//将socket设置为非阻塞模式
			int con_Mode = 1; //0:阻塞
			if (ioctlsocket(connect_sock, FIONBIO, (u_long FAR*) &con_Mode) == SOCKET_ERROR)//非阻塞设置
			{
				std::cout << "set asyn I/O失败!" << std::endl;
				return -1;
			}
			std::cout << "接收到一个连接请求,IP:" << inet_ntoa(remote_addr.sin_addr) << std::endl;
			//主流做法:创建一个子线程,绑定这个socket,负责此socket上的所有通信,或者采用线程池

			tg.create_thread(boost::bind(handle_msg, connect_sock));
		}
	}
	tg.join_all();
	//关闭连接,延时5秒直接关闭,原来的closesocket后,socket处于time_wait状态,不能立即复用,下面的方法是等待5秒后,直接释放可以复用
	//SO_LINGER将决定系统如何处理残存在套接字发送队列中的数据
	struct linger
	{
		u_short l_onoff;
		u_short l_linger;
	};
	linger m_linger;
	m_linger.l_onoff = 1;
	m_linger.l_linger = 5;//若为0,直接丢弃
	setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (const char*)&m_linger, sizeof(m_linger));
	//setsockopt(listen_sock, SOL_SOCKET, SO_DONTLINGER, (const char*)&m_linger, sizeof(m_linger));
	closesocket(listen_sock);
	WSACleanup();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值