标签:设置 == nat sse 通知 mat adl stdout 程序
设计一个C/S程序,客户端发送/接收消息,服务端将从客户端接收到的消息群发给其它已连接套接字,产生
类似群聊的效果
1.客户端可以在服务端终止后得到通知
2.客户端使用shutdown()函数处理批量输入产生的问题
3.服务端使用select()函数管理套接字(单进程),而非使用fork()让每个子进程管理一个套接字(多进程)
客户端:
1 #include "net.h" 2 3 int main(int argc, char **argv) 4 { 5 int sockfd; 6 7 if (argc != 3) 8 { 9 printf("Error arg!\n"); 10 exit(1); 11 } 12 13 printf("%s\n", argv[2]); 14 15 sockfd = tcp_connect(argv[1], SERV_PORT); 16 printf("Success init, the connected socket is %d\n", sockfd); 17 cli_io_select(sockfd, argv[2], stdin); 18 printf("End...\n"); 19 20 return 0; 21 }
4 // 建立一个TCP套接字(IPv4),并与给定主机端口连接,并返回连接后的套接字 5 int tcp_connect(char *ser_ip, int port) 6 { 7 int sockfd; 8 struct sockaddr_in servaddr; 9 10 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) 11 { 12 printf("Error socket!\n"); 13 exit(1); 14 } 15 16 bzero(&servaddr, sizeof(servaddr)); 17 servaddr.sin_family = AF_INET; 18 servaddr.sin_port = htons(port); 19 20 if (inet_pton(AF_INET, ser_ip, &servaddr.sin_addr) <= 0) 21 { 22 printf("Error inet_pton!\n"); 23 exit(1); 24 } 25 26 if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) 27 { 28 printf("Error connect!\n"); 29 exit(1); 30 } 31 32 return sockfd; 33 }
35 // 将一个字符串放到另一个字符串的头部,构造将用户名加到客户发送的消息中 36 // 不提供对字符串空间大小的检查 37 char *addStrHead(char *head, char *row) 38 { 39 int headLen, rowLen, i; 40 headLen = strlen(head); 41 rowLen = strlen(row); 42 43 for (i = headLen + rowLen; i >= 0; i--) 44 { 45 if (i > headLen) 46 { 47 row[i] = row[i - headLen - 1]; 48 } 49 else if (i == headLen) 50 { 51 row[i] = ‘:‘; 52 } 53 else 54 { 55 row[i] = head[i]; 56 } 57 } 58 59 row[headLen + rowLen + 1] = ‘\0‘; 60 61 return row; 62 }
91 // 使用select的cli_io函数,使得在服务器进程终止后客户可以马上获取通知 92 void cli_io_select(int sockfd, char *mark, FILE *fp) 93 { 94 int maxfdp1, n, stdineof; 95 fd_set rset; 96 char sendline[MAXLINE], recvline[MAXLINE]; 97 98 FD_ZERO(&rset); 99 100 for ( ; ; ) 101 { 102 FD_SET(fileno(fp), &rset); 103 FD_SET(sockfd, &rset); 104 105 // fileno() 函数,将文件流指针转换为文件描述符· 106 maxfdp1 = max(fileno(fp), sockfd) + 1; 107 108 if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0) 109 { 110 printf("Error select!\n"); 111 exit(1); 112 } 113 114 if (FD_ISSET(sockfd, &rset)) 115 { 116 if ( (n = read(sockfd, recvline, MAXLINE)) == 0 ) 117 { 118 if (stdineof == 1) 119 { 120 return; 121 } 122 else 123 { 124 printf("Error server terminated prematurely!\n"); 125 exit(1); 126 } 127 } 128 129 if (write(fileno(stdout), recvline, n) < 0) 130 { 131 printf("Error write!\n"); 132 exit(1); 133 } 134 } 135 136 if (FD_ISSET(fileno(fp), &rset)) 137 { 138 if ( (n = read(fileno(fp), sendline, MAXLINE)) == 0 ) 139 { 140 stdineof = 1; 141 142 if (shutdown(sockfd, SHUT_WR) < 0) 143 { 144 printf("Error shutdown!\n"); 145 exit(1); 146 } 147 148 FD_CLR(fileno(fp), &rset); 149 continue; 150 151 return; 152 } 153 154 addStrHead(mark, sendline); 155 156 if (write(sockfd, sendline, (n + strlen(mark) + 1)) < 0) 157 { 158 printf("Error write!\n"); 159 160 } 161 } 162 } 163 } exit(1);
服务端:
1 #include "net.h" 2 3 int main(int argc, char **argv) 4 { 5 int listenfd; 6 listenfd = tcp_listen(SERV_PORT); 7 serv_io_select(listenfd); 8 }
3 // 创建一个tcp套接字,并在指定端口上监听 4 int tcp_listen(int port) 5 { 6 int listenfd; 7 struct sockaddr_in servaddr; 8 9 if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) 10 { 11 printf("Error socket!\n"); 12 exit(1); 13 } 14 15 bzero(&servaddr, sizeof(servaddr)); 16 servaddr.sin_family = AF_INET; 17 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 18 servaddr.sin_port = htons(port); 19 20 if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) < 0) 21 { 22 printf("Error bind!\n"); 23 exit(1); 24 } 25 26 if (listen(listenfd, LISTENQ) < 0) 27 { 28 printf("Error listen!\n"); 29 exit(1); 30 } 31 32 return listenfd; 33 }
88 void sendToOtherSocket(int *client, char *buf, int maxi, int index, int len) 89 { 90 int i; 91 92 for (i = 0; i <= maxi; i++) 93 { 94 if (i != index) 95 { 96 if (write(client[i], buf, len) < 0) 97 { 98 printf("Error write!\n"); 99 exit(1); 100 } 101 } 102 } 103 104 return; 105 } 106 107 // 使用select的serv_io 108 void serv_io_select(int listenfd) 109 { 110 int sockfd, connfd, maxfd, maxi, i, n, nready, client[FD_SETSIZE]; 111 struct sockaddr_in cliaddr; 112 socklen_t clilen; 113 fd_set rset, allset; 114 char buf[MAXLINE]; 115 116 maxfd = listenfd; 117 maxi = -1; 118 119 // 初始化 client 数组,将其所有元素设为 -1,表示这一位未使用 120 for (i = 0; i < FD_SETSIZE; i++) 121 { 122 client[i] = -1; 123 } 124 125 FD_ZERO(&allset); 126 FD_SET(listenfd, &allset); 127 128 for ( ; ; ) 129 { 130 rset = allset; // 使用allset是由于我们使用FD_ISSET来测试fd_set数据类型中的描述符,描述符集内任何与未就绪描述符对应的位返回时均置为1 131 if( (nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0 ) 132 { 133 printf("Error select!\n"); 134 exit(1); 135 } 136 137 // 有新的连接 138 if (FD_ISSET(listenfd, &rset)) 139 { 140 clilen = sizeof(cliaddr); 141 142 if( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0 ) 143 { 144 printf("Error accept!\n"); 145 exit(1); 146 } 147 148 // 在client数组中顺序寻找第一个未被使用的元素,用于保存新的connfd 149 for (i = 0; i < FD_SETSIZE; i++) 150 { 151 if (client[i] < 0) 152 { 153 client[i] = connfd; 154 break; 155 } 156 } 157 158 // client数组中没有可用元素来保存新的connfd 159 if (i == FD_SETSIZE) 160 { 161 printf("Error too many clients!\n"); 162 exit(1); 163 } 164 165 // 将新的connfd加入allset 166 FD_SET(connfd, &allset); 167 168 // 重新设置maxfd, 用于select函数的第一个参数 169 if (connfd > maxfd) 170 { 171 maxfd = connfd; 172 } 173 174 if (i > maxi) 175 { 176 maxi = i; // maxi指向client数组的可用client的最大index 177 } 178 179 // 除了有一个新连接外,没有其他描述符可读 180 if (--nready <= 0) 181 { 182 printf("continue!\n"); 183 continue; 184 } 185 } 186 187 for (i = 0; i <= maxi; i++) 188 { 189 if ( (sockfd = client[i]) < 0 ) 190 { 191 continue; 192 } 193 194 if (FD_ISSET(sockfd, &rset)) 195 { 196 if ( (n = read(sockfd, buf, MAXLINE)) == 0 ) 197 { 198 // 客户端关闭了此套接字 199 if (close(sockfd) < 0) 200 { 201 printf("Error close!\n"); 202 exit(1); 203 } 204 205 FD_CLR(sockfd, &allset); 206 client[i] = -1; 207 } 208 else 209 { 210 if (write(fileno(stdout), buf, n) < 0) 211 { 212 printf("Error write!\n"); 213 exit(1); 214 } 215 sendToOtherSocket(client, buf, maxi, i, n); 216 } 217 218 if (--nready <= 0) 219 { 220 break; 221 } 222 } 223 } 224 } 225 }
服务端:
[root@iZwz976helaylvgqok97prZ net]# ./serv continue! continue! Lin:Hello, Ming Ming:Hi, Lin
客户端:
[wangml@iZwz976helaylvgqok97prZ net]$ ./cli 120.24.55.49 Lin Lin Success init, the connected socket is 3 Hello, Ming Ming:Hi, Lin [wangml@iZwz976helaylvgqok97prZ net]$ ./cli 120.24.55.49 Lin Lin Success init, the connected socket is 3 Hello, Ming Ming:Hi, Lin
标签:设置 == nat sse 通知 mat adl stdout 程序
原文地址:https://www.cnblogs.com/lnlin/p/9609023.html