多进程回声服务器

多进程回声服务器

服务器搭建思路

  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" "端口"

相关推荐
SPC的存折6 小时前
1、Redis数据库基础
linux·运维·服务器·数据库·redis·缓存
爱学习的小囧6 小时前
VMware ESXi 6.7U3v 新版特性、驱动集成教程和资源包、部署教程及高频问答详情
运维·服务器·虚拟化·esxi6.7·esxi蟹卡驱动
小疙瘩7 小时前
只是记录自己发布若依分离系统到linux过程中遇到的问题
linux·运维·服务器
dldw7777 小时前
IE无法正常登录windows2000server的FTP服务器
运维·服务器·网络
我是伪码农8 小时前
外卖餐具智能推荐
linux·服务器·前端
汤愈韬8 小时前
下一代防火墙通用原理
运维·服务器·网络·security
皮皮林5518 小时前
强烈建议大家使用 Linux 做开发?
linux
IMPYLH9 小时前
Linux 的 od 命令
linux·运维·服务器·bash
Kk.080210 小时前
Linux(十一)fork实例练习、文件操作示例及相关面试题目分享
linux·运维·算法
数据雕塑家10 小时前
Linux下大文件切割与合并实战:解决FAT32文件系统传输限制
linux·运维·服务器