两个客户端实现聊天功能,那么服务器作转发信息的作用,客户端A先将信息发送到服务器,在由服务器将信息发送到客户端B,客户端B也是一样。客户端与服务器都应该有两个执行流,服务器的一个执行流不断的接收客户端A的信息并将其发送给客户端B,另一个执行流不断地接收客户端B的信息并将其发送给客户端A,而客户端的两个执行流分别做读信息操作和写信息操作。这是我们的常规思维,如果用单线程的方法有该如何做呢?
socket称之为网络套接字,但其实也是一个文件描述符,这个文件描述符被默认为阻塞状态,accept函数如果没有客户端与之相连就一直阻塞在这里,程序不会在执行下去,read函数也是一样,如果从缓冲区中没有读到数据就会被阻塞,直到读到数据时才能退出这个函数。如下图,clientA向server发送一个data1,server读到了这个data1后在发送给clientB,如果clientA并没有发送信息,此时read函数就会阻塞,函数卡在read函数这,那么此时如果clientB发来一个data2,server根本读不到,但是这个data2已经放在了缓冲区里,当clientA发来消息后,read函数便不再阻塞,并将这条消息发送给clientB,此时server才能从缓冲区里读到data2。

既然是默认为阻塞,那么也可以设置为非阻塞,在非阻塞状态下,read函数读到数据就返回所读取数据的个数,没有读到数据就立即返回0,此时便不会出现无法发送数据或者发完数据后才接收到上一条信息。
我们可用如下方法设置阻塞

 #include <unistd.h>
#include <fcntl.h>
int flags=fcntl(sockid,F_GETFL,0);
fcntl(sockid,F_SETFL,flags|O_NONBLOCK);

sockid表示套接字,也是文件描述符,想要设置谁非阻塞就填谁的套接字

设置非阻塞方法:

 #include <unistd.h>
#include <fcntl.h>
int flags=fcntl(sockid,F_GETFL,0);
fcntl(sockid,F_SETFL,flags&~O_NONBLOCK);

服务器

#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <sys/select.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define clientA 0
#define clientB 1
#define max_client 2  //最大连接客户端数量typedef struct clientNew
{char     *addr;int      sockid;uint16_t port;
}client_new;//存放所连客户端的地址和socket等信息void client_new_init(client_new c_new[])
{int i=0;for(;i<max_client;i++){c_new[i].addr=NULL;c_new[i].sockid=0;c_new[i].port=0;}
}void setsocket_noblock(client_new c_new[])
{//设置所连的两个客户端socket非阻塞int flags=fcntl(c_new[clientA].sockid,F_GETFL,0);fcntl(c_new[clientA].sockid,F_SETFL,flags|O_NONBLOCK);flags=fcntl(c_new[clientB].sockid,F_GETFL,0);fcntl(c_new[clientB].sockid,F_SETFL,flags|O_NONBLOCK);
}void  read_clientA(client_new c_new[],int *flag)
{char receive[100]={0};if(read(c_new[clientA].sockid,receive,sizeof(receive))>0)//读取A客户端信息,如果没读到数据就返回0{time_t timep;//获取A客户端发来信息的时间time(&timep);if(strcmp(receive,"quit\n")==0)//如果A客户端发来quit,先把quit发给B客户端,在结束聊天{printf("ip=%s %s 用户发起退出\n",c_new[clientA].addr,ctime(&timep));write(c_new[clientB].sockid,receive,strlen(receive));usleep(1000);*flag=0;return ;}printf("ip=%s %s: ",c_new[clientA].addr,ctime(&timep));printf("%s\n",receive);write(c_new[clientB].sockid,receive,strlen(receive));//将A客户端发来的信息转发给B客户端}
}void  read_clientB(client_new c_new[],int *flag)
{char receive[100]={0};if(read(c_new[clientB].sockid,receive,sizeof(receive))>0)//读取B客户端信息,如果没读到数据就返回0{time_t timep;time(&timep);if(strcmp(receive,"quit\n")==0){printf("ip=%s %s 用户发起退出\n",c_new[clientA].addr,ctime(&timep));write(c_new[clientA].sockid,receive,strlen(receive));usleep(1000);*flag=0;return ;}printf("ip=%s: %s:",c_new[clientB].addr,ctime(&timep));printf("%s\n",receive);write(c_new[clientA].sockid,receive,strlen(receive));//将B客户端发来的信息转发给A客户端}
}void  communication(client_new c_new[],int server_sockid)
{   int flag=1;while(flag){read_clientA(c_new,&flag);read_clientB(c_new,&flag);}close(server_sockid);
}int internet(client_new c_new[])
{struct sockaddr_in sockaddr;sockaddr.sin_family=AF_INET;sockaddr.sin_port=htons(5188);sockaddr.sin_addr.s_addr=htonl(INADDR_ANY);int server_sockid=socket(AF_INET,SOCK_STREAM,0);const int on=1;if(setsockopt(server_sockid,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)//设置端口可重复利用{printf("setsockopt\n");return 0;}if(bind(server_sockid,(struct sockaddr *)&sockaddr,sizeof(sockaddr))<0){printf("bind\n");return 0;}if(listen(server_sockid,SOMAXCONN)<0){printf("listen\n");return 0;}struct sockaddr_in other_sock_addr;socklen_t other_sock_addr_len=sizeof(other_sock_addr);int j=0;while(j!=max_client)//连接两个客户端{int sockid_client=accept(server_sockid,(struct sockaddr *)&other_sock_addr,&other_sock_addr_len);c_new[j].sockid =sockid_client;c_new[j].addr=inet_ntoa(other_sock_addr.sin_addr);c_new[j].port=ntohs(other_sock_addr.sin_port);printf("ip=%s,port=%d  已连接\n",c_new[j].addr,c_new[j].port);j++;}return server_sockid;
}int main()
{client_new c_new[max_client];      //定义结构体数组client_new_init(c_new);                //初始化结构体数组int server_sockid=internet(c_new);   //网络连接并返回服务器socketsetsocket_noblock(c_new);         //设置所连的两个客户端socket非阻塞communication(c_new,server_sockid);    //通信return 0;
}

客户端

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>void do_read(int sockid,int *flag)
{char receive[100]={0};int r_size=read(sockid,receive,sizeof(receive));if(strcmp(receive,"quit\n")==0){printf("对方已结束聊天\n");*flag=0;return;}if(r_size>0){printf("\t\t\t");fputs(receive,stdout);}
}void do_write(int sockid,int *flag)
{char send[100]={0};int w_size=read(0,send,sizeof(send));if(strcmp(send,"quit\n")==0){printf("您已下线\n");write(sockid,send,sizeof(send));*flag=0;return;}if(w_size>0){write(sockid,send,sizeof(send));memset(send,0,strlen(send));}
}int internet()
{int  flag=1;struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(5188);addr.sin_addr.s_addr=inet_addr("127.0.0.1");int sockid=socket(AF_INET,SOCK_STREAM,0);socklen_t addrlen=sizeof(addr);if(connect(sockid,(struct sockaddr *)&addr,addrlen)<0){printf("connect\n");return 0;}int flags=fcntl(sockid,F_GETFL,0);fcntl(sockid,F_SETFL,flags|O_NONBLOCK);flags=fcntl(0,F_GETFL,0);fcntl(0,F_SETFL,flags|O_NONBLOCK);while(flag){do_read(sockid,&flag);do_write(sockid,&flag);}close(sockid);return 0;
}int main()
{internet();return 0;
}

以上程序只能实现局域网内通信。实现跨局域网聊天请点击

linux——两个客户端之间实现聊天(TCP、单线程)相关推荐

  1. linux服务器拷贝目录文件夹,linux两台服务器之间文件/文件夹拷贝

    linux两台服务器之间文件/文件夹拷贝 跨服务器拷贝需要用到的命令是scp. ----------------------拷贝文件夹--------------------------------- ...

  2. linux两台服务器传输,Linux两台服务器之间高速数据传输命令:scp应用详解

    Linux两台服务器之间高速数据传输命令:scp应用详解 Linux scp命令用于Linux之间复制文件和目录到另外一台,这个命令在多台服务器之间传输还是非常有用的,速度也是非常快的.比window ...

  3. linux同步某台服务器,linux 两台服务器之间开机自动同步指定目录下的文件

    linux 两台服务器之间开机自动同步指定目录下的文件 服务器B(172.17.166.11)上开机或者重启会自动拉取服务器A(172.17.166.10)上指定目录下所有文件 1.创建服务器B到服务 ...

  4. Linux两台主机之间建立信任关系

    Linux两台主机之间建立信任关系 一般用ssh命令访问另一台机器,或者用scp命令从别的机器拷贝数据和文件,都要输入对应账户的密码.而在两台机器之间建立信任关系,则可以省略输入密码的过程. 一  : ...

  5. Linux 两台主机之间建立信任关系方式及基本原理

    前言: 去年学过一段时间的现代密码学,最近在配置github, Linux主机之间建立信任关系的时候都用到了其中一些知识,所以刚好整理一下,想直接看操作方式的可直接拉到下面 密码学基本知识 一 现代密 ...

  6. java TCP 实现聊天室功能 多线程实现一个服务器端和多个客户端之间的聊天

    package 实验702;import java.net.Socket;public class Client {public static void main(String[] args) {So ...

  7. Linux 两台服务器之间传输文件和文件夹

    今天处理一个项目要迁移的问题,突然发现这么多图片怎么移过去,可能第一时间想到的是先从这台服务器下载下来,然后再上传到另外一台服务器上面去,这个方法确实是可行,但是实在是太费时间了,今天我就教大家怎么快 ...

  8. Linux服务器和客户端之间的数据同步(备份)

    最近实验室总是停电,电脑今天系统出现了点问题,所以需要对数据进行备份,以防硬盘发生突然损坏.目前用到的方法是通过rysnc和inotify协同数据同步,数据可以实时更新.另外,对于经常改变且比较大的数 ...

  9. linux做界面切换,linux两个界面之间的切换

    鲁夫 于 2010-11-16 16:07:36发表: 终于知道了!谢谢 qqctk 于 2010-11-16 15:34:55发表: (e:e2s wywrw0512 于 2010-11-01 17 ...

最新文章

  1. 浙大版《C语言程序设计(第3版)》题目集 练习2-17 生成3的乘方表 (15 分)
  2. 【300】◀▶ IDL - ENVI API
  3. MYSQL储存过程和储存函数和变量
  4. mysql字符串函数混合使用_MySql字符串函数使用技巧
  5. UI5应用如果出现白屏但是没有任何错误消息打印出来,应该如何处理
  6. Node.js webpack 加载器
  7. 为啥查询那么慢?看完你就明白了!
  8. redis的持久化(RDBAOF的区别)
  9. 语音信号预处理3——计算原始chirp信号与滤波后chirp信号的相关性
  10. 用友数据库错误“未能读取并闩锁页(1:3355)(用闩锁类型SH)”修复
  11. Structs框架原理
  12. CSS3设置按钮的样式
  13. Hubstudio指纹浏览器和MaxProxy代理的配置教程
  14. csp怎么给线条描边_UI设计风格解析之MBE描边线条设计风格
  15. 鸿蒙系统支持列表,华为鸿蒙系统支持的手机型号一览【最新】
  16. Shiro-SpringBoot (一)
  17. TCGA数据库的利用(二)—— 数据处理
  18. 阿里云CDN全站加速,说说动态加速那些事
  19. javaScript-如何引入js代码
  20. DA LAO:SpringBoot讲述 快速构建微服务体系。

热门文章

  1. PHP学习方向-进阶2(三) 1
  2. 异常:fatal: unable to access 'https://git.oschina.net/pcmpcs/library.git/': Could not resolve host...
  3. Gartner: 2017年11大信息安全技术(解读版)
  4. 解决phpMyAdmin在nginx+php-fpm模式下无法使用的问题
  5. Spring学习(20)--- Schema-based AOP(基于配置的AOP实现) -- 配置切入点pointcut
  6. 9月第1周国内IT技术类网站:CSDN覆盖数持续走低
  7. 同一个闭区间上有界变差函数的和与积都是有界变差函数
  8. linux sed名宁,Linux shell利用sed批量更改文件名的方法
  9. java注解_Java注解教程及自定义注解
  10. 不使用比较运算符如何比较两个数的大小