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

网络编程--服务器双客户端聊天

写一个服务器和客户端

运行服务器和2个客户端,实现聊天功能 客户端1和客户端2进行聊天,客户端1将聊天数据发送给服务器,服务器将聊天数据转发给客户端2

要求: 服务器使用 select 模型实现 ,客户端1使用 poll 模型实现, 客户端2使用多线程实现

服务器:

#include <head.h>
// 将client存入数组arr中的最后一个位置上,存完之后,arr数组的长度记得自增
void insert_client(int arr[], int client, int *len) {
    arr[*len] = client;
    (*len)++;
}

// 将client从数组arr中移除,移除后记得数组长度-1
void remove_client(int arr[], int client, int *len) {
    int i;
    for (i = 0; i < *len; i++) {
        if (arr[i] == client) {
            break;
        }
    }
    if (i == *len) {
        return;
    }
    for (; i < (*len - 1); i++) {
        arr[i] = arr[i + 1];
    }
    (*len)--;
}

int main(int argc, const char *argv[]) {
    if (argc < 2) {
        printf("请输入端口号\n");
        return 1;
    }
    int port = atoi(argv[1]);

    // 创建服务器套接字
    int server = socket(AF_INET, SOCK_STREAM, 0);

    // 为服务器准备ip和port
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");

    if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind");
        return 1;
    }
    listen(server, 50);

    int client_arr[64] = {0}; // 用来存放所有客户端套接字的数组
    int arr_len = 0;         // 记录数组的长度
    fd_set readfds;         // 创建一个select的监视列表

    // 初始化,只有2个描述符可以初始化,1个是server服务器套接字,1个是标准输入流 0
    FD_ZERO(&readfds);
    FD_SET(server, &readfds);    // 将服务器套接字放入到监视列表中
    FD_SET(STDIN_FILENO, &readfds); // 将标准输入流描述符放入到监视列表中

    while (1) {
        fd_set temp = readfds;
        select(1024, &temp, 0, 0, 0);
        // select是一个阻塞型函数,一旦接触阻塞,就说明有任意个描述符激活了,激活的描述符会写入temp里面
        // 判断一下激活列表temp里面的描述符到底是哪些
        if (FD_ISSET(STDIN_FILENO, &temp)) {
            char buf[1024] = {0};
            scanf("%s", buf);
            printf("键盘输入数据:%s\n", buf);
        }
        if (FD_ISSET(server, &temp)) {
            int client = accept(server, 0, 0);
            printf("有新客户端连接\n");

            // 将新连接的客户端加入到监视列表 readfds里面去 以及 数组 client_arr里面去
            FD_SET(client, &readfds);
            insert_client(client_arr, client, &arr_len);
        }

        // 判断一下各种各样的客户端是否被激活,也就是是否有在temp 里面
        for (int i = 0; i < arr_len; i++) {
            int client = client_arr[i];
            if (FD_ISSET(client, &temp)) {
                char pack[1024] = {0};
                int size = 0;
                int res = read(client, &size, 4);
                if (res == 0) {
                    printf("从客户端断开连接\n");
                    // 从监视列表和客户端数组中移除客户端套接字
                    FD_CLR(client, &readfds);
                    remove_client(client_arr, client, &arr_len);
                    close(client); // 关闭相关的客户端
                    break;
                }
                read(client, (char *)&pack + 4, size - 4);
                // 转发数据给其他客户端
                for (int j = 0; j < arr_len; j++) {
                    if (client_arr[j] != client) {
                        write(client_arr[j], &size, 4);
                        write(client_arr[j], pack, size);
                    }
                }
            }
        }
    }
    return 0;
}

客户端1:

#include <head.h>
// 将client存入数组arr中的最后一个位置上,存完之后,arr数组的长度记得自增
void insert_client(struct pollfd *arr, struct pollfd client, int *len) {
    arr[*len] = client;
    (*len)++;
}

// 将client从数组arr中移除,移除后记得数组长度-1
void remove_client(struct pollfd *arr, int client, int *len) {
    int i;
    for (i = 0; i < *len; i++) {
        if (arr[i].fd == client) {
            break;
        }
    }
    if (i == *len) {
        return;
    }
    for (; i < (*len - 1); i++) {
        arr[i] = arr[i + 1];
    }
    (*len)--;
}

int main(int argc, const char *argv[]) {
    if (argc < 2) {
        printf("请输入端口号\n");
        return 1;
    }
    int port = atoi(argv[2]);

    // 创建客户端套接字
    int client = socket(AF_INET, SOCK_STREAM, 0);

    // 准备 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.126.235");

    if (connect(client, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("connect");
        return 1;
    }

    struct pollfd list[10];
    int list_len = 0;

    struct pollfd client_fd = {client, POLLIN, 0};
    insert_client(list, client_fd, &list_len);

    struct pollfd stdin_fd = {STDIN_FILENO, POLLIN, 0};
    insert_client(list, stdin_fd, &list_len);

    while (1) {
        int res = poll(list, list_len, -1);
        if (res == -1) {
            perror("poll");
            break;
        }

        for (int i = 0; i < list_len; i++) {
            if (list[i].revents & POLLIN) {
                if (list[i].fd == STDIN_FILENO) {
                    char buf[1024] = {0};
                    scanf("%s", buf);
                    int size = strlen(buf);
                    write(client, &size, 4);
                    write(client, buf, size);
                } else if (list[i].fd == client) {
                    int size = 0;
                    read(client, &size, 4);
                    char pack[1024] = {0};
                    read(client, pack, size);
                    printf("收到消息: %s\n", pack);
                }
            }
        }
    }
    close(client);
    return 0;
}

客户端2:

#include <head.h>
void* receive_message(void* arg) {
    int client = *(int*)arg;
    while (1) {
        int size = 0;
        int res = read(client, &size, 4);
        if (res == 0) {
            printf("与服务器断开连接\n");
            break;
        }
        char pack[1024] = {0};
        read(client, pack, size);
        printf("收到消息: %s\n", pack);
    }
    return NULL;
}

int main(int argc, const char *argv[]) {
    if (argc < 2) {
        printf("请输入端口号\n");
        return 1;
    }
    int port = atoi(argv[1]);

    // 创建客户端套接字
    int client = socket(AF_INET, SOCK_STREAM, 0);

    // 准备 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.126.235);

    if (connect(client, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("connect");
        return 1;
    }

    pthread_t thread_id;
    pthread_create(&thread_id, NULL, receive_message, &client);
    pthread_detach(thread_id);

    while (1) {
        char buf[1024] = {0};
        scanf("%s", buf);
        int size = strlen(buf);
        write(client, &size, 4);
        write(client, buf, size);
    }
    close(client);
    return 0;
}

相关文章:

  • 论文阅读:2023 arxiv Multiscale Positive-Unlabeled Detection of AI-Generated Texts
  • 从零构建大语言模型全栈开发指南:第二部分:模型架构设计与实现-2.1.2多头注意力扩展与掩码机制(因果掩码与填充掩码)
  • Qt中通过QLabel实时显示图像
  • 数据分析处理库-Pandas
  • 2.1.项目管理前言
  • 除了setup的表达方法,vue3还有什么表达方法
  • MySQL 处理重复数据:保留一条与两条的实现方案
  • 鸿蒙harmonyOS:笔记 正则表达式
  • Cloudfare内网穿透配置
  • Java设计模式之中介者模式
  • YOLO11改进|全网首发|YOLO11中引入轻量级坐标注意力LCA
  • (UI自动化测试web端)第二篇:元素定位的方法_class定位
  • OpenCV平滑处理:图像去噪与模糊技术详解
  • LeetCode(704):二分查找
  • 【大模型科普】大模型:人工智能的前沿(一文读懂大模型)
  • Canal同步延迟和数据丢失优化方案
  • IBM ECM结合 第三方AI API 来实现文档分析和 RAG
  • 如何从后端实现页面跳转?
  • MLIR中Dialect的抽象层级 简介
  • 算法训练营第二十二天 | 回溯算法(四)
  • 常州市委原常委、组织部部长陈翔调任江苏省民宗委副主任
  • 绿城约13.93亿元竞得西安浐灞国际港港务片区地块,区内土地楼面单价首次冲破万元
  • 首家股份行旗下AIC来了,兴银金融资产投资有限公司获批筹建
  • 世界人形机器人运动会将在北京“双奥场馆”举行
  • 谢晖不再担任中超长春亚泰队主教练:战绩不佳主动请辞
  • 碧桂园服务:拟向杨惠妍全资持有的公司提供10亿元贷款,借款将转借给碧桂园用作保交楼