博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
回射程序改进3——消息的群发
阅读量:4876 次
发布时间:2019-06-11

本文共 8954 字,大约阅读时间需要 29 分钟。

前文列表:

 

目的:

设计一个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                 else123                 {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_io108 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数据类型中的描述符,描述符集内任何与未就绪描述符对应的位返回时均置为1131         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数组中顺序寻找第一个未被使用的元素,用于保存新的connfd149             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数组中没有可用元素来保存新的connfd159             if (i == FD_SETSIZE)160             {161                 printf("Error too many clients!\n");162                 exit(1);163             }164 165             // 将新的connfd加入allset166             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的最大index177             }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                 else209                 {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, MingMing:Hi, Lin

客户端:

[wangml@iZwz976helaylvgqok97prZ net]$ ./cli 120.24.55.49 LinLinSuccess init, the connected socket is 3Hello, MingMing:Hi, Lin[wangml@iZwz976helaylvgqok97prZ net]$ ./cli 120.24.55.49 LinLinSuccess init, the connected socket is 3Hello, MingMing:Hi, Lin

 

转载于:https://www.cnblogs.com/lnlin/p/9609023.html

你可能感兴趣的文章
几种方法的尾递归实现
查看>>
php qq第三方登陆
查看>>
添加共享文件夹
查看>>
左值与右值,左值引用与右值引用(C++11)
查看>>
错误:没有为扩展名“.html”注册的生成提供程序。
查看>>
javascript 中 with 的使用
查看>>
集合并卷积
查看>>
【BZOJ4998】星球联盟
查看>>
【BZOJ5015】[Snoi2017]礼物
查看>>
编程如写作
查看>>
问题账户需求分析
查看>>
平衡搜索树
查看>>
php工厂模式学习笔记
查看>>
MySQL表操作
查看>>
用PHP计算相对路径
查看>>
Windows Controls and WPF UserControls Inside an Xna Game Project
查看>>
整型表示
查看>>
OC基础9:预处理程序
查看>>
uva 11181 - Probability|Given
查看>>
【 D3.js 入门系列 --- 9.6 】 生产的包图
查看>>