本篇文章带大家学习Linux网络编程中的高并发服务器。首先我们需要了解什么是高并发服务器,然后是学习如何来编写高并发服务器。
高并发服务器是指能够同时处理大量并发请求的服务器系统。在网络应用中,当多个用户或客户端同时请求服务器时,服务器需要能够高效地处理这些请求,并且保持良好的性能和稳定性。
高并发服务器的设计和实现需要考虑以下几个关键因素:
1.多线程或多进程处理:采用多线程或多进程的方式可以使服务器能够同时处理多个请求。每个线程或进程负责处理一个请求,从而提高服务器的并发处理能力。
2.异步非阻塞I/O:使用异步非阻塞I/O编程模型可以避免在请求处理过程中阻塞线程或进程,充分利用系统资源,提高服务器的并发处理性能。常见的方式是使用事件驱动的编程框架或库,例如Node.js的Event-driven I/O、Nginx的事件驱动模型等。
3.负载均衡:通过在服务器集群中引入负载均衡器,将请求分发到多个服务器节点上,可以进一步提高整个系统的并发处理能力。负载均衡器可以根据一定的策略(如轮询、权重等)将请求分发到不同的服务器,使得每个服务器都能够处理适量的请求。
4.缓存和分布式存储:合理地使用缓存和分布式存储可以降低服务器的负载并提高响应速度。将频繁访问的数据存储在缓存中,减轻对后端存储系统的压力。
5.水平扩展:通过增加服务器的数量来扩展系统的处理能力,例如增加服务器节点或使用云计算服务提供商的弹性伸缩功能。水平扩展可以使系统能够处理更多的并发请求。
TCP通信中其实客户端连接上服务器后,服务器会新创建一个客户端出来与连接的客户端进行通信,而不是直接进行通信。这样的话我们就有思路了,我们需要让服务器一直处于accpet等待连接的状态,等待新的客户端连接上来,当有客户端连接上来后会创建一个新的客户端与之进行通信,那么我们就需要为这个客户端去创建一个线程或者是进程,这样的话就不会影响到服务器的接收新客户端的连接请求了。
客户端连接服务器成功后就使用fork函数创建出子进程和客户端进行通信,父进程使用信号来对子进程进行回收。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*回收子进程*/
void catch_child(int sig)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
{
// 处理子进程的退出状态
}
}
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int client = 0;
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
int len = 0;
char buf[32] = {0};
int r = 0;
pid_t pid;
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1 )
{
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind error\n");
return -1;
}
if( listen(server, 128) == -1 )
{
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while( 1 )
{
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if( client == -1 )
{
if (errno == EINTR)
{
// 信号中断,重新调用accept
client = accept(server, (struct sockaddr*)&caddr, &asize);
}
else
{
perror("accept");
printf("client accept error\n");
return -1;
}
}
pid = fork();//创建子进程与客户端进行通信
if(pid == 0)
{
close(server);
while (1)
{
/*子进程*/
len = read(client, buf, 1024);
if(len == 0)
{
printf("child exit\n");
close(client);
exit(1);//退出子进程
}
write(client, buf, len);
printf("recv_buf : %s len : %d\n", buf, len);
printf("child pid : %d\n", getpid());
}
}
else if(pid > 0)
{
/*父进程*/
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = catch_child;
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
close(client);
}
}
close(server);
return 0;
}
使用多线程的方法比多进程的方法简单一些,这里使用pthread_detach函数将线程进行分离,这样的话就不需要我们手动的去回收线程了,我们要做的就是在线程函数里面和连接上来的客户端进行通信就可以了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void* client_work(void* arg)
{
int client = (int)arg;
int len = 0;
char buf[1024];
while (1)
{
len = read(client, buf, 1024);
if(len == 0)
{
printf("client is close\n");
return NULL;
}
printf("read buf : %s\n", buf);
write(client, buf, len);
}
close(client);
return NULL;
}
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int client = 0;
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
int len = 0;
char buf[32] = {0};
int r = 0;
pid_t pid;
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1 )
{
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind error\n");
return -1;
}
if( listen(server, 128) == -1 )
{
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while( 1 )
{
pthread_t tid;
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if( client == -1 )
{
if (errno == EINTR)
{
// 信号中断,重新调用accept
client = accept(server, (struct sockaddr*)&caddr, &asize);
}
else
{
perror("accept");
printf("client accept error\n");
return -1;
}
}
pthread_create(&tid, NULL, client_work, (void*)client);
pthread_detach(tid);
}
close(server);
return 0;
}
本篇文章就讲解到这里。