基於tcp和多線程的多人聊天室-C語言

以前在學習關於網絡tcp和多線程的編程,學了知識之後不用一下總絕對心虛,因而就編寫了一個基於tcp和多線程的多人聊天室。編程

 

具體的實現過程:數組

  服務器端:綁定socket對象->設置監聽數->等待鏈接->有客戶端鏈接就新建一個線程,這個線程中,一旦就收到這個客戶發送的消息,就廣播的向其餘客戶端發送一樣的消息。服務器

  客戶端:向客戶端鏈接->新建線程用來接收服務器端發送的消息,同時主進程用來發送消息網絡

 

話很少說,直接上代碼多線程

 

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <pthread.h>
  5 #include <arpa/inet.h>
  6 #include <netinet/in.h>
  7 #include <string.h>
  8 #include <unistd.h>
  9 #include <stdlib.h>
 10 
 11 typedef struct sockaddr *sockaddrp;
 12 
 13 //存儲客戶端地址的結構體數組
 14 struct sockaddr_in src_addr[50];
 15 socklen_t src_len = sizeof(src_addr[0]);
 16 
 17 
 18 
 19 //鏈接後記錄confd數組
 20 int confd[50] = {};
 21 
 22 
 23 //設置鏈接人數
 24 int count = 0;
 25 
 26 
 27 void *broadcast(void *indexp)
 28 {
 29     int index = *(int *)indexp;
 30     char buf_rcv[255] = {};
 31     char buf_snd[255] = {};
 32     //第一次讀取用戶姓名
 33     char name[20] = {};
 34     int ret = recv(confd[index],name,sizeof(name),0);
 35     if(0 > ret)
 36     {
 37         perror("recv");
 38         close(confd[index]);
 39         return;
 40     }
 41 
 42     while(1)
 43     {
 44         bzero(buf_rcv,sizeof(buf_rcv));
 45         recv(confd[index],buf_rcv,sizeof(buf_rcv),0);
 46 
 47         //判斷是否退出
 48         if(0 == strcmp("quit",buf_rcv))
 49         {
 50             sprintf(buf_snd,"%s已經退出悟空聊天室",name);
 51             for(int i = 0;i <= count;i++)
 52             {
 53                 if(i == index || 0 == confd[i])
 54                 {
 55                     continue;
 56                 }
 57 
 58                 send(confd[i],buf_snd,strlen(buf_snd),0);
 59             }
 60             confd[index] = -1;
 61             pthread_exit(0);
 62                     
 63         }
 64 
 65 
 66         sprintf(buf_snd,"%s:%s",name,buf_rcv);
 67         printf("%s\n",buf_snd);
 68         for(int i = 0;i <= count;i++)
 69         {
 70             if(i == index || 0 == confd[i])
 71             {
 72                 continue;
 73             }
 74 
 75             send(confd[i],buf_snd,sizeof(buf_snd),0);
 76         }
 77         
 78     }
 79 
 80 }
 81 
 82 
 83 
 84 
 85 
 86 int main(int argc,char **argv)
 87 {
 88     printf("悟空聊天室服務器端開始運行\n");
 89 
 90 
 91     //建立通訊對象
 92     int sockfd = socket(AF_INET,SOCK_STREAM,0);
 93     if(0 > sockfd)
 94     {
 95         perror("socket");
 96         return -1;
 97     }
 98 
 99     //準備地址
100     struct sockaddr_in addr = {AF_INET};
101     addr.sin_port = htons(atoi(argv[1]));
102     addr.sin_addr.s_addr = inet_addr(argv[2]);
103 
104     socklen_t addr_len = sizeof(addr);
105 
106 
107 
108     //綁定
109     int ret = bind(sockfd,(sockaddrp)&addr,addr_len);
110     if(0 > ret)
111     {
112         perror("bind");
113         return -1;
114     }
115 
116 
117     //設置最大排隊數
118     listen(sockfd,50);
119 
120     int index = 0;
121 
122 
123     while(count <= 50)
124     {
125         confd[count] = accept(sockfd,(sockaddrp)&src_addr[count],&src_len);
126         ++count;
127         //保存這次客戶端地址所在下標方便後續傳入
128         index = count-1;
129 
130         pthread_t tid;
131         int ret = pthread_create(&tid,NULL,broadcast,&index);
132         if(0 > ret)
133         {
134             perror("pthread_create");
135             return -1;
136         }
137 
138 
139     }
140 
141 
142 }
server.c
  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <arpa/inet.h>
  5 #include <netinet/in.h>
  6 #include <pthread.h>
  7 #include <string.h>
  8 
  9 
 10 
 11 typedef struct sockaddr *sockaddrp;
 12 int sockfd;
 13 
 14 void *recv_other(void *arg)
 15 {
 16     char buf[255]= {};
 17     while(1)
 18     {
 19         int ret = recv(sockfd,buf,sizeof(buf),0);
 20         if(0 > ret)
 21         {
 22             perror("recv");
 23             return;
 24         }
 25         printf("%s\n",buf);
 26     }
 27 }
 28 
 29 
 30 
 31 
 32 int main(int argc,char **argv)
 33 {
 34     if(3 != argc)
 35     {
 36         perror("參數錯誤");
 37         return -1;
 38     }
 39 
 40     //創建socket對象
 41     sockfd = socket(AF_INET,SOCK_STREAM,0);
 42     if(0 > sockfd)
 43     {
 44         perror("socket");
 45         return -1;
 46     }
 47 
 48     //準備鏈接地址
 49     struct sockaddr_in addr = {AF_INET};
 50     addr.sin_port = htons(atoi(argv[1]));
 51     addr.sin_addr.s_addr = inet_addr(argv[2]);
 52 
 53     socklen_t addr_len = sizeof(addr);
 54 
 55 
 56     //鏈接
 57     int ret = connect(sockfd,(sockaddrp)&addr,addr_len);
 58     if(0 > ret)
 59     {
 60         perror("connect");
 61         return -1;
 62     }
 63 
 64     //發送名字
 65     char buf[255] = {};
 66     char name[255] = {};
 67     printf("請輸入您的暱稱:");
 68     scanf("%s",name);
 69     ret = send(sockfd,name,strlen(name),0);
 70     if(0 > ret)
 71     {
 72         perror("connect");
 73         return -1;
 74     }
 75 
 76     //建立接收子線程
 77     pthread_t tid;
 78     ret = pthread_create(&tid,NULL,recv_other,NULL);
 79     
 80     if(0 > ret)
 81     {
 82         perror("pthread_create");
 83         return -1;
 84     }
 85     //循環發送
 86     while(1)
 87     {
 88         //printf("%s:",name);
 89         scanf("%s",buf);
 90         int ret = send(sockfd,buf,strlen(buf),0);
 91         if(0 > ret)
 92         {
 93             perror("send");
 94             return -1;
 95         }
 96 
 97         //輸入quit退出
 98         if(0 == strcmp("quit",buf))
 99         {
100             printf("%s,您已經退出了悟空聊天室\n",name);
101             return 0;
102         }
103 
104     }
105 
106 }
client.c

將兩份代碼分別編譯生成相應可執行文件,例如在Linux下server,client,而後先執行./server 端口號 ip ,再執行./client 端口號 ip就能夠運行這個聊天室了。socket

 

總結:關於網絡編程,tcp是一種鏈接方式的通訊方式,兩邊一旦創建鏈接,就能夠經過send和recv函數發送消息,比較的可靠,缺點是速度比較慢(相對於udp來講)。另外關於多線程編程方面,線程實際上是一個進程的實體,是一個進程的組成部分,多個線程共享除了棧區之外的大部分區域,所以進程間的通訊比較方便,這種方便帶來的代價是,當多個進程同時去操做同一量時,容易形成不可預知的錯誤,所以就引入了互斥量(鎖)的概念,互斥量的使用就保證了進程間通訊的同步。tcp

相關文章
相關標籤/搜索