linux socketチャットルーム

34776 ワード

linux socketチャットルームは、もともと自分でやるべきことではなく、他の人の宿題を完成させるためです.ちょうど最近の授業はsocketプログラミングについてですが、手の練習に来ませんか.
SOcketベースのチャットルームは、入学当初から似たようなことをしていたが、当時使っていたjavaで実現されただけで、正式にSOcketを学んだことがないため、コードは他人のものを転用しただけで、深く理解していなかった.
単一ユーザ-サービスの会話は、マルチユーザ-サービスであっても、連続サービスでない限り、サービス側はポーリングによって複数のユーザにサービスを提供することができる.問題は、一般的なsocket I/O関数の多くがブロックされていることです.これは、単一のスレッドが1人のユーザーにしかサービスできないことを意味します.したがって、マルチスレッドを使用することは自然に考えられますが、マルチスレッドは最適な解決策ではありません.結局、頻繁にスレッドを作成し、破棄すると、一定の浪費をもたらします.selectの多重化を利用すれば、よりよく解決できます.
クライアント実装
クライアントはよく実現され、2つのI/Oだけが通信し、server socketとstdinであり、fd値が固定されており、maxfdpは直接socket+1を取ればよい.
 1 #include <stdio.h>

 2 #include <stdlib.h>

 3 #include <string.h>

 4 #include <stdbool.h>

 5 #include <unistd.h>

 6 #include <sys/socket.h>

 7 #include <arpa/inet.h>

 8 

 9 #define BUF_SIZE 256 //      

10 #define STDIN 0 // stdinfd

11 #define STDOUT 1 // stdoutfd

12 #define INVALID -1

13 

14 /**    socket     ,       */

15 int socket_setup(const char *serv_ip, int serv_port)

16 {

17     int rtn, sockfd = socket(AF_INET, SOCK_STREAM, 0);

18     struct sockaddr_in sockaddr;

19 

20     bzero(&sockaddr, sizeof(sockaddr));

21     sockaddr.sin_family = AF_INET;

22     sockaddr.sin_port = htons(serv_port);

23     inet_pton(AF_INET, serv_ip, &sockaddr.sin_addr);

24 

25     rtn = connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));

26 

27     if (rtn == INVALID)

28     {

29         puts("Connection failure");

30         exit(1);

31     }

32     else

33     {

34         puts("Connection successful");

35         return sockfd;

36     }

37 }

38 

39 int main(int argc, const char *argv[])

40 {

41     int i, read_size, sockfd = socket_setup(argv[1], atoi(argv[2]));

42     char buffer[BUF_SIZE];

43     fd_set fdset;

44 

45     while (true)

46     {

47         FD_ZERO(&fdset);

48         FD_SET(STDIN, &fdset);

49         FD_SET(sockfd, &fdset);

50         select(sockfd + 1, &fdset, NULL, NULL, NULL);

51 

52         /** socket ->      */

53         if (FD_ISSET(sockfd, &fdset))

54         {

55             read_size = read(sockfd, buffer, BUF_SIZE);

56             write(STDOUT, buffer, read_size);

57 

58             if (read_size == 0)

59             {

60                 puts("Server close");

61                 exit(1);

62             }

63         }

64 

65         /**      -> socket */

66         if (FD_ISSET(STDIN, &fdset))

67         {

68             read_size = read(STDIN, buffer, BUF_SIZE);

69             write(sockfd, buffer, read_size);

70         }

71     }

72 

73     return 0;

74 }

サービス側実装
このチャットルームの実現の難点はサービス側であり,マルチユーザチャットをサポートできることである.
私の実装方法は,接続された顧客情報を1つの構造体配列clientsで保存し,配列を巡る方式でメッセージをブロードキャストすることであり,一般的にはこの点を考えることができ,selectによる多重化に重点を置いている.
お客様のsocketはダイナミックなので、お客様の接続/切断に注意して処理する必要があります.ユーザー接続がある場合はsocketを保存し、接続を切断した後、socket値を無効にします.
  1 #include <stdio.h>

  2 #include <stdlib.h>

  3 #include <string.h>

  4 #include <stdbool.h>

  5 #include <unistd.h>

  6 #include <time.h>

  7 #include <sys/socket.h>

  8 #include <arpa/inet.h>

  9 

 10 #define TIME_SIZE 16 //           

 11 #define IP_SIZE 16 // IP      

 12 #define BUF_SIZE 256 //      

 13 #define CLIENT_SIZE 8 //         

 14 #define BACKLOG CLIENT_SIZE // listen     ,          

 15 #define INVALID -1

 16 

 17 /**              */

 18 struct CLIENT {

 19     int clientfd;

 20     struct sockaddr_in sockaddr;

 21     char ip[IP_SIZE];

 22     int port;

 23 } clients[CLIENT_SIZE];

 24 

 25 /**          */

 26 void init_clients(void)

 27 {

 28     int i;

 29 

 30     for (i = 0; i< CLIENT_SIZE; i++)

 31         clients[i].clientfd = INVALID;

 32 }

 33 

 34 /**                */

 35 void broadcast(char *msg)

 36 {

 37     int i;

 38 

 39     for (i = 0; i< CLIENT_SIZE; i++)

 40         if (clients[i].clientfd != INVALID)

 41             write(clients[i].clientfd, msg, strlen(msg));

 42 }

 43 

 44 /**           */

 45 void strfmsg(int i, char *buffer, const char *msg)

 46 {

 47     char curtime[TIME_SIZE];

 48     time_t curtime_t;

 49     struct tm *timeinfo;

 50 

 51     time(&curtime_t);

 52     timeinfo = localtime(&curtime_t);

 53     strftime(curtime, TIME_SIZE, "%X", timeinfo);

 54 

 55     sprintf(

 56         buffer,

 57         "<%s %s:%d> %s",

 58         curtime,

 59         clients[i].ip,

 60         clients[i].port,

 61         msg);

 62 }

 63 

 64 /**       */

 65 void accept_connect(int listenfd)

 66 {

 67     int connectfd, i;

 68     char buffer[BUF_SIZE];

 69     struct sockaddr_in clientaddr;

 70     socklen_t connectlen = sizeof(struct sockaddr_in);

 71 

 72     connectfd = accept(

 73         listenfd,

 74         (struct sockaddr *)&clientaddr,

 75         &connectlen);

 76 

 77     /**         */

 78     for (i = 0; i < CLIENT_SIZE; i++)

 79     {

 80         if (clients[i].clientfd == INVALID)

 81         {

 82             clients[i].clientfd = connectfd;

 83             memcpy(&clients[i].sockaddr, &clientaddr, connectlen);

 84             clients[i].port = ntohs(clients[i].sockaddr.sin_port);

 85             inet_ntop(

 86                 AF_INET,

 87                 &clients[i].sockaddr.sin_addr,

 88                 clients[i].ip,

 89                 IP_SIZE);

 90 

 91             strfmsg(i, buffer, "login
"); 92 printf("%s", buffer); 93 broadcast(buffer); 94 95 break; 96 } 97 } 98 99 /** */ 100 if (i == CLIENT_SIZE) 101 { 102 strcpy(buffer, "Out of Number
"); 103 write(connectfd, buffer, strlen(buffer)); 104 close(connectfd); 105 } 106 } 107 108 /** */ 109 void chat(fd_set fdset) 110 { 111 int sockfd, read_size, i; 112 char read_buf[BUF_SIZE], send_buf[BUF_SIZE]; 113 114 for (i = 0; i < CLIENT_SIZE; i++) 115 { 116 sockfd = clients[i].clientfd; 117 118 if (sockfd != INVALID && FD_ISSET(sockfd, &fdset)) 119 { 120 read_size = read(sockfd, read_buf, BUF_SIZE - 1); 121 122 if (read_size == 0) 123 { 124 /** */ 125 close(sockfd); 126 clients[i].clientfd = INVALID; 127 128 strfmsg(i, send_buf, "logout
"); 129 printf("%s", send_buf); 130 broadcast(send_buf); 131 132 continue; 133 } 134 else 135 { 136 read_buf[read_size] = '\0'; 137 strfmsg(i, send_buf, read_buf); 138 printf("%s", send_buf); 139 broadcast(send_buf); 140 } 141 } 142 } 143 } 144 145 /** socket , */ 146 int socket_setup(int port) 147 { 148 int rtn, listenfd = socket(AF_INET, SOCK_STREAM, 0); 149 struct sockaddr_in sockaddr; 150 151 bzero(&sockaddr, sizeof(sockaddr)); 152 sockaddr.sin_family = AF_INET; 153 sockaddr.sin_port = htons(port); 154 sockaddr.sin_addr.s_addr= htonl(INADDR_ANY); 155 156 rtn = bind(listenfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); 157 158 if (rtn == INVALID) 159 { 160 puts("Bind failure"); 161 exit(1); 162 } 163 164 if (listen(listenfd, BACKLOG) == INVALID) 165 { 166 puts("Listen failure"); 167 exit(1); 168 } 169 170 puts("Service startup"); 171 return listenfd; 172 } 173 174 int main(int argc, const char *argv[]) 175 { 176 int maxfdp, i, listenfd = socket_setup(atoi(argv[1])); 177 fd_set fdset; 178 179 init_clients(); 180 181 while (true) 182 { 183 FD_ZERO(&fdset); 184 FD_SET(listenfd, &fdset); 185 maxfdp = listenfd; 186 187 /** socket fdset, maxfdp */ 188 for (i = 0; i < CLIENT_SIZE; i++) 189 { 190 if (clients[i].clientfd != INVALID) 191 { 192 FD_SET(clients[i].clientfd, &fdset); 193 194 if (clients[i].clientfd > maxfdp) 195 maxfdp = clients[i].clientfd; 196 } 197 } 198 199 select(maxfdp + 1, &fdset, NULL, NULL, NULL); 200 201 if (FD_ISSET(listenfd, &fdset)) 202 accept_connect(listenfd); 203 204 chat(fdset); 205 } 206 207 return 0; 208 }