天天看點

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

功能描述:程式應用多線程技術,可是實作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 elseif(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

讀者可以對比一下http://blog.csdn.net/hwz119/archive/2007/03/19/1534233.aspx

讀者可以發現,連結網絡中的程式需要結束目前一個聊天才能進行下一個聊天,而這個服務端可以同時對N個人進行聊天,盡管有些bug(如果用戶端對方回複太快太頻繁,服務端的鎖就會切換來切換去,無法回複到正确的用戶端)。

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

用戶端代碼

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 }elseif(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

繼續閱讀