歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux下C語言多線程網絡通信簡單聊天程序

Linux下C語言多線程網絡通信簡單聊天程序

日期:2017/3/1 11:16:04   编辑:Linux編程

功能描述:程序應用多線程技術,可是實現1對N進行網絡通信聊天。但至今沒想出合適的退出機制,除了用Ctr+C。出於演示目的,這裡采用UNIX域協議(文件系統套接字),程序分為客戶端和服務端。應用select函數來實現異步的讀寫操作。

先說一下服務端:首先先創建套接字,然後綁定,接下進入一個無限循環,用accept函數,接受“連接”請求,然後調用創建線程函數,創造新的線程,進入下一個循環。這樣每當有一個新的“連接”被接受都會創建一個新的線程,實現1對N的網絡通信。在服務端程序中線程中用一個buffer讀寫,為了避免錯誤,這時就要給關鍵代碼加上互斥鎖work_mutex,具體見代碼。

服務端代碼

  1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<pthread.h>
5 #include<sys/socket.h>
6 #include<sys/un.h>
7 #include<unistd.h>
8 #include<semaphore.h> //這裡沒有用二進制信號量可以刪掉
9
10 char buffer[1024]; //讀寫用的區域
11 sem_t bin_sem; //沒用到的二進制信號量,可以刪掉
12 void *pthread_function(void *arg); //線程入口函數聲明
13 pthread_mutex_t work_mutex; //聲明互斥鎖
14
15 int main(){
16 int result; //整數變量用來儲存調用函數的返回值
17 struct sockaddr_un server_address, client_address; //UNIX域的套接字,server_address用於服務端的監聽,client_address用於客戶端連接後的套接字
18 int client_len; //連接後,accept函數會把客戶端的地址的長度儲存在這
19 int server_socketfd, client_socketfd;//服務端和客戶端的套接字文件描述符
20 pthread_t a_thread; //線程ID標志
21 pthread_attr_t thread_attr; //線程的屬性,後面可以看的,被我注釋掉了,沒用到,可以刪掉。
22
23 result = sem_init(&bin_sem, 0, 1); //初始化二進制信號量,因為用了互斥鎖,所以沒用到,可以刪掉
24 if(result != 0){
25 perror("sem_init");
26 exit(EXIT_FAILURE);
27 }
28
29 result = pthread_mutex_init(&work_mutex, NULL);//初始化互斥鎖
30 if(result != 0){
31 perror("pthread_mutex_init");
32 exit(EXIT_FAILURE);
33 }
34
35 server_socketfd = socket(AF_UNIX, SOCK_STREAM, 0);//創建套接字,用TCP連接方式,出於演示目的只用UNIX域套接字。
36
37 server_address.sun_family = AF_UNIX;
38 strcpy(server_address.sun_path, "server_socket");
39
40 unlink("server_socket"); //在綁定之前,把以前存在當前目錄下的套接字刪除
41
42 result = bind(server_socketfd, (struct sockaddr*)&server_address, sizeof(server_address)); //綁定
43 if(result != 0){
44 perror("bind");
45 exit(EXIT_FAILURE);
46 }
47
48 result = listen(server_socketfd, 5);//監聽,最多允許5個連接請求
49 if(result != 0){
50 perror("listen");
51 exit(EXIT_FAILURE);
52 }
53
54 client_len = sizeof(client_address);
55 while(1){ //開始進入無限循環
56 /* printf("If you want to quit, please enter 'quit'\n");
57 printf("Do you want to accept a connectiong\n");
58 memset(buffer, '\0', sizeof(buffer));
59 fgets(buffer, sizeof(buffer), stdin);
60 if((strncmp("quit", buffer, 4))==0) break; */
61
62 client_socketfd = accept(server_socketfd, (struct sockaddr*)&client_address, &client_len); //接受一個連接請求
63
64 /* result = pthread_attr_init(&thread_attr);
65 if(result != 0){
66 perror("pthread_attr_init");
67 exit(EXIT_FAILURE);
68 }
69 result = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
70 if(result != 0){
71 perror("pthread_attr_setdetachstate");
72 exit(EXIT_FAILURE);
73 } */
74 result = pthread_create(&a_thread, NULL, pthread_function, (void *)client_socketfd); //成功接受一個請求後,就會創建一個線程,然後主線程又進入accept函數,如果此時沒有連接請求,那麼主線程會阻塞
75 if(result != 0){
76 perror("pthread_create");
77 exit(EXIT_FAILURE);
78 }
79
80 }
81 }
82
83 void *pthread_function(void *arg){ //線程入口函數,每調用一次pthread_create,都會創建一個新的線程
84 int fd = (int) arg; //把函數參數,即連接成功後的套接字,賦給fd.
85 int result;
86 fd_set read_fds; //文件描述符集合,用於select函數
87 int max_fds; //文件描述符集合的最大數
88
89 printf("%d id has connected!!\n", fd);
90 while (1){
91
92 FD_ZERO(&read_fds);//清空集合
93 FD_SET(0, &read_fds);//將標准輸入放入監聽的文件描述符集合, 這個用於讀取標准輸入,即鍵盤的輸入
94 FD_SET(fd, &read_fds);//將連接後的客戶文件描述符放入監聽的文件描述符集合, 這個用於向客戶端讀取數據
95 max_fds = fd + 1;
96
97 // sem_wait(&bin_sem);
98 pthread_mutex_lock(&work_mutex); //對關鍵區域上鎖
99 printf("%d has get the lock\n", fd);
100 result = select(max_fds, &read_fds, (fd_set *)NULL, (fd_set *)NULL, (struct timeval*)NULL); //開始監聽那些文件描述符出於可讀狀態
101 if(result < 1){
102 printf("select");
103 }
104 if(FD_ISSET(0, &read_fds)){ //如果標准輸入處於可讀狀態,說明鍵盤有所輸入,將輸入的數據存放在buffer中,然後向客戶端寫回,如果輸入“quit”將會退出一個聊天線程
105 memset(buffer, '\0', sizeof(buffer)); //保險起見,清零
106 fgets(buffer, sizeof(buffer), stdin);
107 if((strncmp("quit", buffer, 4))==0){
108 printf("You have terminaled the chat\n");
109 // sem_post(&bin_sem);
110 pthread_mutex_unlock(&work_mutex);
111 break;
112 }
113 else{
114 result=write(fd, buffer, sizeof(buffer));
115 if(result==-1){
116 perror("write");
117 exit(EXIT_FAILURE);
118 }
119 }
120 }
121 if(FD_ISSET(fd, &read_fds)){ //如果客戶套接字符可讀,那麼讀取存放在buffer中,然後顯示出來,如果對方中斷聊天,那麼result==0
122 memset(buffer, '\0', sizeof(buffer));
123 result = read(fd, buffer, sizeof(buffer));
124 if(result == -1){
125 perror("read");
126 exit(EXIT_FAILURE);
127 }
128 else if(result == 0){
129 printf("The other side has terminal the chat\n");
130 // sem_post(&bin_sem);
131 pthread_mutex_unlock(&work_mutex);
132 break;
133 }
134 else{
135 printf("receive message: %s", buffer);
136 }
137 }
138 pthread_mutex_unlock(&work_mutex); //解鎖
139 sleep (1); //如果沒有這一行,當前線程會一直占據buffer.讓當前線程暫停一秒可以實現1對N的功能。
140 // sem_post(&bin_sem);
141 // sleep (1);
142 }
143 // printf("I am here\n");
144 close(fd);
145 pthread_exit(NULL);
146
147 }
148

這個服務端可以同時對N個人進行聊天,盡管有些bug(如果客戶端對方回復太快太頻繁,服務端的鎖就會切換來切換去,無法回復到正確的客戶端)。

客戶端跟服務端很像,但比較簡單。這裡面就不注釋了。這兩個程序我都運行過。。。沒什麼基本大的問題。。但是功能很不完善。。。還需改進。。。。。

客戶端代碼

 1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/socket.h>
4 #include<sys/un.h>
5 #include<string.h>
6 #include<sys/types.h>
7 #include<sys/time.h>
8
9 int main(){
10 int result;
11 int socketfd;
12 int len;
13 struct sockaddr_un address;
14 fd_set read_fds, test_fds;
15 int fd;
16 int max_fds;
17 char buffer[1024];
18
19 socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
20
21 address.sun_family = AF_UNIX;
22 strcpy(address.sun_path, "server_socket");
23 len = sizeof(address);
24
25 result = connect(socketfd, (struct sockaddr*)&address, len);
26 if(result == -1){
27 perror("connect");
28 exit(EXIT_FAILURE);
29 }
30
31 FD_ZERO(&read_fds);
32 FD_SET(0, &read_fds);
33 FD_SET(socketfd, &read_fds);
34 max_fds = socketfd +1;
35
36 printf("Chat now!!\n");
37
38 while(1){
39 test_fds = read_fds;
40 result = select(max_fds, &test_fds, (fd_set *)NULL, (fd_set *)NULL, (struct timeval*)NULL);
41 if(result < 1){
42 perror("select");
43 exit(EXIT_FAILURE);
44 }
45
46 if(FD_ISSET(0, &test_fds)){
47 memset(buffer, '\0', sizeof(buffer));
48 // printf("send:");
49 fgets(buffer, sizeof(buffer), stdin);
50 if((strncmp("quit", buffer, 4))== 0){
51 printf("\nYou are going to quit\n");
52 break;
53 }
54 result = write(socketfd, buffer, sizeof(buffer));
55 if(result == -1){
56 perror("write");
57 exit(EXIT_FAILURE);
58 }
59 }
60 if(FD_ISSET(socketfd, &test_fds)){
61 memset(buffer, '\0', sizeof(buffer));
62 result = read(socketfd, buffer, sizeof(buffer));
63 if(result == -1){
64 perror("read");
65 exit(EXIT_FAILURE);
66 }else if(result == 0){
67 printf("The other side has termianl chat!\n");
68 break;
69 }else{
70 printf("recieve: %s", buffer);
71 }
72 }
73 }
74 close(socketfd);
75 exit(EXIT_SUCCESS);
76 }
77
Copyright © Linux教程網 All Rights Reserved