Linux C小项目 —— 聊天室

2023-05-16




多线程的聊天室

服务器端:

实现多用户群体聊天功能(人数上限可设置);

每个用户所发送的消息,其他已连接服务器的用户均可以收到;

用户输入”bye”退出,服务器输入quit”退出。

 

服务端总共有三个函数

主函数main:服务器端的初始化,接受连接;

消息处理函数rcv_snd:接收某一用户的消息,简单处理后发送给其他所有用户;

退出函数quit:当输入quit时,服务器程序终止。

这三个函数分别属于三个线程(准确说是大于等于三个)。Main函数作为主线程,并创建一个退出函数所在的线程,以及一个每次连接新用户时创建一个对此连接的消息进行处理的线程。

 

客户端:

建立与服务器的连接,发送和接收消息

 

客户端总共有两个函数

主函数main:初始化客户端套接字,与服务器建立连接,接收消息并在本地显示

发送函数snd:从控制台读入消息,发送到服务器

这两个函数分别在两个线程上运行,一个是主函数所在的线程,另一个是主函数创建的发送函数所在的线程。

 

 

附:

宏定义调试打印开关

#define DEBUG_PRINT 1

#ifdef DEBUG_PRINT

#define DEBUG(format, ...) printf(“FILE: “__FILE__”, LINE: %d: “format”\n”, __LINE__, ##__VA_ARGS__)

#else

#define DEBUG(format, ...)

#endif

 

创建线程 gcc编译时需-lpthread

Phtread_t thread;  Pthread_creat(&thread, NULL, (void *)quit, NULL);

Pthread_creat(malloc(sizeof(pthread_t)), NULL, (void *)rcv_snd, (void *)i);

 

系统时间 #include <time.h>

Time_t ticks = time(NULL); printf(“%s\n”, ctime(&ticks));

 

服务端

Struct sockaddr_in serv_addr;

Bzero(&serv_addr, sizeof(serv_addr));

Serv_addr.sin_family = AF_INET;

Serv_addr.sin_port = htons(PORT);

Serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

 

Listenfd = socket(AF_INET, SOCK_STREAM, 0);

Bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

Listen(listenfd, 10);

Int len = sizeof(cli_addr);

Connfd[i] = accept(listenfd, (struct sockaddr *)&cli_addr, &len);

 

客户端

Struct sockaddr_in serv_addr;

Bzero(&serv_addr, sizeof(serv_addr));

Serv_addr.sin_family = AF_INET;

Serv_addr.sin_port = htons(PORT);

Inet_pton(IP, &serv_addr.sin_addr);

 

Sockfd = socket(AF_INET, SOCK_STREAM, 0);

Connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

 源代码之 server.c

  1 #include <stdio.h>
  2 #include <stdlib.h>     // exit
  3 #include <string.h>
  4 #include <unistd.h>     // bind listen
  5 #include <time.h>       // time(NULL)   ctime(&ticks)
  6 #include <netinet/in.h>
  7 #include <arpa/inet.h>  // 必须包含,用于inet_ntop
  8 
  9 #define PORT 8000
 10 #define MAXMEM 10 
 11 #define BUFFSIZE 128
 12 
 13 //#define DEBUG_PRINT 1         // 宏定义 调试开关
 14 #ifdef DEBUG_PRINT
 15 #define DEBUG(format, ...) printf("FILE: "__FILE__", LINE: %d: "format"\n", __LINE__, ##__VA_ARGS__)
 16 #else
 17 #define DEBUG(format, ...)
 18 #endif
 19 
 20 int listenfd, connfd[MAXMEM];
 21 
 22 void quit();
 23 void rcv_snd(int p);
 24 
 25 int main()
 26 {
 27         struct sockaddr_in serv_addr, cli_addr;
 28 //      int len = sizeof(cli_addr), i;
 29         int i;
 30         time_t ticks;
 31         pthread_t thread;
 32         char buff[BUFFSIZE];
 33 
 34         printf("running...\n(Prompt: enter command ""quit"" to exit server)\n");
 35         DEBUG("=== initialize...");     // 初始化填充服务端地址结构
 36         bzero(&serv_addr, sizeof(struct sockaddr_in));
 37         serv_addr.sin_family = AF_INET;
 38         serv_addr.sin_port = htons(PORT);
 39         serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 40 
 41         DEBUG("=== socket..."); // socket 创建服务器端的监听套接字
 42         listenfd = socket(AF_INET, SOCK_STREAM, 0);
 43         if(listenfd < 0)
 44         {
 45                 perror("fail to socket");
 46                 exit(-1);
 47         }
 48 
 49         DEBUG("=== bind...");   // bind 将套接字与填充好的地址结构进行绑定
 50         if(bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
 51         {
 52                 perror("fail to bind");
 53                 exit(-2);
 54         }
 55 
 56         DEBUG("=== listening..."); // listen 将主动连接套接字变为被动倾听套接字
 57         listen(listenfd, MAXMEM);
 58 
 59         /* === 创建一个线程,对服务器程序进行管理,调用quit函数 === */
 60         pthread_create(&thread, NULL, (void *)(quit), NULL);
 61 
 62         // 将套接字描述符数组初始化为-1,表示空闲
 63         for(i=0; i<MAXMEM; i++)
 64                 connfd[i] = -1;
 65 
 66         while(1)
 67         {
 68                 int len;// = sizeof(cli_addr);
 69                 for(i=0; i<MAXMEM; i++)
 70                 {
 71                         if(connfd[i] == -1)
 72                                 break;
 73                 }
 74                 // accept 从listen接受的连接队列中取得一个连接
 75                 connfd[i] = accept(listenfd, (struct sockaddr *)&cli_addr, &len);
 76                 if(connfd[i] < 0)
 77                 {
 78                         perror("fail to accept");
 79                 //      continue;       // 此句可以不用,accept会阻塞等待
 80                 }
 81                 ticks = time(NULL);
 82                 //sprintf(buff, "%.24s\r\n", ctime(&ticks));
 83                 printf("%.24s\n\tconnect from: %s, port %d\n",
 84                                 ctime(&ticks), inet_ntop(AF_INET, &(cli_addr.sin_addr), buff, BUFFSIZE),
 85                                 ntohs(cli_addr.sin_port));      // 注意 inet_ntop的使用,#include <arpa/inet.h>
 86 
 87                 /* === 针对当前套接字创建一个线程,对当前套接字的消息进行处理 === */
 88                 pthread_create(malloc(sizeof(pthread_t)), NULL, (void *)(&rcv_snd), (void *)i);
 89         }
 90         return 0;
 91 }
 92 
 93 void quit()
 94 {
 95         char msg[10];
 96         while(1)
 97         {
 98                 scanf("%s", msg);       // scanf 不同于fgets, 它不会读入最后输入的换行符
 99                 if(strcmp(msg, "quit") == 0)
100                 {
101                         printf("Byebye... \n");
102                         close(listenfd);
103                         exit(0);
104                 }
105         }
106 }
107 
108 void rcv_snd(int n)
109 {
110         int len, i;
111         char name[32], mytime[32], buf[BUFFSIZE];
112         time_t ticks;
113         int ret;
114 
115         // 获取此线程对应的套接字用户的名字
116         write(connfd[n], "your name: ", strlen("your name: "));
117         len = read(connfd[n], name, 32);
118         if(len > 0)
119                 name[len-1] = '\0';     // 去除换行符
120         strcpy(buf, name);
121         strcat(buf, "\tjoin in\n\0");
122         // 把当前用户的加入 告知所有用户
123         for(i=0; i<MAXMEM; i++)
124         {
125                 if(connfd[i] != -1)
126                         write(connfd[i], buf, strlen(buf));
127         }
128 
129         while(1)
130         {
131                 char temp[BUFFSIZE];
132                 if((len=read(connfd[n], temp, BUFFSIZE)) > 0)
133                 {
134                         temp[len-1] = '\0';
135                         // 当用户输入bye时,当前用户退出
136                         if(strcmp(temp, "bye") == 0)
137                         {
138                                 close(connfd[n]);
139                                 connfd[n] = -1;
140                                 pthread_exit(&ret);
141                         }
142                         ticks = time(NULL);
143                         sprintf(mytime, "%.24s\r\n", ctime(&ticks));
144                         //write(connfd[n], mytime, strlen(mytime));
145                         strcpy(buf, name);
146                         strcat(buf, "\t");
147                         strcat(buf, mytime);
148                         strcat(buf, "\r\t");
149                         strcat(buf, temp);
150                         strcat(buf, "\n");
151 
152                         for(i=0; i<MAXMEM; i++)
153                         {
154                                 if(connfd[i] != -1)
155                                         write(connfd[i], buf, strlen(buf));
156                         }
157                 }
158         }
159 
160 }
源代码之 client.c

  1 /*
  2  * FILE: client.c
  3  * DATE: 20180206
  4  * ==============
  5  */
  6 
  7 #include <stdio.h>
  8 #include <stdlib.h>
  9 #include <string.h>
 10 
 11 #include <netinet/in.h>
 12 
 13 #define BUFFSIZE 128
 14 #define HOST_IP "192.168.159.3"
 15 #define PORT 8000
 16 
 17 int sockfd;
 18 
 19 void snd();
 20 
 21 int main()
 22 {
 23         pthread_t thread;       // pthread_t 线程,gcc编译时需加上-lpthread
 24         struct sockaddr_in serv_addr;   // struct sockaddr_in
 25         char buf[BUFFSIZE];
 26         // 初始化服务端地址结构
 27         bzero(&serv_addr, sizeof(struct sockaddr_in));  // bzero 清零
 28         serv_addr.sin_family = AF_INET;         // sin_family   AF_INET
 29         serv_addr.sin_port = htons(PORT);       // sin_port     htons(PORT)
 30         inet_pton(HOST_IP, &serv_addr.sin_addr);        // inet_pton
 31         // 创建客户端套接字
 32         sockfd = socket(AF_INET, SOCK_STREAM, 0);       // socket 创建套接字
 33         if(sockfd < 0)
 34         {
 35                 perror("fail to socket");
 36                 exit(-1);
 37         }
 38         // 与服务器建立连接
 39         printf("connecting... \n");
 40         if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) // connect
 41         {
 42                 perror("fail to connect");
 43                 exit(-2);
 44         }
 45         /* === 从此处开始 程序分做两个线程 === */
 46         // 创建发送消息的线程,调用发送消息的函数snd
 47         pthread_create(&thread, NULL, (void *)(&snd), NULL);    // pthread_create
 48         // 接收消息的线程
 49         while(1)
 50         {
 51                 int len;
 52                 if((len=read(sockfd, buf, BUFFSIZE)) > 0)       // read 读取通信套接字
 53                 {
 54                         buf[len] = '\0';        // 添加结束符,避免显示缓冲区中残留的内容
 55                         printf("\n%s", buf);
 56                         fflush(stdout);         // fflush 冲洗标准输出,确保内容及时显示
 57                 }
 58         }
 59         return 0;
 60 }
 61 
 62 // 发送消息的函数
 63 void snd()
 64 {
 65         char name[32], buf[BUFFSIZE];
 66 
 67         fgets(name, 32, stdin); // fgets 会读取输入字符串后的换行符
 68         write(sockfd, name, strlen(name));      // write 写入通信套接字
 69         while(1)
 70         {
 71                 fgets(buf, BUFFSIZE, stdin);
 72                 write(sockfd, buf, strlen(buf));
 73                 if(strcmp(buf, "bye\n") == 0)   // 注意此处的\n
 74                         exit(0);
 75         }
 76 }


 


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux C小项目 —— 聊天室 的相关文章

  • 常见RISC-V介绍

    当前一颗新出的CPU xff1a RISC V简直火透了半边天 xff0c 无论是财大气粗的阿里系的平头哥 xff0c 还是新创企业 xff0c 似乎只要和RISC V挂上钩就足可以实现赶英超美 那事实上RISC V是什么 xff1f 除了
  • qsort的compare函数

    qsort的compare函数 功能 xff1a 使用快速排序例程进行排序 头文件 xff1a stdlib h 用法 xff1a void qsort void base size t num size t width int cdecl
  • 深度相机(3D相机)

    二维图片 xff0c 人眼可以通过物体的相对位置关系判断物体距离的远近 xff0c 而相机则不可以 深度相机 xff08 3D相机 xff09 就是终端和机器人的眼睛 xff0c 其就是通过该相机能检测出拍摄空间的景深距离 通过深度相机获取
  • ubuntu只有一种分辨率的解决方案——4K显示屏与扩展屏幕

    4K显示屏的ubuntu系统在安装NVIDIA显卡后 xff0c 只有一个分辨率为 xff1a 3840 2160 xff0c 如图所示 xff0c 该分辨率在扩展显示器的使用时 xff0c 会出现4K主屏幕分辨率很高 xff0c 而扩展显
  • 使用RTSO-9003拓展板的TX2镜像备份与恢复

    须知 TX2进入recovery模式 在给TX2通电的时候 xff0c 马上按住板子上的recovery键 xff0c 持续3秒左右 xff0c 继续保持按住recovery键并按住reset键 xff0c 便可以进入TX2的recover
  • MATLAB学习笔记

    MATLAB学习笔记 一级目录备忘录HELP文件路径MATLAB函数定义与表达MATLAB特殊变量和常量MATLAB文件读写数组数组的创建常用操作常用操作常用操作 MATLAB画图plot函数 xff1a 改变图像中线的颜色和线条形式leg
  • ubuntu16.04 boot空间不足 no space left on device

    Linux 中 boot 是存放系统启动文件的地方 xff0c 安装 ubuntu 时单独分区给 200M 足够 xff0c 但是系统内核更新后 xff0c 老的内核依然保存在 boot 分区内 xff0c 几次升级后 xff0c 就会提示
  • 什么是死锁,产生死锁的原因及必要条件

    什么是死锁 xff1f 所谓死锁 xff0c 是指多个进程在运行过程中因争夺资源而造成的一种僵局 xff0c 当进程处于这种僵持状态时 xff0c 若无外力作用 xff0c 它们都将无法再向前推进 因此我们举个例子来描述 xff0c 如果此
  • nvidia jetson TX2 踩坑解决记录

    最近拿着一张多年前实验室买的Jetson想刷个软路由玩 xff0c 奈何折腾了一周才把clash meta内核装好 xff0c 记录一下自己踩的坑 xff0c 整理一下以免其他玩jetson TX2的兄弟掉大坑 已经过去一周了很多都记不太清
  • 接收灵敏度

    接收灵敏度是检验基站接收机接收微弱信号的能力 xff0c 它是制约基站上行作用距离的决定性技术指标 xff0c 也是RCR STD 28协议中 xff0c 空中接口标准要求测试的技术指标之一 合理地确定接收灵敏度直接地决定了大基站射频收发信
  • 16行,使用Python制作简易版QQ自动回复机器人(windows版)

    目录 1 安装go cqhttp 2 使用go cqhttp 2 1 发送信息 2 1 1发送 你好 2 1 2 在群里 64 人 2 2获取群成员列表 2 3 实现QQ机器人 1 安装go cqhttp 点此安装go cqhttp xff
  • FPGA在线升级实战应用篇

    FPGA在线升级实战应用篇 1 摘要 项目在运营过程中可能需要根据应用需求更改固件 xff0c 或者对现有产品进行升级及在产品使用过程出现的故障进行分析 xff0c 故需要对产品进行升级维护 以往的产品出现的故障或BUG问题只能通过产品寄回
  • (xTaskNotify)- assert failed! 错误的修复

    今日在测试ESP32代码的时候 xff0c 使用xTaskNotify发生错误 xff0c 提示如下 xff1a xTaskNotify assert failed xff0c 然后系统重启 找了一下原因 xff0c 在xTaskNotif
  • kubernetes dashboard用户界面安装使用

    原文 xff1a https www toocruel net kubernetes dashboardyong hu jie mian an zhuang shi yong 1 下载kubernetes dashboard yaml文件
  • 网络通信编程学习笔记(四):在Ubuntu下创建新用户、用puTTY/VNCViewer远程登录、用ftp上传和下载、用Xming远程连接

    前言 真的用不惯VNCViewer xff0c 树莓派还是外接显示屏来的舒服 xff0c 分辨率也是1080p xff0c 只有全高清壁纸才可以慰籍学习之痛 xff01 Xming也是不如按开机键来的方便 笑哭 目录 一 用puTTY VN
  • node.js和npm离线安装

    离线安装node js和npm 1 下载官方安装包并拷贝到离线机器上 官方下载地址 xff1a https nodejs org en download 2 解压文件 xff1a tar xJf node v8 9 4 linux x64
  • Github 创建新分支

    一 clone Repository clone Github 上的Repository xff0c 如下 xff1a git clone git 64 github span class hljs preprocessor com spa
  • ARM平台基于嵌入式Linux部署ROS

    By Toradex 秦海 随着ARM平台处理能力的日益强大 xff0c 越来越多的工业智能 机器人应用在ARM平台上面实现 xff0c 在这个过程中不可避免的就涉及到将机器人应用开发框架移植到ARM平台来运行 xff0c 因此本文就着重示
  • 如何设计一款低成本的计算机载板- 第一部分

    By Toradex Peter Lischer 1 简介 在以前的博客文章中 xff0c 我们已经在一个硬件项目中使用计算机模块提出了许多讨论 xff0c 因此 xff0c 这里我们假设你已经在项目中决定采用计算机模块SoM xff0c
  • git rebase后commit id的变化

    经测试发现 xff0c 在执行完 git rebase 之后 xff0c 1 xff09 会生成的新的 commit id 2 xff09 新 commit 与旧 commit 的父节点不相同 3 xff09 旧 commit 的父节点保持

随机推荐

  • 嵌入式Linux下串口调试

    By Toradex秦海 1 简介 UART串口是嵌入式设备最为常用的调试和通讯接口之一 xff0c 无论是RS232还是RS422 485都有着非常广泛的应用 xff0c 因此本文就基于嵌入式Linux演示在User Space进行串口调
  • [LeetCode刷题笔记] 关于LeetCode的前言

    原创文章 转载请注册来源http blog csdn net tostq 又到了一年毕业就业季了 xff0c 三年前的校招季我逃避了 xff0c 可这一次终于还是要轮到我了 61 61 作为要准备踏入码农行业的人来说 xff0c 要准备校招
  • 关于机器视觉标定的pnp问题

    https blog csdn net cocoaqin article details 77485436 https blog csdn net cocoaqin article details 77848588利用二维码求解相机世界坐标
  • kvaser在linux中的应用

    本文主要讲解 xff0c kvaser如何使用简单socketcan 1 硬件 kvaser USBcan Pro 2xHS v2 2 准备系统 ubuntu 16 04 由于项目需要在程序中使用socketcan xff0c 所以需要将k
  • 编译错误-build stopped: subcommand failed. 解决方法

    make 1 Leaving directory 96 home sunhz sl8541e out target product sp8541e srvm obj u boot15 39 make Leaving directory 96
  • 如何备份jetson nano 的u盘系统?

    使用工具 xff1a Win32DiskImager 备份步骤 xff1a 1 在本地盘 xff08 C盘或D盘都行 xff0c 盘符剩余内存大于u盘系统内存就行 xff09 新建文本文档 xff0c 连后缀名字一起改成backup img
  • Security Onboard Communication-SecOC

    一 通讯加密的必要性 随着汽车电子的发展及整车功能复杂性的提高 xff0c 车载控制器数量从之前的寥寥几个增加至规模复杂的上百个 基于功能的需求 xff0c 各个控制器每时每刻需要进行大量数据的交互 xff0c 数据交互的方式也多种多样 x
  • 解决VNC远程连接树莓派,窗口显示不全的问题,亲测可行!!

    哇 xff0c 就在刚刚才百度到解决VNC远程连接树莓派 xff0c 窗口显示不全的问题 xff0c 昨晚上查了一晚上都没搞定 xff0c xff0c xff0c 首先说下问题吧 xff0c 就是用VNC远程连接树莓派后 xff0c 会出现
  • Avoid mutating a prop directly since the value will be overwritten whenever

    在vue中 父组件向子组件传值 并在子组件改变了父组件的值 就会发出警告 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定 xff1a 父级 prop 的更新会向下流动到子组件中 xff0c 但是反过来则不行 这样会防止
  • 【Linux】异步网络库dyad代码阅读

    简介 dyad是一个基于C编写的异步网络库 xff0c 非常精简 xff0c 单C文件 xff0c 仅实现TCP xff0c 很适合用来学习Linux网络编程和异步非阻塞处理 链接 Github链接 基于Dyad的echo server实现
  • 明文签署文件不可用,结果为‘NOSPLIT’

    快速记录一下在 debian上使用 apt update时遇到的一个小问题 明文签署文件不可用 xff0c 结果为 NOSPLIT xff08 您的网络需要认证吗 xff1f xff09 报错现场如下所示 xff1a span class
  • Ubuntu 16.04 安装 Gazebo

    Gazebo 是一款 3D 动态模拟器 xff0c 能够准确有效地模拟复杂室内和室外环境中的机器人群体 虽然类似于游戏引擎 xff0c Gazebo 提供了更高保真度的物理模拟 一套传感器以及用户和程序接口 Gazebo 的典型用途包括 x
  • 设计一款STM32的BootLoader

    参考文章 xff1a https blog csdn net qingtian506 article details 9128899 之前很想做一个属于STM32的BootLoader xff0c 但是想想没什么实际用处就没有下手 xff0
  • vscode之C/C++代码自动补全

    目录 准备 xff1a 步骤 xff1a 安装插件重启加载更改配置选项找到 设置 打开json配置添加配置json保存并重新打开vscode即可自动补全 准备 xff1a IDE xff1a vscode 安装就不展开啦 步骤 xff1a
  • Velodyne 16线三维激光雷达

    Velodyne 16线三维激光雷达VLP 16介绍 16线激光雷达VLP 16是Velodyne公司出品的小型的3维激光雷达 xff0c 保留了电机转速可调节的功能 实时上传周围距离和反射率的测量值 16线激光雷达VLP 16具有100米
  • [gazebo仿真]添加RealSense双目相机传感器

    下载双目相机模型和插件 xff0c 其中包含了T265 R200 D435模型 git clone https span class token operator span span class token operator span sp
  • actionlib的应用:STDR仿真器定点巡航

    关于STDR仿真器的定点巡航 xff0c ROS小课堂提供了非常详细的教程http www corvin cn 892 html xff0c 作者是用python写的巡航脚本 xff0c 下面提供一下C 43 43 版本的巡航代码 若有错误
  • ROS动态调节参数

    参考 xff1a ROS学习之路07 xff1a 编写动态重配置 dynamic reconfigure 参数的节点 https blog csdn net l1216766050 article details 79575423 ROS使
  • Linux C小项目 —— 实现文件传输

    编译运行 xff1a 文档说明 xff1a 1 整体程序设计 服务器程序是一个死循环 xff0c 处理一次连接之后不退出 xff0c 而客户端程序只处理一个连接就可以了 2 客户端程序设计 客户端程序的主要任务 xff1a a 分析用户输入
  • Linux C小项目 —— 聊天室

    多线程的聊天室 服务器端 xff1a 实现多用户群体聊天功能 xff08 人数上限可设置 xff09 xff1b 每个用户所发送的消息 xff0c 其他已连接服务器的用户均可以收到 xff1b 用户输入 bye 退出 xff0c 服务器输入