linux——两个客户端之间实现聊天(TCP、单线程)
兩個客戶端實現聊天功能,那么服務器作轉發(fā)信息的作用,客戶端A先將信息發(fā)送到服務器,在由服務器將信息發(fā)送到客戶端B,客戶端B也是一樣。客戶端與服務器都應該有兩個執(zhí)行流,服務器的一個執(zhí)行流不斷的接收客戶端A的信息并將其發(fā)送給客戶端B,另一個執(zhí)行流不斷地接收客戶端B的信息并將其發(fā)送給客戶端A,而客戶端的兩個執(zhí)行流分別做讀信息操作和寫信息操作。這是我們的常規(guī)思維,如果用單線程的方法有該如何做呢?
socket稱之為網絡套接字,但其實也是一個文件描述符,這個文件描述符被默認為阻塞狀態(tài),accept函數如果沒有客戶端與之相連就一直阻塞在這里,程序不會在執(zhí)行下去,read函數也是一樣,如果從緩沖區(qū)中沒有讀到數據就會被阻塞,直到讀到數據時才能退出這個函數。如下圖,clientA向server發(fā)送一個data1,server讀到了這個data1后在發(fā)送給clientB,如果clientA并沒有發(fā)送信息,此時read函數就會阻塞,函數卡在read函數這,那么此時如果clientB發(fā)來一個data2,server根本讀不到,但是這個data2已經放在了緩沖區(qū)里,當clientA發(fā)來消息后,read函數便不再阻塞,并將這條消息發(fā)送給clientB,此時server才能從緩沖區(qū)里讀到data2。
既然是默認為阻塞,那么也可以設置為非阻塞,在非阻塞狀態(tài)下,read函數讀到數據就返回所讀取數據的個數,沒有讀到數據就立即返回0,此時便不會出現無法發(fā)送數據或者發(fā)完數據后才接收到上一條信息。
我們可用如下方法設置阻塞
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客戶端發(fā)來信息的時間time(&timep);if(strcmp(receive,"quit\n")==0)//如果A客戶端發(fā)來quit,先把quit發(fā)給B客戶端,在結束聊天{printf("ip=%s %s 用戶發(fā)起退出\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客戶端發(fā)來的信息轉發(fā)給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 用戶發(fā)起退出\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客戶端發(fā)來的信息轉發(fā)給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、单线程)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux debian安装Notepa
- 下一篇: 单词助手(可联网)