Linux网络编程(高并发服务器)

简介: Linux网络编程(高并发服务器)

前言

本篇文章带大家学习Linux网络编程中的高并发服务器。首先我们需要了解什么是高并发服务器,然后是学习如何来编写高并发服务器。

一、什么是高并发服务器

高并发服务器是指能够同时处理大量并发请求的服务器系统。在网络应用中,当多个用户或客户端同时请求服务器时,服务器需要能够高效地处理这些请求,并且保持良好的性能和稳定性。

高并发服务器的设计和实现需要考虑以下几个关键因素:

1.多线程或多进程处理:采用多线程或多进程的方式可以使服务器能够同时处理多个请求。每个线程或进程负责处理一个请求,从而提高服务器的并发处理能力。

2.异步非阻塞I/O:使用异步非阻塞I/O编程模型可以避免在请求处理过程中阻塞线程或进程,充分利用系统资源,提高服务器的并发处理性能。常见的方式是使用事件驱动的编程框架或库,例如Node.js的Event-driven I/O、Nginx的事件驱动模型等。

3.负载均衡:通过在服务器集群中引入负载均衡器,将请求分发到多个服务器节点上,可以进一步提高整个系统的并发处理能力。负载均衡器可以根据一定的策略(如轮询、权重等)将请求分发到不同的服务器,使得每个服务器都能够处理适量的请求。

4.缓存和分布式存储:合理地使用缓存和分布式存储可以降低服务器的负载并提高响应速度。将频繁访问的数据存储在缓存中,减轻对后端存储系统的压力。

5.水平扩展:通过增加服务器的数量来扩展系统的处理能力,例如增加服务器节点或使用云计算服务提供商的弹性伸缩功能。水平扩展可以使系统能够处理更多的并发请求。

二、使用多线程和多进程实现高并发服务器的思路

TCP通信中其实客户端连接上服务器后,服务器会新创建一个客户端出来与连接的客户端进行通信,而不是直接进行通信。这样的话我们就有思路了,我们需要让服务器一直处于accpet等待连接的状态,等待新的客户端连接上来,当有客户端连接上来后会创建一个新的客户端与之进行通信,那么我们就需要为这个客户端去创建一个线程或者是进程,这样的话就不会影响到服务器的接收新客户端的连接请求了。

三、多进程服务器代码编写

客户端连接服务器成功后就使用fork函数创建出子进程和客户端进行通信,父进程使用信号来对子进程进行回收。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
/*回收子进程*/
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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
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;
}

总结

本篇文章就讲解到这里。


相关文章
|
1月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
74 2
|
2月前
|
机器学习/深度学习 人工智能 运维
企业内训|LLM大模型在服务器和IT网络运维中的应用-某日企IT运维部门
本课程是为某在华日资企业集团的IT运维部门专门定制开发的企业培训课程,本课程旨在深入探讨大型语言模型(LLM)在服务器及IT网络运维中的应用,结合当前技术趋势与行业需求,帮助学员掌握LLM如何为运维工作赋能。通过系统的理论讲解与实践操作,学员将了解LLM的基本知识、模型架构及其在实际运维场景中的应用,如日志分析、故障诊断、网络安全与性能优化等。
92 2
|
4天前
|
缓存 负载均衡 监控
HTTP代理服务器在网络安全中的重要性
随着科技和互联网的发展,HTTP代理IP中的代理服务器在企业业务中扮演重要角色。其主要作用包括:保护用户信息、访问控制、缓存内容、负载均衡、日志记录和协议转换,从而在网络管理、性能优化和安全性方面发挥关键作用。
19 2
|
1月前
|
域名解析 网络协议 安全
|
2月前
|
运维 监控 网络协议
|
2月前
|
存储 安全 数据可视化
提升网络安全防御有效性,服务器DDoS防御软件解读
提升网络安全防御有效性,服务器DDoS防御软件解读
56 1
提升网络安全防御有效性,服务器DDoS防御软件解读
|
2月前
|
网络协议 Java 应用服务中间件
深入浅出Tomcat网络通信的高并发处理机制
【10月更文挑战第3天】本文详细解析了Tomcat在处理高并发网络请求时的机制,重点关注了其三种不同的IO模型:NioEndPoint、Nio2EndPoint 和 AprEndPoint。NioEndPoint 采用多路复用模型,通过 Acceptor 接收连接、Poller 监听事件及 Executor 处理请求;Nio2EndPoint 则使用 AIO 异步模型,通过回调函数处理连接和数据就绪事件;AprEndPoint 通过 JNI 调用本地库实现高性能,但已在 Tomcat 10 中弃用
深入浅出Tomcat网络通信的高并发处理机制
|
1月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
654 2
|
1月前
|
存储 Ubuntu Linux
2024全网最全面及最新且最为详细的网络安全技巧 (三) 之 linux提权各类技巧 上集
在本节实验中,我们学习了 Linux 系统登录认证的过程,文件的意义,并通过做实验的方式对 Linux 系统 passwd 文件提权方法有了深入的理解。祝你在接下来的技巧课程中学习愉快,学有所获~和文件是 Linux 系统登录认证的关键文件,如果系统运维人员对shadow或shadow文件的内容或权限配置有误,则可以被利用来进行系统提权。上一章中,我们已经学习了文件的提权方法, 在本章节中,我们将学习如何利用来完成系统提权。在本节实验中,我们学习了。
|
2月前
|
Ubuntu Linux 虚拟化
Linux虚拟机网络配置
【10月更文挑战第25天】在 Linux 虚拟机中,网络配置是实现虚拟机与外部网络通信的关键步骤。本文介绍了四种常见的网络配置方式:桥接模式、NAT 模式、仅主机模式和自定义网络模式,每种模式都详细说明了其原理和配置步骤。通过这些配置,用户可以根据实际需求选择合适的网络模式,确保虚拟机能够顺利地进行网络通信。
111 1