一:创建一个socket
二:创建一个地址
三:将地址与socket绑定
四:对当前socket进行监听
五:等待接受客服端的连接
六:开辟工作线程
下面代码就是 一socket一线程 的逻辑
#include <sys/socket.h>
#include <iostream>
#include <netinet/in.h>
#include <unistd.h> //close(server_fd)
#include <string.h> // memset
#include <arpa/inet.h> // inet_ntoa
#include <thread>
void workthread(int client_fd)
{
if (client_fd != -1)
{
while (true)
{
char readbuff[1024] = {0};
int readlen = recv(client_fd, readbuff, sizeof(readbuff), 0);
std::cout << "rec:" << readbuff << std::endl;
/*do some work*/
send(client_fd,readbuff,readlen,0);
}
}
}
int main(int argc, char **argv)
{
if (argc != 2)
{
std::cout << "application only accepts one parameter" << std::endl;
return -1;
}
std::cout << "input par: " << argv[1] << "," << std::endl;
char *appname = argv[0];
int port = atoi(argv[1]);
//第一步
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
//第二步
sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(sockaddr_in));
serveraddr.sin_port = htons(port); //本地转网络字节序
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = INADDR_ANY; //0.0.0.0 代表监听本机的所有地址
//第三步
//sockaddr_in 与sockaddr的含义差不多,只是sockaddr_in 将端口和ip分开了
if (0 != bind(server_fd, (sockaddr *)&serveraddr, sizeof(sockaddr_in)))
{
return -1;
}
//第四步
if (0 != listen(server_fd, 5)) //5代表同一时刻最大的监听个数
{
return -1;
}
while (true)
{
//第五步
sockaddr_in client_addr;
socklen_t client_addrlen;
int client_fd = accept(server_fd, (sockaddr *)&client_addr, &client_addrlen);
std::cout << "client ip:" << inet_ntoa(client_addr.sin_addr) << std::endl;
std::cout << "client port:" << client_addr.sin_port << std::endl;
//第六步
std::thread thread(workthread, client_fd);
thread.detach();
}
close(server_fd);
return 0;
}
linux下一个线程大概占用4M内存,假设一台服务器的内存为8G,以资源的使用率不超过80%考虑,那么他能支持同时创建的8x1024x0.8 /4 = 1638.4个线程,如果不需要同时处理超过 1638 个客户端连接的时候,以上代码的处理逻辑不会有任何问题,反之则会存在卡死崩溃的问题。
如何解决呢?
首先要明白问题出现的原因,当多个连接同时进入服务器时:
1、卡死主要体现在单线程监听时需要阻塞等待新的连接
解决的方式可以使用一个多路复用的底层实现
2、崩溃主要体现在开辟的线程过多,导致资源消耗过多,
解决方式可以使用一个线程池