【Linux网络编程】基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型

来源:互联网 发布:淘宝衣服平铺拍摄技巧 编辑:程序博客网 时间:2024/05/23 13:22




服务端代码:

myselect.c

#include <stdio.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/socket.h>#include <sys/types.h>#include <sys/stat.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#define ARRAY_SIZE 1024/* 使用说明 */void usage(const char* proc){printf("%s usage : [server_ip] [ server_port]\n", proc);}/* 初始化一个socket连接 */int init_sock(const char* _ip, int _port){int sock = socket(AF_INET, SOCK_STREAM, 0);if(sock < 0){perror("socket");exit(2);}int flg = 1;setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flg, sizeof(flg));struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip);if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0){perror("bind");exit(3);}if(listen(sock , 10) < 0){perror("listen");exit(4);}return sock;}/* 等待事件发生并处理 */void run_select(int listen_sock){int array_fds[ARRAY_SIZE];  /* 保存所有关心的描述符  */array_fds[0] = listen_sock; /* 将listen_sock 保存*//* 初始化为 array_fds */int idx_init = 1;for(idx_init = 1; idx_init < ARRAY_SIZE; ++idx_init)array_fds[idx_init] = -1;fd_set rfds;      /* 关心的读事件描述符集 */fd_set wfds;      /* 关心的写事件描述符集 */int max_fd = -1;struct timeval _timeout = {1, 0};while(1){max_fd = -1;_timeout.tv_sec = 1;FD_ZERO(&rfds);   FD_ZERO(&wfds);/* 一. 将数组中存储的描述符,添加到关心的集合中  *  并找出其中最大的描述符, */int idx_add = 1;for(idx_add = 0; idx_add < ARRAY_SIZE; idx_add++){if(array_fds[idx_add] > 0){FD_SET(array_fds[idx_add], &rfds);FD_SET(array_fds[idx_add], &wfds);if(array_fds[idx_add] > max_fd)max_fd = array_fds[idx_add];}}/* 二. 检测select的返回情况*/int select_ret = select(max_fd+1, &rfds, &wfds, NULL, &_timeout);switch( select_ret ){case 0:printf("timeout\n");break;case -1:perror("select\n");break;default:{/* 遍历数组,检测事件发生的描述符,并响应  */int idx_check = 0;for(idx_check = 0; idx_check < ARRAY_SIZE; ++idx_check){if(array_fds[idx_check] < 0)continue;if(idx_check == 0 && FD_ISSET(array_fds[idx_check], &rfds) ){ /* listensock 有数据来,表示有新的连接请求 */struct sockaddr_in client;socklen_t len = sizeof(client);int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);if(new_sock > 0){printf("新的连接请求来自: ip = %s, port = %d \n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));/* 在数组中找到未被占用的描述符位置,并保存新连接的描述符 */int idx_find = 0;for(idx_find = 1; idx_find < ARRAY_SIZE; ++idx_find){if(array_fds[idx_find] < 0){array_fds[idx_find] = new_sock;break;}}if(idx_find == ARRAY_SIZE)close(new_sock);}} // << end if idx_check == 0 && FD_ISSET -- rfds>> else if( idx_check != 0 &&  FD_ISSET(array_fds[idx_check], &rfds) ){ /* 其余描述符有数据来*/ char buf[ 1024];memset(buf, '\0', sizeof(buf));ssize_t s = read(array_fds[idx_check], buf, sizeof(buf) - 1);if(s > 0){printf("client say : %s\n", buf);if(FD_ISSET(array_fds[idx_check], &wfds))write(array_fds[idx_check], buf, sizeof(buf)-1);}else if(s == 0){printf("client quit\n");close(array_fds[idx_check]);array_fds[idx_check] = -1;}else{perror("read fail");close(array_fds[idx_check]);array_fds[idx_check] = -1;}} // << end else if idx_check != 0 && FD_ISSET -- rfds >>} // << end for idx_check = 0; idx_check<ARRAY_SIZE; ++idx_check >>} // << end default >>break;} // << end switch select >>} // << end while 1 >>}int main(int argc, char **argv){if(3 != argc){usage(argv[0]);exit(1);}int sock = init_sock(argv[1], atoi(argv[2]));printf("start run_select\n");run_select(sock);return 0;}



客户端代码:

为了练习dup 和 dup2 函数的使用,在客户端中,使用了这两个函数进行标准输出的重定向以及恢复,使用printf 函数向sockfd 中写数据,并提示用户输入。


#include <stdio.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <netinet/in.h>#include <sys/socket.h>#include <arpa/inet.h>#include <stdlib.h>int main(int argc, char **argv){if(argc != 3){printf("Usage [server_ip] [ server_port]\n");exit(1);}int sock = socket(AF_INET, SOCK_STREAM, 0);if(sock < 0){perror("socket");exit(2);}struct sockaddr_in server;server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(argv[1]);server.sin_port = htons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)&server, sizeof(server))<0){perror("connect");exit(3);}// 保存标准输出的描述符int oldfd = dup(STDOUT_FILENO);char buf[1024];while(1){printf("please input #");fflush(stdout);memset(buf, '\0', sizeof(buf));/* 重定向输出到sock */dup2(sock, STDOUT_FILENO);ssize_t s = read(0, buf, sizeof(buf)-1);if(s > 0){/* 处理客户端只输入一个回车而程序挂起的bug */if( buf[0] == '\n' ){dup2(oldfd, STDOUT_FILENO);continue;}if(strncmp("quit", buf, 4) == 0)break;buf[s - 1] = 0;/* 用printf 向sock写 */printf("%s", buf);fflush(stdout);    /* 恢复 标准输出 */    dup2(oldfd, STDOUT_FILENO);/* 从sock读服务端回显的数据,  */    ssize_t _s =  read(sock, buf, sizeof(buf) - 1);    if(_s >0)    {    buf[_s] = 0;    printf("server echo # %s\n", buf);    }    else if (s <= 0)    {    continue;    }}}close(sock);close(oldfd);return 0;}


阅读全文
0 0
原创粉丝点击