作为对上一篇文章的相关代码补充点击打开链接
#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;
}