Linux 简单的聊天室
生活随笔
收集整理的這篇文章主要介紹了
Linux 简单的聊天室
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
1.引言
??前兩篇寫過關(guān)于多線程、多進程的Socket編程文章。這里就寫了一個簡單的多線程聊天室。文章寫的很粗糙,對于函數(shù)的一些錯誤返回,沒有具體分析(但簡單的聊天室基本不需要這些),還請諒解!!
??目的是:服務(wù)端將客戶端發(fā)來的消息,轉(zhuǎn)發(fā)給其他在線的客戶端。
??在編程中,遇到很多有趣的Bug,覺得很有意思,后面與大家分享。
2. 關(guān)鍵點
- 對于文件描述符,也就是套接字,是int類型的。所以可以在服務(wù)端創(chuàng)建一個int類型數(shù)組,用來存儲所有連接上的套接字。我們將套接字,作為用戶的ID。這樣就方便廣播。
- 連接上的套接字,通常是從3開始的。3,4,5,6…依次往下排。這是因為,3號套接字是服務(wù)端用來監(jiān)聽的套接字。剩下的是與客戶端連接順序有關(guān)的套接字,客戶端越先與服務(wù)端連接,分到的套接字值就越小。0,1,2號套接字是被系統(tǒng)占用,不會給用戶分配。
- 因為涉及到線程,所以編譯時,一定要加上 -lpthread。
- send()/write(),實際上是copy的過程,絕不是發(fā)送的過程。它們只是將要發(fā)送的東西copy到套接字的發(fā)送緩沖區(qū)中。只要copy成功,它們就返回。至于具體的發(fā)送,是協(xié)議要做的事。read()/recv(),也是如此。
- 這里要強調(diào)一個問題,數(shù)據(jù)的接收和發(fā)送是無關(guān)的,read()/recv() 函數(shù)不管數(shù)據(jù)發(fā)送了多少次,都會盡可能多的接收數(shù)據(jù)。也就是說,read()/recv() 和 write()/send() 的執(zhí)行次數(shù)可能不同。write()/send() 重復(fù)執(zhí)行三次,每次都發(fā)送字符串"abc",那么目標(biāo)機器上的 read()/recv() 可能分三次接收,每次都接收"abc";也可能分兩次接收,第一次接收"abcab",第二次接收"cabc";也可能一次就接收到字符串"abcabcabc"。
3. 代碼實現(xiàn)
/*** Server 端Auther:Liang jie Objective:服務(wù)端將收到的消息轉(zhuǎn)發(fā)給其他客戶端 */#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 //最大連接數(shù),也就是可以參與群聊的最大人數(shù)#define MAXSIZE 1024//轉(zhuǎn)發(fā)函數(shù)的聲明 int SendToClient(int fd,char* buf,int Size);/*定義全局變量*/ int fdt[Max]={0}; //用來存套接字(文件描述符)的數(shù)組 char mes[MAXSIZE]; //接收緩沖區(qū) /**///子線程函數(shù) void *pthread_service(void* sfd) {int fd=*(int *)sfd;while(1){int numbytes; //返回的實際字節(jié)數(shù)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("轉(zhuǎn)發(fā)的信息=%s\n",mes);//開始轉(zhuǎn)發(fā)SendToClient(fd,mes,numbytes);bzero(mes,MAXSIZE);}close(fd);pthread_exit(0); }/*轉(zhuǎn)發(fā)函數(shù)*/ int SendToClient(int fd,char* buf,int Size) {int i;int e;for(i=0;i<Max;i++){ //給在線的客戶端轉(zhuǎn)發(fā)消息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:服務(wù)端將收到的消息轉(zhuǎn)發(fā)給其他客戶端 */ #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端只有一個套接字//用來接收消息的子線程函數(shù)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);//創(chuàng)建子線程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. 結(jié)論
??文章是關(guān)于Linux下聊天室的,很簡單的一個代碼。但是這樣簡單的一個代碼段,卻體現(xiàn)出來很多問題。包括對函數(shù)的理解、Socket緩沖區(qū)的概念、以及TCP/UDP協(xié)議的要求等。
總結(jié)
以上是生活随笔為你收集整理的Linux 简单的聊天室的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hive的Rank函数
- 下一篇: linux 其他常用命令