《TCP/IP网络编程》笔记12-并发服务器的实现方法-I/O复用

时间:2021-11-30 17:58:15

并发服务器的实现方法:

  • 多进程服务器
  • 多路复用服务器
  • 多线程服务器

select函数是I/O复用的全部内容,server端代码如下:

// echoSvrSelect.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment (lib,"ws2_32.lib") 

#define BUF_SZ 1024
int main(int argc, char* argv[])
{
    WSADATA wsaData;
    SOCKET hSocket,hClientSock;
    SOCKADDR_IN servAddr,clientAddr;
    fd_set read_set, cpy_set;
    int strlen,i,fd_num;
    TIMEVAL timeout;
    char message[BUF_SZ];
    int szClientAddr;
    if(argc!=2){
        printf("usage : %s port\n",argv[0]);
        return 0;
    }
    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){
        printf("WSAStartup failed\n");
        return 0;
    }
    hSocket = socket(PF_INET,SOCK_STREAM,0);
    if(hSocket == INVALID_SOCKET){
        printf("socket error\n");
        return 0;
    }
    memset(&servAddr,0,sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(atoi(argv[1]));


    if(bind(hSocket,(SOCKADDR*)&servAddr,sizeof(servAddr)) == SOCKET_ERROR){
        printf("bind port [%d] error\n",atoi(argv[1]));
        return 0;
    }

    if(listen(hSocket,5) == SOCKET_ERROR){
        printf("listen error\n");
        return 0;
    }
    else{
        printf("server started, port :%d\n",atoi(argv[1]));
    }

    FD_ZERO(&read_set);
    FD_SET(hSocket,&read_set);

    for(;;){
        // select 的通用方法,selcet调用之后,除发生变化的文件描述符,其他的
        // 所有位初始化为0,因此为了住初始值,必须经过这个复制过程
        cpy_set = read_set;

        // select 只有在监听的文件描述符变化时才返回,否则进入阻塞状态,
        // 设置超时时间防止这种情况发生
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        if((fd_num = select(0,&cpy_set,0,0,&timeout))==SOCKET_ERROR){
            break;
        }

        if(fd_num == 0){
            continue;
        }
        for(i=0;i<read_set.fd_count;++i){
            if(FD_ISSET(read_set.fd_array[i],&cpy_set))
            {
                if(read_set.fd_array[i] == hSocket){
                    szClientAddr = sizeof(clientAddr);
                    hClientSock = accept(hSocket,(SOCKADDR*)&clientAddr,&szClientAddr);
                    FD_SET(hClientSock,&read_set);
                    char saddr[20];
                    char * paddr;
                    paddr = inet_ntoa(clientAddr.sin_addr);
                    strcpy(saddr,paddr);
                    printf("[%s:%d] connect!\n",saddr,ntohs(clientAddr.sin_port));
                    printf("total online:%d\n",read_set.fd_count);
                }
                else{
                    strlen=recv(read_set.fd_array[i],message,BUF_SZ-1,0);
                    message[strlen] = 0;
                    if(strlen == 0){
                        closesocket(read_set.fd_array[i]);
                        FD_CLR(read_set.fd_array[i],&read_set);
                        printf("close socket\n");
                    }
                    else{
                        printf("echo:%d:%s\n",read_set.fd_array[i],message);
                        send(read_set.fd_array[i],message,strlen,0);
                    }
                }
            }
        }

    }

    closesocket(hSocket);
    WSACleanup();
    return 0;
}