欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux下用select()实现异步的Echo服务器

程序员文章站 2024-02-01 20:11:46
...

本例子使用异步socket(select方法)实现了ECHO服务器程序。

搞了一个晚上,终于弄好了,出现的问题主要如下:

  • 这是最重要的问题!当读取完数据后,需要将数据重新FD_SET进去,特别是serverFd,注意这个testFd意义非常重大,相当于参数传递中的复制行参,需要好好体会。
  • 当read(rd)后,返回为0表示客户端的socket已经关闭,此时除了要FD_CLR,还要关闭fd!!否则fd资源没有被释放,很快就会达到select监听的上限!

#相关代码, [四号程序员] http://www.coder4.com
/*
 * main.cc
 *
 *  Created on: 2009-11-30
 *      Author: liheyuan
 *    Describe: 非阻塞模式服务器(Echo服务器)
 *
 *   Last Date: 2009-11-30
 *   CopyRight: 2009 @ ICT LiHeyuan
 */
 
#include <iostream>
using namespace std;
 
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
 
#define SERVER_PORT 18000
 
#define SERVER_QUEUE 10
#define FD_SET_SIZE 10
#define MAX_BUF 16
 
int main() {
 
    //设置服务器Addr,在18000,任意IP监听
    int serverFd;
    serverFd = socket(AF_INET, SOCK_STREAM, 0);
 
    /* 设置 serverFd 为非阻塞方式 */
    int opt = SO_REUSEADDR;
    setsockopt(serverFd, SOL_SOCKET, opt, &opt, sizeof(opt));
 
    struct sockaddr_in serverAddr;
    socklen_t serverAddrLen = sizeof(sockaddr_in);
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(SERVER_PORT);
 
    //绑定
    if (bind(serverFd, (sockaddr*) &serverAddr, serverAddrLen)) {
        cout << "Binding on " << SERVER_PORT << " fail." << endl;
        return -1;
    }
 
    //创建等待队列
    listen(serverFd, SERVER_QUEUE);
 
    //设定fd_set
    fd_set readfds, testfds;
    FD_ZERO(&readfds);
    FD_SET(serverFd,&readfds);
 
    //非阻塞模式等待客户连接
    struct sockaddr_in clientAddr;
    int clientFd;
    socklen_t clientAddrLen;
    int len;
    char buf[MAX_BUF];
    while (1) {
        FD_SET(serverFd,&readfds);
        testfds = readfds;
 
        //选择readfds中可用的fd
        if (select(FD_SET_SIZE, &testfds, (fd_set *) NULL, (fd_set *) NULL,
                (struct timeval *) NULL) > 0) {
            if (FD_ISSET(serverFd,&testfds)) {
                //如果服务器fd可用,则为accept
                clientAddrLen = sizeof(sockaddr_in);
                clientFd = accept(serverFd, (struct sockaddr*) &clientAddr,
                        &clientAddrLen);
                if (clientFd == -1) {
                    cout << "accept() error" << endl;
                    return -1;
                } else {
                    FD_SET(clientFd,&readfds);
                    cout << inet_ntoa(clientAddr.sin_addr) << " connect"
                            << " at fd" << clientFd << endl;
                }
            }
            //依次查询
            for (int fd = 0; fd < FD_SET_SIZE; fd++) {
                if (FD_ISSET(fd,&testfds)) {
                    //如果是Client活动,进行Echo
                    len = read(fd, buf, MAX_BUF);
                    if (len > 0) {
                        write(fd, buf, len);
                    } else {
                        //结束
                        cout << "Client end at" << clientFd << endl;
                        FD_CLR(fd,&readfds);
                    }
                }
            }
        }
    }
 
    close(serverFd);
}

 

相关标签: select 网络编程