多进程回声服务器

多进程回声服务器

服务器搭建思路

  1. 处理僵尸进程
  2. 生成服务端
  3. 循环接收客服端连接并开辟新进程处理新连接

以下是服务器实例:

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/tcp.h>
#include<wait.h>
#include<signal.h>
#include<bits/sigaction.h>

//子进程退出信号回调函数
void ChildExitHandle()
{
    //使用waitpid()函数
    int status;
    pid_t pid = waitpid(-1,&status,WNOHANG);//监控任意子进程、状态信息、非阻塞
    if (WIFEXITED(status))
    {
        printf("Child process exited.\n");
        printf("The child process's id is %d\n",WEXITSTATUS(status));
    }
    
}

//错误处理
void error_handing(char *message)
{
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}

int main(int argc, char const *argv[])
{
    //步骤1:处理僵尸进程 -> 使用sigaction()函数
    struct sigaction sig_cfg;
    sig_cfg.sa_handler = ChildExitHandle;
    sigaction(SIGCLD,&sig_cfg,NULL);

    //步骤2.1:生成服务端socket
    int server_sock;
    server_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (server_sock == -1)
    {
        error_handing("socket() error");
    }

    //步骤2.2:绑定监听端口
    struct sockaddr_in server_addr;
    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(atoi("3333"));
    if (bind(server_sock,(struct sockaddr *)&server_addr,sizeof(server_addr)) == -1)
    {
        error_handing("bind() error");
    }

    //步骤2.3:开启端口监听
    if (listen(server_sock,5) == -1)
    {
        error_handing("listen() error");
    }

    //步骤3:循环接收客服端连接并开辟新进程处理新连接
    while (1)
    {
        //步骤3.1:接受连接
        int accept_sock;
        struct sockaddr accept_addr;
        socklen_t len = sizeof(accept_addr);
        accept_sock = accept(server_sock,&accept_addr,&len);//第二个参数存储接入的客户端的地址信息
        if (accept_sock == -1)
        {
            //error_handing("accept() error");
            continue;
        }
        else
        {
            printf("New client connected...\n");
        }

        //步骤3.2:开辟新进程与客户端交互
        pid_t pid = fork();

        if (pid == 0)//子进程
        {
            //子进程同时也复制了父进程的server_sock,要关闭
            close(server_sock);

            //步骤3.3:开始交互
            const int message_size = 10;
            char message[message_size];
            int read_len = 0;
            while((read_len=read(accept_sock, message, message_size))!=0)
                write(accept_sock, message, read_len);

            //步骤3.3:交互完毕,关闭连接
            close(accept_sock);

            //步骤3.4:子进程退出
            exit(2);
        }
        else//父进程
        {
            //父进程同时也包含了子进程的accept_sock,要关闭
            close(accept_sock);
        }
        
    }
    close(server_sock);
    return 0;
}

客户端实例:采用分割I/O的方法,将输入和出输出分割,简化逻辑

c 复制代码
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <sys/socket.h> 

#define BUF_SIZE 30
void error_handling(char *message); 
void read_routline(int sock, char *buf);
void write_routline(int sock, char *buf);

int main(int argc, char *argv[]) 
{
    
    int sock; 
    pid_t pid;
    struct sockaddr_in serv_addr;
    char message[BUF_SIZE]; 
    int str_len,i; 

    
    if (argc != 3)
    {
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1); 
    }

    //步骤1:创建套接字
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1) 
        error_handling("socket() error"); 

    //步骤2:连接端口
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET; // 地址族设置为IPv4
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址
    serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号

    // 发送连接请求
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
        error_handling("connect() error"); 
        else{
            printf("Connected ok\n");
        }
    
    //步骤3:进行通讯
    pid=fork();
    if(pid==0){
        write_routline(sock,message);
    }else{
        read_routline(sock,message);
        close(sock);
        return 0;
    }

}


void error_handling(char* message){
    fputs(message, stderr); 
    fputc('\n', stderr); 
    exit(1); 
}

void write_routline(int sock, char *buf){
    while(1){
        fgets(buf, BUF_SIZE, stdin);
        if(!strcmp(buf,"q\n")||!strcmp(buf,"Q\n")){
            shutdown(sock, SHUT_WR);
            return;
        }
        write(sock, buf, strlen(buf));
    }
}

void read_routline(int sock, char *buf){
    while(1){
        int str_len=read(sock, buf, BUF_SIZE);
        if(str_len==0){
            return;
        }
        buf[str_len]=0;
        printf("Message from server:%s", buf);
    }
}

使用客户端 :./client "服务端ip" "端口"

相关推荐
C++忠实粉丝33 分钟前
Linux系统基础-多线程超详细讲解(5)_单例模式与线程池
linux·运维·服务器·c++·算法·单例模式·职场和发展
zhuyan10841 分钟前
【VMware】使用笔记
服务器
华纳云IDC服务商1 小时前
CentOS系统中查看内网端口映射的多种方法
linux·运维·centos
EasyCVR1 小时前
EHOME视频平台EasyCVR萤石设备视频接入平台视频诊断技术可以识别哪些视频质量问题?
服务器·人工智能·计算机视觉·音视频·1024程序员节
中云DDoS CC防护蔡蔡2 小时前
棋牌游戏防ddos攻击,高防IP好用吗?
运维·服务器·游戏·网络安全·ddos
gengjianchun2 小时前
clickhouse 安装配置
服务器·网络·clickhouse
光芒再现dev2 小时前
CentOS—OpenEulerOS系统联网指南
linux·运维·centos
马里嗷2 小时前
Puppeteer - 掌控浏览器自动化的开源利器
后端·github
蓝莓星冰乐2 小时前
Linux入门(2)
linux·运维·服务器