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

news2025/3/20 8:42:36

写一个服务器和客户端

运行服务器和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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2318262.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

yum软件包乾坤大挪移(Yum Package Qiankun Great Migration)

yum软件包乾坤大挪移 背景 由于很多的生产环境是无法连接外网的&#xff0c;因此用yum或者dnf命令来安装软件包常常是一个比较麻烦的事情&#xff0c;原因是很多软件的依赖很复杂&#xff0c;如果要一个个下载、拷贝、再安装&#xff0c;这往往是一个非常繁琐冗杂的过程&…

Java:读取中文,read方法

public static void main(String[] args) throws IOException {FileReader fr new FileReader("C:\\aaa\\a.txt");//字符流的底层也是一个字节一个字节读取的&#xff0c;遇到中文就一次读多个&#xff0c;GBK一次读两个&#xff0c;UTF-8一次读三个字节//idea默认U…

英语词性--数词

文章目录 数词概念数词分词基数词序数词 基数与序数词的区别基变序的规律 数词概念 数词&#xff08;Numerals&#xff09; 是英语中用于表示 数量&#xff08;基数&#xff09;或顺序&#xff08;序数&#xff09; 的词类&#xff0c;通常用于描述数字、计数、顺序等。 例如&…

Linux 一步部署DHCP服务

#!/bin/bash #脚本作者和日期 #author: PEI #date: 20250319 #检查root权限 if [ "$USER" ! "root" ]; then echo "错误&#xff1a;非root用户&#xff0c;权限不足&#xff01;" exit 0 fi #防火墙与高级权限 systemctl stop firewa…

如何打造安全稳定的亚马逊采购测评自养号下单系统?

在当今的电商领域&#xff0c;亚马逊作为全球领先的在线购物平台&#xff0c;其商品种类繁多&#xff0c;用户基数庞大&#xff0c;成为了众多商家和消费者的首选。而对于一些需要进行商品测评或市场调研的用户来说&#xff0c;拥有一个稳定、安全的亚马逊账号体系显得尤为重要…

Pytorch中layernorm实现详解

平时我们在编写神经网络时&#xff0c;经常会用到layernorm这个函数来加快网络的收敛速度。那layernorm到底在哪个维度上进行归一化的呢&#xff1f; 一、问题描述 首先借用知乎上的一张图&#xff0c;原文写的也非常好&#xff0c;大家有空可以去阅读一下&#xff0c;链接放…

基于java的ssm+JSP+MYSQL的高校四六级报名管理系统(含LW+PPT+源码+系统演示视频+安装说明)

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

ns3使用入门_基于ns3.44_Part2_配置模块参数的Configuration 和Attributes

前言 事实上ns3的官方手册很全,相关书籍也是有的,官网先贴在这里: ns-3 | a discrete-event network simulator for internet systemsa discrete-event network simulator for internet systemshttps://www.nsnam.org/相关的脚本介绍也都有一些: ns-3.35_wifi-he-networ…

性能测试过程实时监控分析

性能监控 前言一、查看性能测试结果的3大方式1、GUI界面报告插件2、命令行运行 html报告3、后端监听器接入仪表盘 二、influxDB grafana jmeter测试监控大屏1、原理&#xff1a;2、linux环境中influxDB 安装和配置3、jmerer后端监听器连接influxDB4、linux环境总grafana环境搭…

C程序设计(第五版)及其参考解答,附pdf

通过网盘分享的文件&#xff1a;谭浩强C语言设计 链接: https://pan.baidu.com/s/1U927Col0XtWlF9TsFviApg?pwdeddw 提取码: eddw 谭浩强教授的《C程序设计》是C语言学习领域的经典教材&#xff0c;其内容深入浅出&#xff0c;适合不同层次的学习者。 一、教材版本与特点 最…

杰理科技JL703N双模蓝牙芯片—云信

杰理科技JL703N芯片运算能力、接收灵敏度、发射功率、音频性能等指标均处于行业一流水平&#xff0c;能满足多场景的应用需求&#xff0c;具有以下明显优势&#xff1a; 一、高性能双核浮点CPU&#xff0c;算力十足 JL703N芯片搭载了32位高性能双核CPU&#xff0c;主频高达32…

Rust + 时序数据库 TDengine:打造高性能时序数据处理利器

引言&#xff1a;为什么选择 TDengine 与 Rust&#xff1f; TDengine 是一款专为物联网、车联网、工业互联网等时序数据场景优化设计的开源时序数据库&#xff0c;支持高并发写入、高效查询及流式计算&#xff0c;通过“一个数据采集点一张表”与“超级表”的概念显著提升性能…

Nvidia 官方CUDA课程学习笔记

之前心血来潮学习了一下Nvidia CUDA&#xff0c;外行&#xff0c;文章有理解不当的地方&#xff0c;望指正。 主要根据以下Nvidia官方课程学习&#xff1a; https://www.bilibili.com/video/BV1JJ4m1P7xW/?spm_id_from333.337.search-card.all.click&vd_sourcec256dbf86b…

【AI News | 20250319】每日AI进展

AI Repos 1、XianyuAutoAgent 实现了 24 小时自动化值守的 AI 智能客服系统&#xff0c;支持多专家协同决策、智能议价和上下文感知对话&#xff0c;让我们店铺管理更轻松。主要功能&#xff1a; 智能对话引擎&#xff0c;支持上下文感知和专家路由阶梯降价策略&#xff0c;自…

一种基于大规模语言模型LLM的数据分析洞察生成方法

从复杂数据库中提取洞察对数据驱动决策至关重要,但传统手动生成洞察的方式耗时耗力,现有自动化数据分析方法生成的洞察不如人工生成的有洞察力,且存在适用场景受限等问题。下文将介绍一种新的方法,通过生成高层次问题和子问题,并使用SQL查询和LLM总结生成多表数据库中的见…

【npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree】

npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree 当我们拿到一个前端项目的时候&#xff0c;想要把它运行起来&#xff0c;首先是要给它安装依赖&#xff0c;即cd到…

用 pytorch 从零开始创建大语言模型(四):从零开始实现一个用于生成文本的GPT模型

从零开始创建大语言模型&#xff08;Python/pytorch &#xff09;&#xff08;四&#xff09;&#xff1a;从零开始实现一个用于生成文本的GPT模型 4 从零开始实现一个用于生成文本的GPT模型4.1 编写 L L M LLM LLM架构4.2 使用层归一化对激活值进行标准化4.3 使用GELU激活函数…

【新能源汽车“心脏”赋能:三电系统研发、测试与应用匹配的恒压恒流源技术秘籍】

新能源汽车“心脏”赋能&#xff1a;三电系统研发、测试与应用匹配的恒压恒流源技术秘籍 在新能源汽车蓬勃发展的浪潮中&#xff0c;三电系统&#xff08;电池、电机、电控&#xff09;无疑是其核心驱动力。而恒压源与恒流源&#xff0c;作为电源管理的关键要素&#xff0c;在…

目标检测20年(一)

今天看的文献是《Object Detection in 20 Years: A Survey》&#xff0c;非常经典的一篇目标检测文献&#xff0c;希望通过这篇文章学习到目标检测的基础方法并提供一些创新思想。 论文链接&#xff1a;1905.05055 一、摘要 1.1 原文 Object detection, as of one the most…

【MySQL数据库】存储过程与自定义函数(含: SQL变量、分支语句、循环语句 和 游标、异常处理 等内容)

存储过程&#xff1a;一组预编译的SQL语句和流程控制语句&#xff0c;被命名并存储在数据库中。存储过程可以用来封装复杂的数据库操作逻辑&#xff0c;并在需要时进行调用。 类似的操作还有&#xff1a;自定义函数、.sql文件导入。 我们先从熟悉的函数开始说起&#xff1a; …