基于linux平台制作的简单聊天室程序,可以通过修改宏定义USER_LIMIT进而修改支持的用户个数

不涉及任何外部库,直接g++编译即可

涉及到:socket编程,零拷贝文件描述符数据splice,IO多路复用。

进入聊天室:

当有其他人进入聊天室:

发送信息(没加键盘对应的中断回调,不小心多按了sorry):

另一个用户接收

当聊天室满了会提示:

源码 客户端程序

/***************************************************************************
聊天室客户端
功能:1.从标准输入接收数据发送到服务器
功能:2.打印从服务器收到的数据
功能:3.超出一定时间没连上就退出
by Frankie****************************************************************************/
#include<stdio.h>
#include<sys/socket.h>
#include<time.h>
#include<strings.h>
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <poll.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>#define _GNU_SOURCE 1
#define BUF_SIZE 300int Timeout_Connect(const char* ip, int port, int time)
{int ret = 0;struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int sockfd = socket(PF_INET, SOCK_STREAM, 0);assert(sockfd >= 0);struct timeval timeout;timeout.tv_sec = time;timeout.tv_usec = 0;socklen_t len = sizeof(timeout);ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);assert(ret != -1);ret = connect(sockfd, (struct sockaddr*)&address, sizeof(address));if (ret == -1){if (errno == EINPROGRESS){printf("connecting timeout\n");return -1;}printf("Can't connect Server,Please try it agian.\n");return -1;}return sockfd;
}int main(int argc, char* argv[])
{argc = 2;if (argc <= 2){printf("Pls put in two args: 1.IP 2.Port \n");}//连接服务器//int port = atoi(argv[2]);//int sockfd = Timeout_Connect(argv[1], port,10);int sockfd = Timeout_Connect("192.168.154.128", 12347,10);//监听标准输入与服务端struct pollfd fds[2];memset(fds, 0, sizeof fds);fds[0].fd = 0, fds[0].events = POLLIN, fds[0].revents = 0;fds[1].fd = sockfd, fds[1].events = POLLIN | POLLRDHUP, fds[1].revents = 0;//用户读缓存char RDBUF[BUF_SIZE];int pipefd[2];int ret = pipe(pipefd);if (ret < 0){perror("PIPE creat:");}while (1){ret = poll(fds, 2, -1);if (ret < 0){printf("Exception:Poll error\n");break;}if (fds[1].revents & POLLRDHUP){printf("Server has closed your connection\n");break;}if (fds[1].revents & POLLIN){memset(RDBUF, '\0', sizeof RDBUF);recv(sockfd, RDBUF, BUF_SIZE-1,0);printf("%s\n", RDBUF);}if (fds[0].revents & POLLIN){splice(0, 0, pipefd[1], NULL, 32768, SPLICE_F_MORE);splice(pipefd[0], NULL, sockfd, 0, 32768, SPLICE_F_MORE);}}close(sockfd);}

服务端

/***************************************************************************
聊天室服务器
功能:1.从对端接收用户数据并维护
功能:2.广播用户发送的报文
by Frankie****************************************************************************/
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <poll.h>#define USER_LIMIT 5
#define BUFFER_SIZE 300
#define FD_LIMIT 65535struct client_data
{sockaddr_in address;char* write_buf;char buf[BUFFER_SIZE];
};int setnonblocking(int fd)
{int old_option = fcntl(fd, F_GETFL);int new_option = old_option | O_NONBLOCK;fcntl(fd, F_SETFL, new_option);return old_option;
}int main(int argc, char* argv[])
{if (argc <= 2){printf("usage: %s ip_address port_number\n", basename(argv[0]));return 1;}const char* ip = argv[1];int port = atoi(argv[2]);int ret = 0;struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int listenfd = socket(PF_INET, SOCK_STREAM, 0);assert(listenfd >= 0);ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));assert(ret != -1);ret = listen(listenfd, 5);assert(ret != -1);//初始化环境client_data* users = new client_data[FD_LIMIT];pollfd fds[USER_LIMIT + 1];int user_counts = 0;fds[0].fd = listenfd, fds[0].events = POLLIN | POLLERR, fds[0].revents = 0;for (int i = 1;i <= USER_LIMIT;i++){fds[i].fd = -1;fds[i].events = POLLIN | POLLERR;fds[i].revents = 0;}//事件循环while (1){ret = poll(fds, user_counts + 1, -1);if (ret < 0){printf("POLL FAILED!\n");break;}for (int i = 0;i < user_counts + 1;i++){//监听到lfd上的连接事件if ((fds[i].fd == listenfd) && (fds[i].revents & POLLIN)){struct sockaddr_in client_address;socklen_t client_addrlength = sizeof(client_address);int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);if (connfd < 0){printf("errno is: %d\n", errno);continue;}if (user_counts >= USER_LIMIT){//printf("Refuse the %d Connection\n",connfd);send(connfd, "Sorry,The Person is full\n", 30, 0);close(connfd);continue;}user_counts++;users[connfd].address = client_address;setnonblocking(connfd);fds[user_counts].fd = connfd;fds[user_counts].events = POLLIN | POLLERR | POLLRDHUP;fds[user_counts].revents = 0;for (int j = 1;j < user_counts;j++) { send(fds[j].fd, "Coming a new Person\n", 30, 0); } //向其他人说明send(connfd, "Welcome to my chatting room!\n\n\nYou can say what ever you want!\n", 80, 0);}//出现错误事件else if (fds[i].revents & POLLERR){printf("get an error from %d\n", fds[i].fd);char errors[100];memset(errors, '\0', 100);socklen_t length = sizeof(errors);if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length) < 0){printf("get socket option failed\n");}continue;}else if (fds[i].revents & POLLRDHUP){users[fds[i].fd] = users[fds[user_counts].fd]; //将用户信息向前移,下个连接进来user_counts处会被覆盖close(fds[i].fd);fds[i] = fds[user_counts]; //i被删除,现在将最后面的用户数据移到这里,所以要重新在处理一次,故将i--i--; user_counts--;printf("A Client Left\n");}else if (fds[i].revents & POLLIN){int connfd = fds[i].fd;memset(users[connfd].buf, '\0', BUFFER_SIZE);ret = recv(connfd, users[connfd].buf, BUFFER_SIZE - 1, 0);//printf("Get %d bytes of client data %s from %d\n", ret, users[connfd].buf, connfd);//读时出现错误if (ret < 0){if (errno != EAGAIN){close(connfd);users[fds[i].fd] = users[fds[user_counts].fd];fds[i] = fds[user_counts];i--;user_counts--;}}else {for (int j = 1;j <= user_counts;j++){if (fds[j].fd == connfd) continue;fds[j].events &= ~POLLIN;     //准备下次写事件,同时防止逻辑混乱,暂时~POLLINfds[j].events |= POLLOUT;users[fds[j].fd].write_buf = users[connfd].buf; //要通知每个人,置指针指向发送者的缓冲区}}}else if (fds[i].revents & POLLOUT){int connfd = fds[i].fd;if (!users[connfd].write_buf){continue;}ret = send(connfd, users[connfd].write_buf, strlen(users[connfd].write_buf), 0);users[connfd].write_buf = NULL;fds[i].events &= ~POLLOUT;fds[i].events |= POLLIN;}}}delete[] users;close(listenfd);return 0;}

对于本服务器程序,有很多改善的地方。比如我们可以开多进程或多线程将核利用起来实现负载均衡,其次,可以关注到服务器读到的数据是可以不用处理的,我们可以用与客户端同样的方式,利用splice进行零拷贝将数据发送到对端,还可以利用shmget同样可以优化性能。

源码链接: https://github.com/VerdantE1/Chatting_room

基于Linux实现的聊天室小程序相关推荐

  1. java聊天室小程序论文_在Java项目中利用continue与break制作一个聊天室小程序

    在Java项目中利用continue与break制作一个聊天室小程序 发布时间:2020-12-08 16:03:27 来源:亿速云 阅读:98 作者:Leah 在Java项目中利用continue与 ...

  2. Linux网络编程--聊天室客户端程序

    聊天室客户端程序 #define _GNU_SOURCE 1 #include <sys/types.h> #include <sys/socket.h> #include & ...

  3. Linux下基于socket和多线程的聊天室小程序

    转载:http://blog.csdn.net/robot__man/article/details/52460733 要求:基于TCP编写,一个聊天室最多100人.  客户端:  1.用户需要登录, ...

  4. 聊天室小程序服务端源码(客户端接下一条)

    功能:类似qq群聊         1.进入聊天室需要输入用户名         2.有人进入聊天室有提示,其他人会收到提示            xxx进入聊天室         3.某人发消息,则 ...

  5. ajax无刷新聊天室,实现一个无刷新的基于ajax的简易聊天室

    实现一个基于ajax的简易聊天室 1 程序主页面 在线交流对话 include_once("server1.server.php"); #servidor para XAJAX $ ...

  6. 基于百度UNIT的微信智能问答聊天机器人小程序

    微信聊天机器人小程序 方法 人人都能拥有的聊天小程序 登录百度ai开放平台,申请百度unit智能聊天机器人,获取ak和sk密钥. 将密钥复制到chat.js对应处即可. 功能 可以进行闲聊,天气查询, ...

  7. 【毕业设计之PHP系列】基于PHP的网络聊天室系统

    基于PHP的网络聊天室系统 摘要:我们生活在一个通信变得非常重要的世界里,人们需要同他人快速容易的进行交流.E-mail.电话.邮件以及在线聊天是以书写文字的形式让人们进行思想交流的媒体.通信时一个重 ...

  8. workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)...

    workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...

  9. libvirt 用c语言编译,基于C语言libvirt API简单小程序

    libvirt API简单小程序 1.程序代码如下 #include #include int getDomainInfo(int id) { virConnectPtr conn = NULL; v ...

最新文章

  1. 【TensorFlow2.0】(7) 张量排序、填充、复制、限幅、坐标选择
  2. React路由 react-router-dom
  3. 自保护、人机互动柔性织物传感器研究取得进展
  4. php imagecreate 白色,ImageCreateTrueColor白色背景问题
  5. TF之TF flags:TF flags(命令行解析)的简介、安装、使用方法之详细攻略
  6. 万达放弃A股上市,数据揭秘王思聪投资为何频繁跳水?
  7. 根据centos系统启动过程定位故障位置
  8. pdffactory字体打印不对_标准论文格式字体要求
  9. android 音频加载hal so调试
  10. java溢出怎么处理_java数据溢出怎么处理?
  11. windows下kangle虚拟主机-easypanel跑iis6.0开php空间并支持ZendOptimizer教程
  12. 计算机里面保留游戏账号名字的是什么文件夹,租号玩系统文件已损坏-电脑安装游戏老是显示文件已损坏...
  13. java ffmpeg amr转wav_FFmpeg转音频格式为wav
  14. 微信小程序实现蓝牙开门前后端项目(一)
  15. 最近网上比较火的虎年西游记金钱豹头像制作小程序源码
  16. c++11:std::declval、decltype
  17. 如何让Python画笔画一个圆
  18. 戏说:诸葛亮的真实身份竟是汉献帝
  19. 127.0.0.1:3000端口已被占用
  20. “管家婆”软件用于维修管理

热门文章

  1. UMA Frame Buffer Size 核显显存与CSGO帧率
  2. 远程服务器证书过期怎么解决,关于演示win7远程桌面证书过期的具体操作方式...
  3. Decal Buffer相关
  4. 计算机桌面英文翻译,电脑显示器英语怎么说
  5. Excel如何快速评定考核成绩等级
  6. Evernote 印象笔记离线使用方法
  7. C#图片压缩,等比例缩小
  8. 针对Google注册不能收到手机验证码的解决方案
  9. 全国二级计算机考试准考证打印官网
  10. ObjectARX C++自定义实体