怀念夏天 发表于 2023-5-19 09:52:43

Linux网络编程:socket & pthread_create()多线程 实现clients/server通信

一、问题引入

Linux网络编程:socket & fork()多进程 实现clients/server通信 随笔介绍了通过fork()多进程实现了服务器与多客户端通信。但除了多进程能实现之外,多线程也是一种实现方式。
重要的是,多进程和多线程是涉及操作系统层次。随笔不仅要利用pthread_create()实现多线程编程,也要理解线程和进程的区别。
二、解决过程

client 代码无需修改,请参考 Linux网络编程:socket & fork()多进程 实现clients/server通信
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>
#include <unistd.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/syscall.h>

#define IP "10.8.198.227"
#define PORT 8887
#define gettid() syscall(__NR_gettid)
#define PTHREAD_MAX_SIZE 3    // 允许最大客户端连接数

typedef struct PTHREAD_DATA_ST
{
    pthread_t pthread_id;
    int connfd;
    char socket;
    struct sockaddr_in cliaddr;
}PTHREAD_DATA_ST;

//static int g_pthread_num = 3; // 允许最大客户端连接数
static struct PTHREAD_DATA_ST g_pthread_data;

static void pthread_data_index_init(void)
{
    for (int i = 0; i < PTHREAD_MAX_SIZE; i++)
    {
      memset(&g_pthread_data, 0 , sizeof(struct PTHREAD_DATA_ST));
      g_pthread_data.connfd = -1;
    }
}

static int pthread_data_index_find(void)
{
    int i;
    for (i = 0; i < PTHREAD_MAX_SIZE; i++)
    {
      if (g_pthread_data.connfd == -1)
            break;
    }
    return i;
}

static int string_toupper(const char *src, int str_len, char *dst)
{
    int count = 0;
    for (int i = 0; i < str_len; i++)
    {
      dst = toupper(src);
      count++;
    }
    return count;
}

void *pthread_handle(void *arg)
{
    struct PTHREAD_DATA_ST *pthread = (struct PTHREAD_DATA_ST *)arg;
    int connfd = pthread->connfd;
    int recv_len, send_len;
    pid_t tid = gettid();
    char read_buf, write_buf;
    while (1)
    {
      memset(read_buf, 0, sizeof(read_buf));
      memset(write_buf, 0, sizeof(write_buf));
      recv_len = read(connfd, read_buf, sizeof(read_buf));
      if (recv_len <= 0)
      {
            printf("%s close, child %d terminated\n", pthread->socket, tid);
            close(connfd);
            pthread->connfd = -1;
            pthread_exit(NULL);
      }
      printf("%s:%s(%d Byte)\n", pthread->socket, read_buf, recv_len);
      send_len = string_toupper(read_buf, strlen(read_buf), write_buf);
      write(connfd, write_buf, send_len);
      if (strcmp("exit", read_buf) == 0)
      {
            printf("%s exit, child %d terminated\n", pthread->socket, tid);
            close(connfd);
            pthread->connfd = -1;
            pthread_exit(NULL);
      }
    }
}

int main(void)
{
    int listenfd, connfd;
    struct sockaddr_in server_sockaddr;
    struct sockaddr_in client_addr;
    char buf;
    char client_socket;
    socklen_t length;
    int idx;
    int opt = 1;

    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);
    }
    // 设置端口复用
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    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);
    }

    pthread_data_index_init();
    while (1)
    {
      // 接受来自客户端的信息
      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);
            }
      }

      idx = pthread_data_index_find();
      if (idx == PTHREAD_MAX_SIZE)
      {
            printf("client connected upper limit, refused connect\n");
            close(connfd);
            continue;
      }

      memset(&client_socket, 0, sizeof(client_socket));
      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));
      g_pthread_data.connfd = connfd;
      g_pthread_data.cliaddr = client_addr;
      strcpy(g_pthread_data.socket, client_socket);
      pthread_create(&(g_pthread_data.pthread_id), NULL, pthread_handle, &(g_pthread_data));
      pthread_detach(g_pthread_data.pthread_id);
    }
    close(listenfd);
    return EXIT_SUCCESS;
}2-2 编译运行



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Linux网络编程:socket & pthread_create()多线程 实现clients/server通信