错误的尝试:回射程序改进2
在回射程序的基础上,设计一个类似于群聊的应用
与改进1相比增加的设计:
1.将每个客户端发出的消息增加一个字符串,用于表示发送者的身份
2.服务端将收到的每个客户端消息转发给其他已连接的客户端套接字
改进1:https://www.cnblogs.com/lnlin/p/9568279.html
改进的思考过程:
1.消息头的增加:
增加一个客户端mian函数参数,用于表示用户名。
将每个客户端用户名添加到客户端发送的消息头部来表名某个消息发送者的身份,添加方式:
a.在客户端在标准输入输入消息后,客户端自动将此客户端用户用户名字符串添加到消息头部,再将
添加了用户名的消息发送给服务端
b.在客户端与服务端建立连接后,将客户端用户名发送给服务端,服务端保存此用户名,并在转发客
户端消息时,将相应用户名添加到相应消息数据中
下面的实现选用 a 方法,原因是我考虑到这么做虽然增加了客户端发送给服务端的消息长度,但减少
了客户端所需要的操作。
客户端main函数:
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(sockfd, argv[2]); 18 19 return 0; 20 }
将一个字符串添加到另一个字符串头部:
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 }
与改进1相比变化的cli_io函数:
64 // 回射程序客户端对套接字的读写 65 void cli_io(int sockfd, char *mark) 66 { 67 int n; 68 char sendline[MAXLINE], recvline[MAXLINE]; 69 70 while (fgets(sendline, MAXLINE, stdin) != NULL) 71 { 72 // 将用户名添加到消息头部 73 addStrHead(mark, sendline); 74 75 if (write(sockfd, sendline, strlen(sendline)) < 0) 76 { 77 printf("Error write!\n"); 78 exit(1); 79 } 80 81 if ( (n = read(sockfd, recvline, MAXLINE)) > 0 ) 82 { 83 recvline[n] = '\0'; 84 fputs(recvline, stdout); 85 } 86 } 87 88 return; 89 }
服务端改进:
将从客户端收到的消息转发自其他已连接套接字
错误的尝试:在使用fork创建子进程的方式来处理每个自连接的基础上,在服务端添加一个结构用于保
存所有连接过的套接字与相应套接字的存活状态,每个子进程在收到此消息后通过此套接字列表转发消
息。
错误原因思考:如果父进程先创建子进程,后父进程生成socket,这些socket和子进程无关
服务端代码:
1 #include "net.h" 2 3 4 int main(int argc, char **argv) 5 { 6 int listenfd, connfd, n; 7 struct sockaddr_in cliaddr; 8 pid_t childpid; 9 socklen_t clilen; 10 11 // 为所有与服务器建立连接的套接字创建一个存储数组 12 struct sockConn socklist[MAXSOCKET]; 13 int cntSockfd = 0; 14 listenfd = tcp_listen(SERV_PORT); 15 for ( ; ; ) 16 { 17 clilen = sizeof(cliaddr); 18 19 if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0 ) 20 { 21 printf("Error accept!\n"); 22 exit(0); 23 } 24 25 printf("accept: %d\n", connfd); 26 27 addSockfd(connfd, socklist, &cntSockfd); 28 29 if ( (childpid = fork()) == 0 ) 30 { 31 if (close(listenfd) < 0) 32 { 33 printf("Error close!\n"); 34 exit(1); 35 } 36 37 serv_io(connfd, socklist, &cntSockfd); 38 exit(0); 39 } 40 } 41 }
所用函数:
35 // 将从某一客户端接收的数据群发到所有已连接的客户(套接字) 36 void send2All(char *message, int itself, struct sockConn *socklist, int *cntSockfd) 37 { 38 int i; 39 40 for (i = 0; i < *cntSockfd; i++) 41 { 42 if (socklist[i].used && socklist[i].sockfd != itself) 43 { 44 if (write(socklist[i].sockfd, message, strlen(message)) < 0) 45 { 46 printf("Error write!\n"); 47 exit(1); 48 } 49 } 50 } 51 52 return; 53 } 54 55 // 服务端io 56 void serv_io(int connfd, struct sockConn *socklist, int *cntSockfd) 57 { 58 char buff[MAXLINE]; 59 int n; 60 61 while ( (n = read(connfd, buff, MAXLINE)) > 0 ) 62 { 63 buff[n] = '\0'; 64 fputs(buff, stdout); 65 66 if (write(connfd, buff, strlen(buff)) < 0) 67 { 68 printf("Error write!\n"); 69 exit(1); 70 } 71 72 //send2All(buff, connfd, socklist, cntSockfd); 73 } 74 75 return; 76 } 77 78 // 在accept后将已连接套接字加入socklist,并将已连接过的套接字数加一 79 void addSockfd(int sockfd, struct sockConn *socklist, int *cntSockfd) 80 { 81 socklist[*cntSockfd].sockfd = sockfd; 82 socklist[*cntSockfd].used = 1; 83 *cntSockfd++; 84 85 return; 86 } 87
转载请注明出处