1.引言

  前两篇写过关于多线程、多进程的Socket编程文章。这里就写了一个简单的多线程聊天室。文章写的很粗糙,对于函数的一些错误返回,没有具体分析(但简单的聊天室基本不需要这些),还请谅解!!
  目的是:服务端将客户端发来的消息,转发给其他在线的客户端。
  在编程中,遇到很多有趣的Bug,觉得很有意思,后面与大家分享。

2. 关键点

  • 对于文件描述符,也就是套接字,是int类型的。所以可以在服务端创建一个int类型数组,用来存储所有连接上的套接字。我们将套接字,作为用户的ID。这样就方便广播。
  • 连接上的套接字,通常是从3开始的。3,4,5,6…依次往下排。这是因为,3号套接字是服务端用来监听的套接字。剩下的是与客户端连接顺序有关的套接字,客户端越先与服务端连接,分到的套接字值就越小。0,1,2号套接字是被系统占用,不会给用户分配。
  • 因为涉及到线程,所以编译时,一定要加上 -lpthread
  • send()/write(),实际上是copy的过程,绝不是发送的过程。它们只是将要发送的东西copy到套接字的发送缓冲区中。只要copy成功,它们就返回。至于具体的发送,是协议要做的事。read()/recv(),也是如此。
  • 这里要强调一个问题,数据的接收和发送是无关的,read()/recv() 函数不管数据发送了多少次,都会尽可能多的接收数据。也就是说,read()/recv() 和 write()/send() 的执行次数可能不同。write()/send() 重复执行三次,每次都发送字符串"abc",那么目标机器上的 read()/recv() 可能分三次接收,每次都接收"abc";也可能分两次接收,第一次接收"abcab",第二次接收"cabc";也可能一次就接收到字符串"abcabcabc"。

3. 代码实现

/***
Server 端Auther:Liang jie
Objective:服务端将收到的消息转发给其他客户端
*/#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#define PORT 10005#define Max 10   //最大连接数,也就是可以参与群聊的最大人数#define MAXSIZE 1024//转发函数的声明
int SendToClient(int fd,char* buf,int Size);/*定义全局变量*/
int fdt[Max]={0};    //用来存套接字(文件描述符)的数组
char mes[MAXSIZE];   //接收缓冲区
/**///子线程函数
void *pthread_service(void* sfd)
{int fd=*(int *)sfd;while(1){int numbytes;  //返回的实际字节数int i;numbytes=recv(fd,mes,MAXSIZE,0);if(numbytes<=0){for(i=0;i<Max;i++){if(fd==fdt[i]){fdt[i]=0;               }}printf("客户端 %d 已退出\n",fd);break;}printf("receive message from %d:\n",fd);printf("转发的信息=%s\n",mes);//开始转发SendToClient(fd,mes,numbytes);bzero(mes,MAXSIZE);}close(fd);pthread_exit(0);
}/*转发函数*/
int SendToClient(int fd,char* buf,int Size)
{int i;int e;for(i=0;i<Max;i++){     //给在线的客户端转发消息if((fdt[i]!=0)&&(fdt[i]!=fd)) send(fdt[i],buf,Size,0); }bzero(buf,sizeof(buf));return 0;
}int  main()
{ int listenfd, connectfd;    struct sockaddr_in server; struct sockaddr_in client;      int sin_size; sin_size=sizeof(struct sockaddr_in); int number=0;if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {   perror("Creating socket failed.");exit(1);}bzero(&server,sizeof(server));server.sin_family=AF_INET; server.sin_port=htons(PORT); server.sin_addr.s_addr = htonl (INADDR_ANY); if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { perror("Bind error.");exit(1); }   if(listen(listenfd,1) == -1){  perror("listen() error\n"); exit(1); } printf("Waiting for client....\n");while(1){if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) {perror("accept() error\n"); exit(1); }if(number>=Max){printf("no more client is allowed\n");close(connectfd);}int i;for(i=0;i<Max;i++){if(fdt[i]==0){fdt[i]=connectfd;break;}}pthread_t tid;pthread_create(&tid,NULL,(void*)pthread_service,&connectfd);pthread_detach(tid);number=number+1;}close(listenfd);  return 0;          }
/***
Client 端Auther:Liang jie
Objective:服务端将收到的消息转发给其他客户端
*/   #include <stdio.h> #include <stdlib.h> #include <unistd.h>#include <strings.h>#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h>     #include<string.h>#include <pthread.h>#define PORT 10005   #define MAXSIZE 4096char sendbuf[MAXSIZE];   char recvbuf[MAXSIZE];char name[100];int fd;    //client端只有一个套接字//用来接收消息的子线程函数void  *pthread_recv(void* ptr)
{while(1){if ((recv(fd,recvbuf,MAXSIZE,0)) == -1){ printf("recv() error\n"); exit(1); } printf("%s",recvbuf);memset(recvbuf,0,sizeof(recvbuf));}
}int main(int argc, char *argv[]) { int  numbytes;   char buf[MAXSIZE];   struct hostent *he;       struct sockaddr_in server;  if (argc !=2) {         printf("Usage: %s <IP Address>\n",argv[0]); exit(1); } if ((he=gethostbyname(argv[1]))==NULL){  printf("gethostbyname() error\n"); exit(1); } if ((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){ printf("socket() error\n"); exit(1); } bzero(&server,sizeof(server));server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr = *((struct in_addr *)he->h_addr);  if(connect(fd, (struct sockaddr *)&server,sizeof(struct sockaddr))==-1){  printf("connect() error\n"); exit(1); } printf("connect success\n");char str[]="已进入聊天室\n";printf("请输入用户名:");//读入一行值,直到遇到回车fgets(name,sizeof(name),stdin);send(fd,name,(strlen(name)-1),0);send(fd,str,(strlen(str)),0);//创建子线程pthread_t tid;pthread_create(&tid,NULL,pthread_recv,NULL);pthread_detach(tid); //客户端的输入while(1){memset(sendbuf,0,sizeof(sendbuf));fgets(sendbuf,sizeof(sendbuf),stdin);if(strcmp(sendbuf,"exit\n")==0){memset(sendbuf,0,sizeof(sendbuf));printf("您已退出群聊\n");send(fd,sendbuf,(strlen(sendbuf)),0);break;}send(fd,name,(strlen(name)-1),0);send(fd,":",1,0);send(fd,sendbuf,(strlen(sendbuf)),0);} close(fd);  return 0;}

4. 实验截图


5. 结论

  文章是关于Linux下聊天室的,很简单的一个代码。但是这样简单的一个代码段,却体现出来很多问题。包括对函数的理解、Socket缓冲区的概念、以及TCP/UDP协议的要求等。

Linux 简单的聊天室相关推荐

  1. linux大作业聊天室报告,Linux聊天室系统期末大作业.docx

    NANCHANG UNIVERSITY <Linux 系统编程>课程设计 题目:Linux 聊天室报告 学院:软件学院 专业:计算机软件 班级:计软 121 班 姓名:李俊楠 起讫日期: ...

  2. ICE专题:实现简单的聊天室(一)

    目标:实现一个简单的聊天室.本文实现的聊天室仅出于演示ICE的多播功能,即由一个Client发送的消息,广播至注册的其他Client上.以后的系列文章,将逐步完善这个例子,使其成为一个现实意义上可用的 ...

  3. java udp简单聊天程序_Java基于UDP协议实现简单的聊天室程序

    最近比较闲,一直在抽空回顾一些java方面的技术应用. 今天没什么事做,基于udp协议,写了一个非常简单的聊天室程序. 现在的工作,很少用到socket,也算是对java网络编程方面的一个简单回忆. ...

  4. Application对象 简单的聊天室

    使用Application对象可以使得多个用户在访问同一个asp.net应用程序时,能够共享信息,在多个用户同时访问asp.net时,都会产生一个Application对象.通常可以把共享的数据变量存 ...

  5. java 简单的聊天室_Java简单聊天室

    实现Java简单的聊天室 所用主要知识:多线程+网络编程 效果如下图 /** * * @author Administrator * *简单的多人聊天系统--重点:同时性,异步性 *1.客户端:发送消 ...

  6. SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室

    SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室 一.概述 WebSocket 是一种网络通信协议.RFC6455 定义了它的通信标准. WebSocket 是 HTML5 ...

  7. 用ServletContext做一个简单的聊天室

    这里主要是ServletContext的一个特性:ServletContext是一个公共的空间,可以被所有的客户访问.由此可见ServletContext比cookie和session的作用范围要大[ ...

  8. Netty - 一个简单的聊天室小项目

     经过一段时间对Netty的学习,我们对Netty各版本以及像ProtocolBuffers等技术应用都有了不少相关的了解, 我们就用这段时间学到的只是做一个简单的聊天室的小项目来练习自己学到的技术. ...

  9. python开发一个简单的聊天室

    使用python的twisted框架编写一个简单的聊天室 下面是基本架构 基本架构图 -- coding:utf-8 -- from twisted.internet.protocol import ...

  10. 局域网服务器发送消息,Java实现简单局域网聊天室

    本文实例为大家分享了Java实现简单局域网聊天室的具体代码,供大家参考,具体内容如下 Java 的Socket编程: 1.TCP协议是面向连接的.可靠的.有序的.以字节流的方式发送数据,通过三次握手方 ...

最新文章

  1. mysql 如何修改wait_timeout,interactive_timeout ,和 session ,global 有什么关系
  2. 利用OpenCV实现人眼的检测与跟踪
  3. Nginx的启动、停止
  4. QT的QDirIterator类的使用
  5. python线性输出_python sklearn-02:线性回归简单例子1
  6. webpack:多文件、多环境、跨域处理、热加载
  7. JLOI2016 方
  8. 网络邻居无法共享解决办法
  9. linux怎么修改sftp默认端口,转:linux 修改sftp服务默认提供者sshd的session timeout
  10. 第48课 加加乐 《小学生C++趣味编程》
  11. ea mysql建模_UML数据建模工具之Enterprise Architect(EA)实例-UML应用
  12. js - 预加载+监听图片资源加载制作进度条
  13. Java基础篇:简单介绍一下final
  14. Apache 基金会宣布 Apache Kylin 成为顶级项目
  15. 各种排序算法稳定性的探讨
  16. 高效能人士的七个习惯的简要定义与架构图
  17. box-sizing: border-box的作用
  18. 支付宝转账提现相关问题
  19. unshift向数组添加自定义对象
  20. 实参高阶函数和装饰器

热门文章

  1. 10 个免费学习编程的最佳网站
  2. java websocket ie8_解决WebSocket兼容ie浏览器版本问题
  3. C# 图片转换ICO工具
  4. 自动驾驶 4-5 自行车模型的横向动力学 Lateral Dynamics of Bicycle Model
  5. Python format 使用实例
  6. 推荐 | 九本不容错过的深度学习和神经网络书籍
  7. 安卓桌面软件哪个好_每日提醒软件哪个好?电脑上有什么好用的可以每天提醒的桌面便签软件...
  8. 【考研英语语法】名词性从句
  9. 哪种云计算机能玩游戏,低配置电脑的福音,体验腾讯START云游戏:只要有网就能玩这几个游戏,除了Mac版还有Win版!...
  10. 2018软科计算机科学工程排行,2018全球计算机与工程学科排名:清华第7,中国9个学科世界第一!...