跨进程通信

C语言跨进程AF_LOCAL socket通信的示例,其中server端是一个进程,client端是多个线程,并且使用epoll管理server端,其中client端通过read()接受数据,重复发送10次相同数据,数据中携带线程信息,并且每次发送前都重先建联socket然后发送完之后再断连socket。其中server端会根据client线程数目创建相同数量的线程专门监听该连接,并在线程中发送和接受数据。

server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/un.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>

#define SOCKET_PATH "/tmp/example_socket"
#define BUFFER_SIZE 1024
#define MAX_EVENTS 10
#define MAX_CLIENTS 5

typedef struct {
    long thread_id;
    int sequence_number;
    char message[BUFFER_SIZE];
} Message;

void error_and_exit(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

void *connection_thread(void *arg) {
    int client_socket = *(int *)arg;
    Message buffer;
    int bytes_read;

    // 读取客户端消息
    while ((bytes_read = read(client_socket, &buffer, sizeof(buffer))) > 0) {
        // 输出客户端消息
        printf("Received from thread %ld sequence number %d: %s\n", buffer.thread_id, buffer.sequence_number, buffer.message);

        // 发送响应
        const char *response = "Hello from server";
        if (write(client_socket, response, strlen(response)) < 0) {
            error_and_exit("Write failed");
        }

        // 更新序列号
        buffer.sequence_number++;
    }

    // 检查错误
    if (bytes_read == -1) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // No data available
            printf("No data available\n");
        } else {
            perror("Read failed");
        }
    }

    // 关闭客户端套接字
    close(client_socket);

    // 释放线程参数
    free(arg);

    pthread_exit(NULL);
}

int main() {
    int server_socket, epoll_fd, client_socket;
    struct sockaddr_un addr;
    struct epoll_event events[MAX_EVENTS];
    socklen_t len;
    int num_events;

    // 清理套接字文件
    if (unlink(SOCKET_PATH) == -1 && errno != ENOENT) {
        perror("Failed to remove socket file");
        exit(EXIT_FAILURE);
    }

    // 创建套接字
    if ((server_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
        error_and_exit("Socket creation failed");
    }

    // 清空地址结构
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_LOCAL;
    strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

    // 绑定套接字
    if (bind(server_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        error_and_exit("Bind failed");
    }

    // 监听连接
    if (listen(server_socket, MAX_CLIENTS) < 0) {
        error_and_exit("Listen failed");
    }

    // 创建epoll实例
    if ((epoll_fd = epoll_create1(0)) < 0) {
        error_and_exit("Epoll creation failed");
    }

    // 添加监听套接字到epoll
    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLET;  // 设置为边缘触发
    ev.data.fd = server_socket;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &ev) < 0) {
        error_and_exit("Epoll add failed");
    }

    // 设置为非阻塞模式
    if (fcntl(server_socket, F_SETFL, O_NONBLOCK) < 0) {
        error_and_exit("Non-blocking mode failed");
    }

    int num_client_threads = 0;

    while (1) {
        // 等待事件
        num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (num_events == -1) {
            error_and_exit("Epoll wait failed");
        }

        for (int i = 0; i < num_events; i++) {
            if (events[i].data.fd == server_socket) {
                // 接受连接
                len = sizeof(addr);
                if ((client_socket = accept(server_socket, (struct sockaddr *)&addr, &len)) < 0) {
                    error_and_exit("Accept failed");
                }

                // 设置客户端套接字为非阻塞
                if (fcntl(client_socket, F_SETFL, O_NONBLOCK) < 0) {
                    error_and_exit("Non-blocking mode failed");
                }

                // 读取客户端的第一个消息以获取线程信息
                Message thread_info;
                ssize_t bytes_received = recv(client_socket, &thread_info, sizeof(thread_info), MSG_DONTWAIT);
                if (bytes_received == -1) {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) {
                        // No data available yet
                        continue;
                    } else {
                        perror("Read failed");
                        close(client_socket);
                        continue;
                    }
                }

                // 创建线程处理客户端连接
                int *client_socket_ptr = malloc(sizeof(int));
                *client_socket_ptr = client_socket;
                if (pthread_create(NULL, NULL, connection_thread, client_socket_ptr) != 0) {
                    error_and_exit("Thread creation failed");
                }

                num_client_threads++;  // 跟踪客户端线程数

            } else {
                // 不处理其他事件
                continue;
            }
        }

        // 如果客户端线程数已知并且所有客户端线程都已启动,则退出循环
        if (num_client_threads >= 5) {
            break;
        }
    }

    // 等待所有客户端线程完成
    for (int i = 0; i < num_client_threads; i++) {
        pthread_join(i, NULL);
    }

    close(server_socket);
    close(epoll_fd);
    return 0;
}

client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <pthread.h>
#include <errno.h>

#define SOCKET_PATH "/tmp/example_socket"
#define BUFFER_SIZE 1024
#define NUM_REPETITIONS 10

typedef struct {
    long thread_id;
    int sequence_number;
    char message[BUFFER_SIZE];
} Message;

void error_and_exit(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

void *client_thread(void *arg) {
    int client_socket;
    struct sockaddr_un addr;
    Message buffer;
    long thread_id = (long)arg;

    // 重复建立连接、发送数据、接收响应、然后断开连接
    for (int j = 0; j < NUM_REPETITIONS; j++) {
        // 创建套接字
        if ((client_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
            error_and_exit("Socket creation failed");
        }

        // 清空地址结构
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_LOCAL;
        strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

        // 连接到服务器
        if (connect(client_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
            error_and_exit("Connect failed");
        }

        // 构造消息
        buffer.thread_id = thread_id;
        buffer.sequence_number = j;
        snprintf(buffer.message, sizeof(buffer.message), "Hello from client thread %ld repetition %d", thread_id, j);

        // 向服务器发送消息
        if (write(client_socket, &buffer, sizeof(buffer)) < 0) {
            error_and_exit("Write failed");
        }

        // 读取服务器的响应
        if (read(client_socket, &buffer, sizeof(buffer)) < 0) {
            error_and_exit("Read failed");
        }

        // 输出服务器的响应
        puts(buffer.message);

        // 断开连接
        close(client_socket);
    }

    pthread_exit(NULL);
}

int main() {
    pthread_t threads[5];

    // 启动多个线程
    for (int i = 0; i < 5; i++) {
        if (pthread_create(&threads[i], NULL, client_thread, (void *)i) != 0) {
            error_and_exit("Thread creation failed");
        }
    }

    // 等待所有线程完成
    for (int i = 0; i < 5; i++) {
        if (pthread_join(threads[i], NULL) != 0) {
            error_and_exit("Thread join failed");
        }
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值