您的位置:首页 > 理论基础 > 计算机网络

{网络编程}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调用,就不准备继续完善所有的逻辑功能啦!还有很多任务要做呢。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: