多进程并发服务器程序

1、server.c

#include "wrap.h"
#include <ctype.h>
#include <strings.h>
#include <signal.h>
#include <wait.h>

typedef void (*sighandler_t)(int);
void sigchld_handler(int signal)
{
    int wstatus;
    pid_t pid = wait(&wstatus);
    while(pid > 0){
        printf("close pid %d success...\n", pid);
        pid = wait(&wstatus);
    }
}
void response_message(int sfd);
#define SERVER_PORT 3000
int main(){
    int sfd, cfd;
    // 1、socket
    sfd = Socket(AF_INET, SOCK_STREAM, 0);
    // 2、bind
    struct sockaddr_in sockaddr;
    bzero(&sockaddr, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(SERVER_PORT);
    sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    // inet_pton(AF_INET, "10.211.55.6", &sockaddr.sin_addr.s_addr);
    Bind(sfd, &sockaddr, sizeof(sockaddr));
    // 3、listen
    Listen(sfd, 128);
    while(1){
        // 4、开启监听accept
        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        socklen_t length = sizeof(addr);
        cfd = Accept(sfd, &addr, &length);
        char buf[BUFSIZ];
        bzero(buf, BUFSIZ);
        printf("Client connected ip:%s, port:%d\n", inet_ntop(AF_INET, &addr.sin_addr.s_addr, buf, BUFSIZ), ntohs(addr.sin_port));
        pid_t pid = fork();
        if (pid < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){
            // 子进程
            close(sfd);
            break;
        }else{
            // 父进程,注册信号捕获
            sighandler_t handler = signal(SIGCHLD, sigchld_handler);
            if (handler == SIG_ERR){
                perror("signal error");
                exit(1);
            }
            close(cfd);
        }
    }
    // 子进程
    if (pid == 0){
        while(1){
            response_message(cfd);
        }
    }
    return 0;
}
// 客户端消息处理函数
void response_message(int cfd)
{
    char buf[BUFSIZ];
    int i;
    ssize_t n = Readn(cfd, buf, BUFSIZ);
    if (n == 0){
        /*
           struct sockaddr_in c_addr;
           socklen_t length = sizeof(c_addr);
           getsockname(cfd, (struct sockaddr *)&c_addr, &length);
           printf("Client closed ip:%s port:%d\n",
           inet_ntop(AF_INET, &c_addr.sin_addr.s_addr, buf, BUFSIZ),
           ntohs(c_addr.sin_port));
           */
        close(cfd);
        exit(1);
    }else if(n < 0){
        close(cfd);
        perror("read error");
        exit(1);
    }
    for(i = 0; i < n; i++){
        buf[i] = toupper(buf[i]);
    }
    write(cfd, buf, n);
}

wrap.h

#ifndef _WRAP_H_
#define _WRAP_H_
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int Socket(int domain, int type, int protocol);
int Bind(int sockfd, const struct sockaddr_in *addr, socklen_t addrlen);
int Listen(int sockfd, int backlog);
int Accept(int sockfd, struct sockaddr_in *addr, socklen_t *addrlen);
int Connect(int sockfd, const struct sockaddr_in *addr, socklen_t addrlen);
ssize_t Readn(int fd, void *buf, size_t count);

#endif

wrap.c

#include "wrap.h"
#include <errno.h>
void print_err(const char* descript)
{
    perror(descript);
    exit(1);
}

int Socket(int domain, int type, int protocol)
{
    int ret = socket(domain, type, protocol);
    if (ret < 1) print_err("socket error");
    return ret;
}

int Bind(int sockfd, const struct sockaddr_in *addr, socklen_t addrlen)
{
    int ret = bind(sockfd, (struct sockaddr *)addr, addrlen);
    if (ret < 0) print_err("bind error");
    return ret;
}

int Accept(int sockfd, struct sockaddr_in *addr, socklen_t *addrlen)
{
    int sfd;
again:
    sfd = accept(sockfd, (struct sockaddr *)addr, addrlen);
    if (sfd < 0){
        // 阻塞等待,考虑被信号中断的情况
        if ((errno == ECONNABORTED) || (errno == EINTR)){
            goto again;
        }else{
            print_err("accept error");
        }
    }
    return sfd;
}

int Listen(int sockfd, int backlog)
{
    int ret = listen(sockfd, backlog);
    if (ret < 0) print_err("listen error");
    return ret;
}

int Connect(int sockfd, const struct sockaddr_in *addr, socklen_t addrlen)
{
    int ret = connect(sockfd, (struct sockaddr *)addr, addrlen);
    if (ret < 0) print_err("connect error");
    return ret;
}

ssize_t Readn(int fd, void *buf, size_t count)
{
    size_t readed = 0;// 已读数
        size_t remain = count;// 未读数
    while(remain > 0){
        int n = read(fd, buf+readed, remain);
        if (n < 0){    // 异常处理
            if (errno == EINTR){
                n = 0;// 信号中断,继续循环
            }else{
                return n;// 异常情况直接返回
            }
        }else if (n < remain){
            remain -= n;
            break;
        }
        readed += n;
        remain -= n;
    }
    return count - remain;// 返回应读数-未读数
}

运行结果:

parallels@ubuntu:~/Linux/mp-socket$ ./server.out
Client connected ip:10.211.55.2, port:49399
Client connected ip:10.211.55.2, port:49400
close pid 5702 success...
close pid 5745 success...
^C
parallels@ubuntu:~/Linux/mp-socket$

2、client.c

#include "wrap.h"
#include <unistd.h>
#include <string.h>

#define SERVER_PORT 3000
#define SERVER_IP "10.211.55.6"
int main(){
    int sfd = Socket(PF_INET, SOCK_STREAM, 0);

    struct sockaddr_in svr_addr;
    svr_addr.sin_family = PF_INET;
    svr_addr.sin_port = htons(SERVER_PORT);
    // 清0操作
    memset(&svr_addr.sin_addr.s_addr, 0, sizeof(svr_addr.sin_addr.s_addr));
    // 将点分十进制字符串转为字节序
    inet_pton(PF_INET, SERVER_IP, &svr_addr.sin_addr.s_addr);
    // 发起网络连接
    Connect(sfd, &svr_addr, sizeof(svr_addr));
    printf("服务器连接成功!\n");
    char buf[BUFSIZ];
    while(1){
        // 读取输入流
        fgets(buf, sizeof(buf), stdin);
        // 写入套接字
        write(sfd, buf, strlen(buf));
        // 读取服务器响应
        int n = read(sfd, buf, sizeof(buf));
        // 将结果输出到标准输出流
        write(STDOUT_FILENO, buf, n);
    }
    close(sfd);
    return 0;
}

运行结果

yusian@SianMac2:~/Linux/socket$ ./client.out
服务器连接成功!
adf
ADF
aaa
AAA
^C
yusian@SianMac2:~/Linux/socket$

Leave a Reply