【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
- 【Linux网络编程】基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型
- Linux网络编程【五】:TCP协议高性能服务器(http)模型之I/O多路转接select
- Linux网络编程【六】:TCP协议高性能服务器(http)模型之I/O多路转接epoll
- Linux网络编程【七】:TCP协议高性能服务器(http)模型之I/O多路转接poll
- 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器
- 【Linux网络编程】I/O多路转接之 epoll 高性能简洁http服务器模型
- Linux【网络编程】——I/O多路转接之Select服务器
- Linux的I/O多路转接模型和select()
- I/O多路转接----select的服务器实现
- Linux c==网络编程、循环服务器、并发服务器、I/O多路转接 (23)
- Linux c==网络编程、循环服务器、并发服务器、I/O多路转接
- 【Linux】I/O多路转接select
- Linux【网络编程】——I/O多路转接之epoll服务器
- Linux【网络编程】——I/O多路转接之Poll服务器
- Linux高性能服务器编程---I/O模型
- I/O的多路转接: select
- 使用Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- Tensorflow基础知识学习
- php入门学习------php循环&开关语句
- 使用gdb进行调试
- DS18B20 单总线多器件的ROM 搜索, ALARM 检测, CRC 校验 源码实现, 基于 STM32F103
- js数组去重的三种常用方法总结
- 【Linux网络编程】基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型
- centos7配置hadoop伪分布式
- Angular学习:模块和组件的动态加载
- win7系统右键怎么添加管理员权限?
- url中的参数带有&连接符号处理 url参数带路径问题
- 如何给Myeclipse设置个性化的代码注释模板?
- Service(二)
- GCC 参数详解
- <第一章>Python环境配置