{网络编程}Multiplexing——Select在网络编程中的简单运用
2010-12-19 14:07
246 查看
因为课程要求,需要编制一个用于局域网络的通信软件。这几天一直在摸索这个东西,今天突然纠正了对SELECT系统调用理解上的偏差,所以决定把它笔记下来,以免得很快就忘掉了。为了验证自己确实基本理解了SELECT的运用场合,自己试着简单模拟了一个群聊天功能。这个功能很简单,多个客户端与服务器连接成功之后,这些客户端便可以随时发送消息与接受其他用户的消息。
当一个服务器要实现同时处理多个连接请求的方式通常有三种:Multiplexing, Fork, Multi-thread。这篇笔记仅仅是有关Multiplexing的,中文下也叫做多路复用。之前太过急躁,便粗略的以为Multiplexing方式对客户端连接的处理仅仅是一个线性的处理过程,比如有A, B, C, D, E 五个客户端,服务器是依据五个客户端的请求顺序依次处理这些请求的,并且只有当对某一个客户端请求完毕之后才能够处理下一个请求,有点类似与最初的单道操作系统。 这样的话还谈什么服务啊!不过今天突然发现,我的理解的有误的。
简单地说,多路复用是通过“监测机制”来实现对多个连接的处理的。也即我们用某个系统调用(SELECT)来阻塞监测与服务器已建立的连接的状态,当某个连接上面有状态变更(数据传输、连接建立请求、连接断开等)时,SELECT退出阻塞态,转而进入处理逻辑。有关SELECT介绍的资料很多,自己就不在复述整个过程啦。
----------------------------------------------------------------------------------------------------------------
如下是程序代码:
Code:
/* 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;
}
Code:
/* 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调用,就不准备继续完善所有的逻辑功能啦!还有很多任务要做呢。
当一个服务器要实现同时处理多个连接请求的方式通常有三种:Multiplexing, Fork, Multi-thread。这篇笔记仅仅是有关Multiplexing的,中文下也叫做多路复用。之前太过急躁,便粗略的以为Multiplexing方式对客户端连接的处理仅仅是一个线性的处理过程,比如有A, B, C, D, E 五个客户端,服务器是依据五个客户端的请求顺序依次处理这些请求的,并且只有当对某一个客户端请求完毕之后才能够处理下一个请求,有点类似与最初的单道操作系统。 这样的话还谈什么服务啊!不过今天突然发现,我的理解的有误的。
简单地说,多路复用是通过“监测机制”来实现对多个连接的处理的。也即我们用某个系统调用(SELECT)来阻塞监测与服务器已建立的连接的状态,当某个连接上面有状态变更(数据传输、连接建立请求、连接断开等)时,SELECT退出阻塞态,转而进入处理逻辑。有关SELECT介绍的资料很多,自己就不在复述整个过程啦。
----------------------------------------------------------------------------------------------------------------
如下是程序代码:
Code:
/* 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;
}
Code:
/* 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调用,就不准备继续完善所有的逻辑功能啦!还有很多任务要做呢。
![](http://student.csdn.net/attachment/201012/19/41850_1292738767w89g.png)
相关文章推荐
- {网络编程}Multiplexing——Select在网络编程的简单运用
- 网络编程:select模型的简单实例
- Python网络编程之 select(2)实现一个最简单的webserver
- 简单的多路复用编程(I/O)----select()和poll函数的运用
- Python网络编程中的select 和 poll I/O复用的简单使用
- Python网络编程中的select 和 poll I/O复用的简单使用
- python 网络编程——urllib,urllib2简单运用
- 朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型
- 网络编程(51)—— Windows下使用select进行IO复用
- Java UDP网络编程 - 最简单示例
- Linux 网络编程一步一步学(一)-简单客户端编写
- linux网络编程十二:简单模拟HTTP请求的读取和分析
- 网络编程与多线程的应用--基于socket udp编写一个简单聊天程序
- linux网络编程简单C/S模型实现
- Python网络编程之 select
- Android网络编程之一:原生网络访问简单封装
- 网络编程之select
- JAVA 网络编程简单示例
- linux网络编程:select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(转)
- Windows网络编程之Select模型学习笔记