select服务器编程综合
来源:互联网 发布:淘宝分享有礼的红包 编辑:程序博客网 时间:2024/06/06 08:25
一、服务器端代码
#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<unistd.h>#include<stdlib.h>#include<netinet/in.h>#include<string.h>#include<sys/time.h>static void Usage(const char *str){ printf("usage: %s [server_ip][server_port]\n",str);}static int startup(const char *ip,int port){ int new_socket = socket(AF_INET,SOCK_STREAM,0); if(new_socket < 0) { perror("socket"); exit(2); } int op = 1; int ret = setsockopt(new_socket,SOL_SOCKET,SO_REUSEADDR,&op,sizeof(op)); if(ret < 0) { perror("setsockopt"); exit(3); } struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); serv_addr.sin_addr.s_addr = inet_addr(ip); ret = bind(new_socket,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); if(ret < 0) { perror("bind"); exit(4); } ret = listen(new_socket,128); if(ret < 0) { perror("listen"); exit(5); } return new_socket;}int array_fds[1024]; //定义一个全局数组。这个全局变量存放的是文件描述符。int max_fd; //最大的文件描述符。int main(int argc ,char *argv[]){ if(argc != 3) { Usage(argv[0]); exit(1); } int i = 0; for(; i < 1024; ++i) array_fds[i] = -1; //将数组中每一个元素都置为-1. int listen_sock = startup(argv[1],atoi(argv[2]) ); array_fds[0] = listen_sock; //将监听套件字放在数组中0号位置。 fd_set reads; //创建只读集合。 struct timeval timeout; while(1) //服务器一直处于服务状态。 { // int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);后面四个参数都是输入输出参数。 FD_ZERO(&reads); //因为select是输入输出参数,当select返回时,已经改变了read这个集合了,下次还需要监听read这个集合中的可读事件的话就需要重新赋值。 max_fd = -1; //每次这个得重新初始化 timeout.tv_sec = 10; //定时10秒,这个参数也是输入输出参数 timeout.tv_usec = 0; for(i = 0; i < 1024; ++i)//遍历第三方数组 { if(array_fds[i] >= 0) { FD_SET(array_fds[i], &reads); //将监听套接字加入到可读事件中。 if(array_fds[i] > max_fd) max_fd = array_fds[i]; } } //准备工作做好后,开始真正的监听了。 int j = 0; switch(select(max_fd+1 ,&reads,NULL,NULL,&timeout)) { case 0: printf("time out....\n"); break; case -1: perror("select"); exit(6); default: //有可读事件发生,但是不知道是那一个可读事件,需要遍历数组,查看数组中存放的描述符那一个可读了。 for(; j < 1024; ++j) { if(array_fds[j] < 0) //-1表示这个文件描述符没有可读事件发生。 continue; char buf[BUFSIZ]; //接收数据缓冲区。 if(j== 0 && FD_ISSET(array_fds[0],&reads)) //监听套接字有可读事件发生,表示有客户连接了。 { struct sockaddr_in clie_addr; socklen_t len = sizeof(clie_addr); int connect_fd = accept(array_fds[0],(struct sockaddr*)&(clie_addr),&len); if(connect_fd < 0) { perror("accept"); continue; //这次连接失败,让它下次连接。 } printf("get a new client :(%s:%d)\n",inet_ntoa(clie_addr.sin_addr),ntohs(clie_addr.sin_port)); //连接套接字后继续监听,看这个套接字是不是有数据发送。 int k = 0; for(; k < 1024; ++k) { if(array_fds[k] == -1) { array_fds[k] = connect_fd; break; } } } //if else if(j != 0 && FD_ISSET(array_fds[j],&reads)) { printf("======================read start==========\n"); ssize_t s = read(array_fds[j],buf,sizeof(buf) - 1); if(s < 0) { perror("read"); close(array_fds[j]); array_fds[j] = -1; //数组重新利用 break; } else if(s == 0) { printf("clinet quit\n"); close(array_fds[j]); array_fds[j] = -1; //数组重新利用 break; } else { buf[s] = 0; printf("clinet say:%s\n",buf); } } }//for结束 } //switch结束 } //while(1)死循环 return 0;}
二、客户端代码
#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<unistd.h>#include<stdlib.h>#include<netinet/in.h>#include<string.h>#include<sys/time.h>#include<fcntl.h>#include<sys/stat.h>static void Usage(const char *str){ printf("usage: %s [server_ip][server_port]\n",str);}int main(int argc, char*argv[] ){ if(argc != 3) { Usage(argv[0]); exit(1); } int new_socket = socket(AF_INET,SOCK_STREAM,0); if(new_socket < 0) { perror("socket"); exit(2); } struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(atoi (argv[2]) ); serv_addr.sin_addr.s_addr = inet_addr(argv[1]); int ret = connect(new_socket,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); if(ret < 0) { perror("connect"); exit(3); } char buf[BUFSIZ]; while(1) { printf("please enter#:"); fflush(stdout); ssize_t s = read(0,buf,sizeof(buf) - 1); if(s > 0) { int outfd = dup(1); //outfd 指向标志输出(保存标准输出好恢复)。 buf[s-1] = 0; //去掉换行符。 // write(new_socket,buf,strlen(buf)); dup2(new_socket, 1); //1号文件描述符去指向new_socket指向的内容了。 printf("%s",buf); //本来是将buf中的东西写入到标准输出中,但是现在1号描述符已经重新定向了,指向套接字的缓冲区,所以现在就是讲buf中的东西写入到套接字缓冲区了。 fflush(stdout); dup2(outfd, 1); //让1重新指向标准输出。 } else break;// ssize_t s2 = read(new_socket,buf,sizeof(buf) - 1);// buf[s2] = 0;// printf("sever echo # %s\n",buf); } close(new_socket); return 0;}
三、与其他类型服务器对比
多线程模型适用于处理短连接,且连接的打开关闭非常频繁的情形,但不适合处理长连接。线程间内存无法共享,因为所有线程处在同一个地址空间中。多进程模型擅长处理并发长连接,但却不适用于连接频繁产生和关闭的情形select服务器优点(1)select()的可移植性更好(2)select() 对于超时值提供了更好的精度:微秒,而poll是毫秒缺点(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大 (2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大 (3)select支持的文件描述符数量太小了,默认是1024
阅读全文
0 0
- select服务器编程综合
- poll服务器编程综合
- e-poll服务器编程综合
- linux服务器编程之select
- C++网络编程服务器select模型(参考)
- Linux网络编程之select服务器
- 使用select.select编写聊天室服务器 《Python网络编程攻略》
- 《Linux网络编程》综合案例web服务器shttpd
- Select服务器
- select服务器
- select服务器
- select服务器
- select服务器
- select服务器
- select服务器
- select服务器
- select服务器
- select服务器
- 新工作自述-1
- C语言中计算机概念
- 使用jvisualvm、jmc远程监控JVM
- 深入浅出ES6(二):迭代器和for-of循环
- Mac下使用simiki搭建个人wiki
- select服务器编程综合
- vertical-align
- 生产环境部署hadoop
- jQuery去除子元素
- (iOS)简单的瀑布流布局
- IIS优化网站性能二
- 安装jenkins
- Virtual SVN + Ubuntu 访问 SSL handshake failed
- 深入浅出ES6(三):生成器 Generators