Linux网络编程:socket & fork实现clients/server通信

发布时间 2023-05-16 17:28:57作者: eiSouthBoy

一、问题引入

Linux网络编程:socket实现client/server通信 随笔简单介绍了TCP Server服务单客户端的socket通信,但是并未涉及多客户端通信。

对于网络编程肯定涉及到多客户端通信和并发编程 (指在同时有大量的客户链接到同一服务器),故本随笔补充这部分知识。

而且并发并发编程涉及到多进程、多线程,其中 fork()函数是Unix中派生新进程的唯一方法。

二、解决过程

2-1 server 代码

#include <stdlib.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* inet(3) functions */
#include <unistd.h>    /* fork(),read(),write() functions */

#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>

#define IP "10.8.198.227"
#define PORT 8887

static int handle(int connect_fd, const char *socket);
static void sig_chld(int);

int main(void)
{
    int listenfd, connfd;
    struct sockaddr_in server_sockaddr;
    struct sockaddr_in client_addr;
    char buf[1024];
    char client_socket[128];
    socklen_t length;
    int pid;

    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(PORT);
    server_sockaddr.sin_addr.s_addr = inet_addr(IP);
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket error");
        exit(1);
    }
    if (bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) < 0)
    {
        perror("bind error");
        exit(1);
    }
    if (listen(listenfd, 5) < 0)
    {
        perror("listen error");
        exit(1);
    }
    if (signal(SIGCHLD, sig_chld) == SIG_ERR)
    {
        perror("signal error");
        exit(1);
    }
    for (;;)
    {
        // 接受来自客户端的信息
        printf("accept start \n");
        memset(&client_addr, 0, sizeof(client_addr));
        length = sizeof(client_addr);
        if ((connfd = accept(listenfd, (struct sockaddr *)&client_addr, &length)) < 0)
        {
            if (errno == EINTR)
                continue;
            else
            {
                perror("accept error");
                exit(1);
            }
        }
        printf("client addr:%s por:%d\n",
               inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)),
               ntohs(client_addr.sin_port));
        snprintf(client_socket, sizeof(client_socket), "client socket (%s:%d)",
                 inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)),
                 ntohs(client_addr.sin_port));
        pid = fork();
        if (pid == 0) // 子进程
        {
            close(listenfd);
            handle(connfd, client_socket);
            exit(1);
        }
        else if (pid < 0) // error
        {
            close(connfd);
        }
        else // 父进程
        {
            close(connfd);
        }
    }
    return EXIT_SUCCESS;
}

static int handle(int connect_fd, const char *socket)
{
    int read_len;
    char buf[1024];

    for (;;)
    {
        memset(buf, 0, sizeof(buf));
        read_len = read(connect_fd, buf, sizeof(buf));
        if (read_len < 0)
        {
            printf("read error \n");
            close(connect_fd);
            return -1;
        }
        else if (read_len == 0)
        {
            printf("%s close \n", socket);
            close(connect_fd);
            break;
        }
        printf("%s:%s\n", socket, buf);
        if (strcmp("exit", buf) == 0)
        {
            printf("%s close \n", socket);
            close(connect_fd);
            return 0;
        }
    }

    return 0;
}

static void sig_chld(int signo)
{
	pid_t	pid;
	int		stat;

	pid = wait(&stat);
	printf("child %d terminated\n", pid);
	return;
}

2-2 client 代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* inet(3) functions */

#include <unistd.h>

#define IP "10.8.198.227"
#define PORT 8887

int handle(int fd);

int main(void)
{
    int sockfd;
    char buf[1024];
    struct sockaddr_in server_addr;
    char server_socket[128];

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr(IP);
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
    {
        printf("connect error \n");
        return -1;
    }
    snprintf(server_socket, sizeof(server_socket), "server socket (%s:%d)",
             inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),
             ntohs(server_addr.sin_port));
    printf("%s\n", server_socket);
    handle(sockfd);
    close(sockfd);

    return EXIT_SUCCESS;
}

int handle(int connect_fd)
{
    char send_buf[1024], recv_buf[1024];

    for (;;)
    {
        memset(send_buf, 0, sizeof(send_buf));
        memset(recv_buf, 0, sizeof(recv_buf));
        while (fgets(send_buf, sizeof(send_buf), stdin) != NULL)
        {
            if (send_buf[strlen(send_buf) - 1] == '\n')
                send_buf[strlen(send_buf) - 1] = '\0';
            write(connect_fd, send_buf, strlen(send_buf));
            if (strcmp("exit", send_buf) == 0)
            {
                return 0;
            }
        }
    }

    return 0;
}

2-3 运行测试

1、client 1 连接 server

2、client 2 连接 server

3、多客户端与服务器通信

三、反思总结

四、参考引用