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

{网络编程}Multiplexing——Select在网络编程的简单运用

2010-12-19 13:58 281 查看
因为课程要求,需要编制一个用于局域网络的通信软件。这几天一直在摸索这个东西,今天突然纠正了对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调用,就不准备继续完善所有的逻辑功能啦!还有很多任务要做呢。


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