Linux网络编程---多进/线程并发服务器

一、多进程并发服务器

实现一个服务器可以连接多个客户端,每当accept函数等待到客户端进行连接时 就创建一个子进程

思路分析:

核心思路:让accept循环阻塞等待客户端,每当有客户端连接时就fork子进程,让子进程去和客户端进行通信,父进程用于监听并使用信号捕捉回收子进程(子进程关闭用于监听的套接字lfd,父进程关闭用于通信的cfd)

服务端server.c代码如下:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<strings.h>
#include<errno.h>
#include<pthread.h>
#include<signal.h>
#include<sys/wait.h>

#define SERV_PORT 9527

void sys_err(const char *str)
{
        perror(str);
        exit(1);
}

void catch_child(int signum)
{
        while(waitpid(0,NULL,WNOHANG) > 0);
        return ;
}

int main(int argc,char *argv[])
{
        int lfd = 0,cfd = 0;
        pid_t pid;
        int ret;
        char buf[BUFSIZ],client_IP[1024];//4096

        struct sockaddr_in serv_addr,clit_addr;
        socklen_t clit_addr_len;

        bzero(&serv_addr,sizeof(serv_addr));//将地址结构清零
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT);
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        lfd = socket(AF_INET,SOCK_STREAM,0);
        if (lfd == -1)
        {
                sys_err("socket errno");
        }

        bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

        listen(lfd,128);

        clit_addr_len = sizeof(clit_addr);

        while(1)
        {
                cfd = accept(lfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
                if (cfd == -1)
                {
                        sys_err("accept error");         
                }
                printf("client ip is:%s port:%d\n",inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),ntohs(clit_addr.sin_port));

                pid = fork();
                if (pid < 0)
                {
                        sys_err("fork error");
                }
                else if (pid == 0)
                {
                        close(lfd);
                        break;
                }
                else
                {
                        struct sigaction act;

                        act.sa_handler = catch_child;
                        sigemptyset(&(act.sa_mask));
                        act.sa_flags = 0;

                        ret = sigaction(SIGCHLD,&act,NULL);
                        if (ret != 0)
                        {
                                sys_err("sigaction error");
                        }
                        close(cfd);
                        continue;
                }
        }

        if (pid == 0)
        {
                for(;;)
                {
                        ret = read(cfd,buf,sizeof(buf));
                        if (ret == 0)
                        {
                                close(cfd);
                                exit(1);
                        }

                        for (int i = 0;i<ret;i++)
                        {
                                buf[i] = toupper(buf[i]);
                        }

                        write(cfd,buf,ret);
                        write(STDOUT_FILENO,buf,ret);
                }
        }
        
        return 0;
}

一个服务端三个客户端执行多进程服务器并发输出如下:

补充:

二、多线程并发服务器:

思路分析:

核心思路:设置监听循环由主线程阻塞等待客户端连接,每当接受到新的客户端连接时,主线程会创建一个新的子线程来处理与该客户端的通信,而主线程继续执行监听循环等待其他客户端(主线程专注于接受新连接,子线程专注于处理客户端请求)

服务端server.c代码如下:

cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

struct s_info {       //定义一个结构体, 将地址结构跟cfd捆绑
    struct sockaddr_in cliaddr;
    int connfd;
};

void *do_work(void *arg)
{
    int n,i;
    struct s_info *ts = (struct s_info*)arg;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];      //#define INET_ADDRSTRLEN 16  可用"[+d"查看

    while (1) {
        n = Read(ts->connfd, buf, MAXLINE);  //读客户端
        if (n == 0) {
            printf("the client %d closed...\n", ts->connfd);
            break;                           //跳出循环,关闭cfd
        }
        printf("received from %s at PORT %d\n",
                inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
                ntohs((*ts).cliaddr.sin_port));  //打印客户端信息(IP/PORT)

        for (i = 0; i < n; i++) 
            buf[i] = toupper(buf[i]);            //小写-->大写

        Write(STDOUT_FILENO, buf, n);           //写出至屏幕
        Write(ts->connfd, buf, n);              //回写给客户端
    }
    Close(ts->connfd);

    return (void *)0;            //pthread_exit(0);
}

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    pthread_t tid;

    struct s_info ts[256];      //创建结构体数组.
    int i = 0;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);        //创建一个socket, 得到lfd

    bzero(&servaddr, sizeof(servaddr));               //地址结构清零
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);     //指定本地任意IP
    servaddr.sin_port = htons(SERV_PORT);             //指定端口号 

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));   //绑定

    Listen(listenfd, 128);             //设置同一时刻链接服务器上限数

    printf("Accepting client connect ...\n");

    while (1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //阻塞监听客户端链接请求
        ts[i].cliaddr = cliaddr;    //把文件描述符和客户端地地址结构组织到一个结构体变量中
        ts[i].connfd = connfd;

        pthread_create(&tid, NULL, do_work, (void*)&ts[i]);//表结构体变量作为参数传递到子线程
        pthread_detach(tid);                //子线程分离,防止僵线程产生.
        i++;
    }

    return 0;
}

执行输出如下:

相关推荐
枫叶红花11 分钟前
【Linux系统编程】:信号(2)——信号的产生
linux·运维·服务器
yaosheng_VALVE19 分钟前
探究全金属硬密封蝶阀的奥秘-耀圣控制
运维·eclipse·自动化·pyqt·1024程序员节
dami_king26 分钟前
SSH特性|组成|SSH是什么?
运维·ssh·1024程序员节
启明真纳32 分钟前
elasticache备份
运维·elasticsearch·云原生·kubernetes
苹果醋335 分钟前
SpringBoot快速入门
java·运维·spring boot·mysql·nginx
TsengOnce1 小时前
Docker 安装 禅道-21.2版本-外部数据库模式
运维·docker·容器
永卿0012 小时前
nginx学习总结(不包含安装过程)
运维·nginx·负载均衡
Zmxcl-0072 小时前
IIS解析漏洞
服务器·数据库·microsoft
Stark、2 小时前
【Linux】文件IO--fcntl/lseek/阻塞与非阻塞/文件偏移
linux·运维·服务器·c语言·后端
人类群星闪耀时2 小时前
大模型技术优化负载均衡:AI驱动的智能化运维
运维·人工智能·负载均衡