一个支持tcp多客户端连接的例子,轻松认识linux select函数的基本原理和使用方法
2015-08-28 17:22
756 查看
这本是libmodbus-3.0.6中的一个例子,觉得是学习select很好的例子,注释了一下分享出来!
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <modbus.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define NB_CONNECTION 5 modbus_t *ctx = NULL; int server_socket; modbus_mapping_t *mb_mapping; static void close_sigint(int dummy) { close(server_socket); modbus_free(ctx); modbus_mapping_free(mb_mapping); exit(dummy); } int main(void) { int master_socket; int rc; fd_set refset; //fd_set现在一共可以监听1024个文件描述符 fd_set rdset; /*文件描述符的最大值,要保证fdmax是需要监视的最大的文件描述符值*/ int fdmax; ctx = modbus_new_tcp("127.0.0.1", 1502); mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, MODBUS_MAX_READ_REGISTERS, 0); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); modbus_free(ctx); return -1; } server_socket = modbus_tcp_listen(ctx, NB_CONNECTION); //这儿并不会阻塞 /** * 这儿的server_socket值应该是3,因为标准输入流的文件描述符为0,标准输出流的文件描述符为1 * 标准错误流的文件描述符为2,server_socket是本程序中的第一个文件描述符,所以其值为3 */ /** * 绑定SIGINT信号到处理函数 * SIGINT信号由Interrupt Key产生,通常是CTRL+C或者DELETE。 * 发送给所有ForeGround Group的进程 */ signal(SIGINT, close_sigint); /*清空监视集合*/ FD_ZERO(&refset); /*将server_socket文件描述符加入到监视集合中*/ FD_SET(server_socket, &refset); /*保证fdmax是需要监视的最大的文件描述符值*/ fdmax = server_socket; for (;;) { /** * select函数成功返回时会将未准备好的描述位清零,所以每次select开始之前都需要将refset的值重新赋值给rdset */ rdset = refset; /** * select(int maxfd, fd_set *rdset, fd_set *wrset, fd_set *exset, struct timeval *timeout); * maxfd是需要监视的最大的文件描述符值+1(这个参数是为了优化性能,否则系统每一次要从0检测到1023) * select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型 * rdset, wrset, exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合,不需要监视的项可以设为NULL * struct timeval 结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有发生变化则函数返回,返回值0。 * (若timeout参数为NULL,则程序一直阻塞直到监视的描述符发生变化) * FD_ZERO(fd_set *fdset);将指定的文件描述符清空,在对文件描述符进行设置前,必须对其进行初始化。 * FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符 * FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符 * FD_ISSET(int fd, fd_set *fdset);用于测试指定的文件描述符是否存在该集合中 * 过去,一个fd_set通常只能包含小于32的fd,因为fd_set其实只用了一个32位矢量来表示fd; * 现在UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,它是数据类型fd_set的描述自数量,其值通常是1024, * 这样就能表示小于1024的fd */ if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1) { perror("Server select() failure."); close_sigint(1); } /**其实master_socket=0处完全可以换为3,因为0/1/2号设备时系统的标准输入/输出/错误流,并不需要监视**/ for (master_socket = 0; master_socket <= fdmax; master_socket++) { if (FD_ISSET(master_socket, &rdset)) { //检查master_socket是否存在该集合中,即对应的描述符是否发生变化 if (master_socket == server_socket) { //socket客户端请求一个新的socket连接 socklen_t addrlen; struct sockaddr_in clientaddr; int newfd; /*处理新的连接请求*/ addrlen = sizeof(clientaddr); memset(&clientaddr, 0, sizeof(clientaddr)); newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen); if (newfd == -1) { perror("Server accept() error"); } else { FD_SET(newfd, &refset); //将新连接的文件描述符加入监视集合中 if (newfd > fdmax) { /*保证fdmax是需要监视的最大的文件描述符值*/ fdmax = newfd; } printf("New connection from %s:%d on socket %d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd); } } else {//一个已连接的socket客户端(modbus主机)发送来了新的请求报文 uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; //处理请求报文 modbus_set_socket(ctx, master_socket); rc = modbus_receive(ctx, query); if (rc != -1) { modbus_reply(ctx, query, rc, mb_mapping); } else { /*读取出错,则说明连接断开或发生了不可恢复的错误,应该释放连接*/ printf("Connection closed on socket %d\n", master_socket); close(master_socket); /*将释放掉的连接的文件描述符从监视集合中清除*/ FD_CLR(master_socket, &refset); if (master_socket == fdmax) { /*保证fdmax是需要监视的最大的文件描述符值*/ fdmax--; } } } } } } return 0; }
相关文章推荐
- android 学习笔记 使用HttpClient
- lighttpd源码学习
- 《TCP/IP详解卷1:协议》——第1章:概述(转载)
- 清除网络共享文件夹密码缓存
- muduo网络库源码学习————Timestamp.cc
- http://www.freebuf.com/
- Response.End(); 用HttpContext.Current.ApplicationInstance.CompleteRequest 代替
- Linux网络编程--IO函数以及示例
- XMLHttpReques对象初认识
- Http Post与Get等
- tomcat配置https
- android网络游戏开发——客户端开发——游戏引擎的选择
- hdu 4731 2013成都赛区网络赛 找规律
- 网络技术新名词笔记(by quqi99)
- 关于修复“启用了不安全的 HTTP 方法”
- Http请求过程
- iOS开发网络篇—数据缓存
- tcp二进制反码求和
- fd_set具体是怎样实现的 http://blog.chinaunix.net/uid-20680966-id-1896524.html
- Note For Linux By Jes(15)-系统配置工具(网络与打印机)与硬件侦测