当前位置: 首页 > news >正文

网络编程-实现客户端通信

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>

#define MAX_CLIENTS 2      // 最大客户端连接数
#define BUFFER_SIZE 1024   // 消息缓冲区大小

int main(int argc, char *argv[]) {
    /* 参数校验 */
    if (argc != 2) {
        printf("用法: %s <端口号>\n", argv[0]);
        exit(1);
    }

    /* 创建服务器socket */
    // AF_INET: IPv4协议
    // SOCK_STREAM: 流式socket(TCP)
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket创建失败");
        exit(1);
    }

    /* 配置服务器地址 */
    struct sockaddr_in addr = {
        .sin_family = AF_INET,                     // IPv4地址族
        .sin_port = htons(atoi(argv[1])),          // 端口号(主机字节序转网络字节序)
        .sin_addr.s_addr = INADDR_ANY              // 监听所有本地接口
    };

    /* 绑定socket到指定地址 */
    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) {
        perror("绑定失败");
        close(server_fd);
        exit(1);
    }

    /* 开始监听连接请求 */
    // 参数5: 等待连接队列的最大长度
    if (listen(server_fd, 5) < 0) {
        perror("监听失败");
        close(server_fd);
        exit(1);
    }
    printf("服务器已启动,监听端口: %s\n", argv[1]);

    /* 客户端管理数据结构 */
    int client_sockets[MAX_CLIENTS] = {0};  // 客户端socket数组
    char client_ids[MAX_CLIENTS] = {0};     // 客户端标识符('1'或'2')
    fd_set readfds;                         // 文件描述符集合
    int max_fd;                             // 最大文件描述符值

    /* 主事件循环 */
    while(1) {
        // 清空文件描述符集合
        FD_ZERO(&readfds);
        
        // 添加服务器socket到监听集合
        FD_SET(server_fd, &readfds);
        max_fd = server_fd;

        // 添加客户端socket到监听集合
        for(int i = 0; i < MAX_CLIENTS; i++) {
            if(client_sockets[i] > 0) {
                FD_SET(client_sockets[i], &readfds);
                if(client_sockets[i] > max_fd)
                    max_fd = client_sockets[i];
            }
        }

        /* 使用select监听IO事件 */
        // 参数说明:
        // 1. max_fd+1: 最大文件描述符值+1
        // 2. &readfds: 读事件监听集合
        // 3. NULL: 不监听写事件
        // 4. NULL: 不监听异常事件
        // 5. NULL: 无限等待
        int activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);
        if ((activity < 0) && (errno != EINTR)) {
            perror("select错误");
        }

        /* 处理新连接请求 */
        if (FD_ISSET(server_fd, &readfds)) {
            // 接受新连接
            int new_socket = accept(server_fd, NULL, NULL);
            if (new_socket < 0) {
                perror("接受连接失败");
                continue;
            }

            // 将新客户端添加到数组
            int added = 0;
            for(int i = 0; i < MAX_CLIENTS; i++) {
                if(client_sockets[i] == 0) {
                    client_sockets[i] = new_socket;
                    
                    // 接收客户端标识符(阻塞式接收第一个字节)
                    recv(new_socket, &client_ids[i], 1, 0);
                    printf("客户端 %c 已连接\n", client_ids[i]);
                    added = 1;
                    break;
                }
            }
            if (!added) {
                printf("连接已满,拒绝新连接\n");
                close(new_socket);
            }
        }

        /* 处理客户端消息 */
        for(int i = 0; i < MAX_CLIENTS; i++) {
            int sd = client_sockets[i];
            if (sd > 0 && FD_ISSET(sd, &readfds)) {
                char buffer[BUFFER_SIZE] = {0};
                
                // 接收消息
                int valread = read(sd, buffer, BUFFER_SIZE);
                
                /* 处理断开连接 */
                if (valread == 0) {
                    getpeername(sd, (struct sockaddr*)&addr, (socklen_t*)&addr);
                    printf("客户端 %c 断开连接\n", client_ids[i]);
                    close(sd);
                    client_sockets[i] = 0;
                    client_ids[i] = 0;
                }
                /* 转发消息 */
                else {
                    printf("转发来自客户端 %c 的消息\n", client_ids[i]);
                    
                    // 遍历所有客户端进行转发
                    for(int j = 0; j < MAX_CLIENTS; j++) {
                        int dest = client_sockets[j];
                        // 跳过自身和无效socket
                        if (dest > 0 && client_ids[j] != client_ids[i]) {
                            send(dest, buffer, strlen(buffer), 0);
                        }
                    }
                }
            }
        }
    }

    close(server_fd);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <poll.h>

#define BUFFER_SIZE 1024   // 输入缓冲区大小

int main(int argc, char *argv[]) {
    /* 参数校验 */
    if (argc != 3) {
        printf("用法: %s <IP地址> <端口号>\n", argv[0]);
        exit(1);
    }

    /* 创建客户端socket */
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket创建失败");
        exit(1);
    }

    /* 配置服务器地址 */
    struct sockaddr_in serv_addr = {
        .sin_family = AF_INET,
        .sin_port = htons(atoi(argv[2])),   // 端口号转换
        .sin_addr.s_addr = inet_addr(argv[1]) // IP地址转换
    };

    /* 建立连接 */
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("连接失败");
        close(sock);
        exit(1);
    }

    /* 发送客户端标识符 */
    char client_id = '1';
    send(sock, &client_id, 1, 0);
    printf("已连接到服务器\n");

    /* 配置poll监听结构体 */
    struct pollfd fds[2] = {
        {.fd = STDIN_FILENO, .events = POLLIN},  // 监听标准输入
        {.fd = sock,         .events = POLLIN}   // 监听socket输入
    };

    /* 主循环 */
    while(1) {
        // 使用poll等待事件(无限等待)
        int ret = poll(fds, 2, -1);
        if (ret == -1) {
            perror("poll错误");
            break;
        }

        /* 处理键盘输入事件 */
        if (fds[0].revents & POLLIN) {
            char buffer[BUFFER_SIZE];
            // 安全获取输入(自动截断)
            if (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
                // 发送消息到服务器
                send(sock, buffer, strlen(buffer), 0);
            }
        }

        /* 处理服务器消息事件 */
        if (fds[1].revents & POLLIN) {
            char buffer[BUFFER_SIZE] = {0};
            int len = recv(sock, buffer, BUFFER_SIZE, 0);
            if (len > 0) {
                printf("收到消息: %s", buffer);
            } else if (len == 0) {
                printf("服务器断开连接\n");
                break;
            } else {
                perror("接收错误");
                break;
            }
        }
    }

    close(sock);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

#define BUFFER_SIZE 1024

// 全局socket描述符
int sock;
// 互斥锁(保护socket操作)
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

/* 发送线程函数 */
void* send_thread(void* arg) {
    while(1) {
        char buffer[BUFFER_SIZE];
        // 获取用户输入
        if (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
            // 加锁发送
            pthread_mutex_lock(&lock);
            send(sock, buffer, strlen(buffer), 0);
            pthread_mutex_unlock(&lock);
        }
    }
    return NULL;
}

/* 接收线程函数 */
void* recv_thread(void* arg) {
    while(1) {
        char buffer[BUFFER_SIZE] = {0};
        int len;
        
        // 加锁接收
        pthread_mutex_lock(&lock);
        len = recv(sock, buffer, BUFFER_SIZE, 0);
        pthread_mutex_unlock(&lock);
        
        if (len > 0) {
            printf("收到消息: %s", buffer);
        } else if (len == 0) {
            printf("服务器断开连接\n");
            break;
        } else {
            perror("接收错误");
            break;
        }
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    /* 参数校验 */
    if (argc != 3) {
        printf("用法: %s <IP地址> <端口号>\n", argv[0]);
        exit(1);
    }

    /* 创建客户端socket */
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket创建失败");
        exit(1);
    }

    /* 配置服务器地址 */
    struct sockaddr_in serv_addr = {
        .sin_family = AF_INET,
        .sin_port = htons(atoi(argv[2])),
        .sin_addr.s_addr = inet_addr(argv[1])
    };
/* 为服务器准备ip和port(提前)
	struct sockaddr_in addr = {0};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr("192.168.110.223");
*/
    /* 建立连接 */
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("连接失败");
        close(sock);
        exit(1);
    }

    /* 发送客户端标识符 */
    char client_id = '2';
    send(sock, &client_id, 1, 0);
    printf("已连接到服务器\n");

    /* 创建发送和接收线程 */
    pthread_t tid_send, tid_recv;
    pthread_create(&tid_send, NULL, send_thread, NULL);
    pthread_create(&tid_recv, NULL, recv_thread, NULL);

    /* 等待线程结束(实际不会执行到这里) */
    pthread_join(tid_send, NULL);
    pthread_join(tid_recv, NULL);

    close(sock);
    return 0;
}

相关文章:

  • conda相关总结
  • 基于Spring Boot的图书管理系统的设计与实现(LW+源码+讲解)
  • 蓝桥杯真题——洛谷Day13 找规律(修建灌木)、字符串(乘法表)、队列(球票)
  • 如何制作一个自己的网站?
  • Excel 小黑第12套
  • 【华为OD-E卷 - 求符合条件元组个数 100分(python、java、c++、js、c)】
  • Redis高级结构-布隆过滤器
  • 【量化科普】Alpha,阿尔法收益
  • laravel 对 数据库 json 字段的查询方式汇总
  • 在 Offset Explorer 中配置多节点 Kafka 集群的详细指南
  • gralloc usage flags
  • 关于QMetaObject::invokeMethod的作用和用法
  • Rust 生命周期
  • 【深度学习与大模型基础】第7章-特征分解与奇异值分解
  • python鸢尾花
  • 基于java的ssm+JSP+MYSQL的九宫格日志网站(含LW+PPT+源码+系统演示视频+安装说明)
  • 每天一道面试题-两数之和
  • SpatialLM尝鲜版
  • JavaEE的知识记录
  • Python第六章03:列表的常用操作
  • 乌外长:乌方准备无条件停火至少30天
  • 邯郸一酒店办婚宴发生火灾,新郎母亲:饭没吃成酒店还要收费
  • 中国海外发展:今年前4个月销售665.8亿元,花费305亿元拿地
  • 巴基斯坦空袭印度多地空军基地,巴战机进入印领空
  • 国家统计局:4月份居民消费价格同比下降0.1%
  • 欧盟决意与俄罗斯能源彻底决裂之际,美国谋划新生意:进口俄气对欧转售