{网络编程}Multiplexing——Select在网络编程的简单运用
2010-12-19 13:58
281 查看
因为课程要求,需要编制一个用于局域网络的通信软件。这几天一直在摸索这个东西,今天突然纠正了对SELECT系统调用理解上的偏差,所以决定把它笔记下来,以免得很快就忘掉了。为了验证自己确实基本理解了SELECT的运用场合,自己试着简单模拟了一个群聊天功能。这个功能很简单,多个客户端与服务器连接成功之后,这些客户端便可以随时发送消息与接受其他用户的消息。
当一个服务器要实现同时处理多个连接请求的方式通常有三种:Multiplexing, Fork, Multi-thread。这篇笔记仅仅是有关Multiplexing的,中文下也叫做多路复用。之前太过急躁,便粗略的以为Multiplexing方式对客户端连接的处理仅仅是一个线性的处理过程,比如有A, B, C, D, E 五个客户端,服务器是依据五个客户端的请求顺序依次处理这些请求的,并且只有当对某一个客户端请求完毕之后才能够处理下一个请求,有点类似与最初的单道操作系统。 这样的话还谈什么服务啊!不过今天突然发现,我的理解的有误的。
简单地说,多路复用是通过“监测机制”来实现对多个连接的处理的。也即我们用某个系统调用(SELECT)来阻塞监测与服务器已建立的连接的状态,当某个连接上面有状态变更(数据传输、连接建立请求、连接断开等)时,SELECT退出阻塞态,转而进入处理逻辑。有关SELECT介绍的资料很多,自己就不在复述整个过程啦。
如下是程序代码:
如下是简要示意图,程序中还是有很多纰漏之处。因为只是熟悉下SELECT调用,就不准备继续完善所有的逻辑功能啦!还有很多任务要做呢。
当一个服务器要实现同时处理多个连接请求的方式通常有三种:Multiplexing, Fork, Multi-thread。这篇笔记仅仅是有关Multiplexing的,中文下也叫做多路复用。之前太过急躁,便粗略的以为Multiplexing方式对客户端连接的处理仅仅是一个线性的处理过程,比如有A, B, C, D, E 五个客户端,服务器是依据五个客户端的请求顺序依次处理这些请求的,并且只有当对某一个客户端请求完毕之后才能够处理下一个请求,有点类似与最初的单道操作系统。 这样的话还谈什么服务啊!不过今天突然发现,我的理解的有误的。
简单地说,多路复用是通过“监测机制”来实现对多个连接的处理的。也即我们用某个系统调用(SELECT)来阻塞监测与服务器已建立的连接的状态,当某个连接上面有状态变更(数据传输、连接建立请求、连接断开等)时,SELECT退出阻塞态,转而进入处理逻辑。有关SELECT介绍的资料很多,自己就不在复述整个过程啦。
如下是程序代码:
/* server.c */ #include <stdio.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> int main(int argc, char* argv[]) { struct sockaddr_in sAddr; fd_set readset, testset; int listensock; int newsock; char buffer[25]; int result; int nread; int x; int val; int ix, clientdescriptor[FD_SETSIZE]; int nclient = 0; // Create the main socket listensock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Set the socket option val = 1; result = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (result < 0) { perror("server"); return 0; } // Bind the socket to the listening port sAddr.sin_family = AF_INET; sAddr.sin_port = htons(1972); sAddr.sin_addr.s_addr = INADDR_ANY; result = bind(listensock, (struct sockaddr*)&sAddr, sizeof(sAddr)); if (result < 0) { perror("server"); return 0; } // Put the socket into "listen" mode so that we can accept incoming connections result = listen(listensock, 5); if (result <0) { perror("server"); return 0; } // Iitialize our descriptor set FD_ZERO(&readset); FD_SET(listensock, &readset); // while (1) { testset = readset; // Begin to wait result = select(FD_SETSIZE, &testset, NULL, NULL, NULL); if (result <1) { perror("server"); return 0; } for (x=0; x<FD_SETSIZE; ++x) { if (FD_ISSET(x, &testset)) { // New connection comes if (x == listensock) { newsock = accept(listensock, NULL, NULL); FD_SET(newsock, &readset); clientdescriptor[nclient++] = newsock; printf("client %d connected!/n", newsock); } // Other event occurs else { sprintf(buffer, "|client %d| says: ", x); int n = strlen(buffer); nread = recv(x, buffer+n, 25, 0); if (nread <= 0) { close(x); FD_CLR(x, &readset); printf("client on descriptor #%i disconnected/n", x); } else { // Relay the message to all the online clients buffer[nread+n] = '/0'; printf("Server received message! '%s'/n",buffer); for (ix=0; ix<nclient; ++ix) { if (clientdescriptor[ix] != x) { // printf("%s/n", buffer); nread = send(clientdescriptor[ix], buffer, nread+n, 0); if (nread > 0) { printf("Message Sending Success!/n"); printf("To client %d./n", clientdescriptor[ix]); } } } } } } } } close(listensock); return 0; }
/* client.c */ #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <sys/ioctl.h> #define MAXBUF 256 int main(int argc, char *argv[]) { int sock; struct sockaddr_in sAddr; fd_set readset; int maxfd; char buffer[MAXBUF]; int len; int result; // Create our child socket and bind it to a local port. sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // We attempt to connect to whichever server is running on the local machine. memset((void*)&sAddr, 0, sizeof(struct sockaddr_in)); sAddr.sin_family = AF_INET; sAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sAddr.sin_port = htons(1972); if (connect(sock, (const struct sockaddr*)&sAddr, sizeof(sAddr))!=0) { perror("client"); return; } printf("Connect Success!/n"); // Once connected, we send some characters to the server. while (1) { // Clear read set FD_ZERO(&readset); // Add the 'standard input' into the read set FD_SET(0, &readset); maxfd = 0; // Add the 'sock' into the read set FD_SET(sock, &readset); if (sock > maxfd) maxfd = sock; // Begin to wait result = select(maxfd+1, &readset, NULL, NULL, NULL); if (result == -1) { printf("Be goint to quit, Select error! '%s'", strerror(errno)); break; } else { // 'sock' need to be serviced if (FD_ISSET(sock, &readset)) { bzero(buffer, MAXBUF); len = recv(sock, buffer,MAXBUF-1, 0); if (len >0) { printf("%s/n",buffer); } else { if (len == 0) { printf("link interrupt!/n"); } else { printf("receive failure! error code %d, error message '%s'/n", errno, strerror(errno)); } break; } } // 'stdin' need to be serviced if (FD_ISSET(0, &readset)) { bzero(buffer, MAXBUF); fgets(buffer, MAXBUF-1, stdin); len = send(sock, buffer, strlen(buffer)-1, 0); if (len <0) { printf("sending failure! error code %d, error message '%s'/n", errno, strerror(errno)); break; } else { // printf(" Message Sending Success!/n"); } } } } close(sock); return 0; }
如下是简要示意图,程序中还是有很多纰漏之处。因为只是熟悉下SELECT调用,就不准备继续完善所有的逻辑功能啦!还有很多任务要做呢。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/03/8a4995f706e47bc8c032704816a0dfb0.gif)
相关文章推荐
- {网络编程}Multiplexing——Select在网络编程中的简单运用
- 网络编程:select模型的简单实例
- Python网络编程中的select 和 poll I/O复用的简单使用
- python 网络编程——urllib,urllib2简单运用
- Python网络编程之 select(2)实现一个最简单的webserver
- 简单的多路复用编程(I/O)----select()和poll函数的运用
- Python网络编程中的select 和 poll I/O复用的简单使用
- Linux 网络编程 select复用模式
- linux------tcp---------Linux网络编程一步一步学-异步通讯聊天程序select
- 【网络编程】Linux多播简单实现
- C# 网络编程之最简单浏览器实现
- 高性能网络编程 - select系统调用
- Java 网络编程 简单Socket(UDP)
- Qt简单网络运用获得本机IP
- linux 网络编程【三】 非阻塞通信select
- 【tcp网络编程】用Socket建立一个简单的文本转换服务器
- Linux 网络编程之socket、select
- 网络编程中的select优化通用C/S模型
- 网络编程之简单网络通信
- 网络编程- ping程序简单实现