【问题标题】:Linux TCP Server - listen on multiple ports in C++Linux TCP Server - 在 C++ 中侦听多个端口
【发布时间】:2013-08-30 04:50:13
【问题描述】:

我的 tcp 服务器有问题。我想监听多个端口以响应客户端。它应该是一种基于事件的。每个端口表示另一种类型的响应。我读了很多关于 epoll、poll、select 或多线程的文章。我尝试使用 Unix Network Programming 等书籍中的大量示例。但可能我需要一些触发关键字。怎样才能正常启动?

希望我的问题很容易理解。 感谢每个答案!

缩小范围,这是我的想法... 我开始考虑这个:

如果我有一个包含很多服务器的“服务器管理器”,我可以按照以下方式进行操作吗?

CreateSockets(ServerList); CheckSockets(SocketList, master_set);

在服务器管理器中: 1)for循环创建所有服务器socket(函数:socket/setsockopt/ioctl/bind/listen)

void CreateSockets(map<int,ServerType> ServerList)
{
    fd_set        master_set;
    map<int,ServerType>::iterator it;
    map<int,int> SocketList;
    for (it= ServerList.begin();it!= ServerList.end();it++)
    {
        listen_sd = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_sd < 0)
        {
            perror("socket() failed");
            exit(-1);
        }
        rc = setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,
                (char *)&on, sizeof(on));
        if (rc < 0)
        {
            perror("setsockopt() failed");
            close(listen_sd);
            exit(-1);
        }
        rc = ioctl(listen_sd, FIONBIO, (char *)&on);
        if (rc < 0)
        {
            perror("ioctl() failed");
            close(listen_sd);
            exit(-1);
        }
        memset(&addr, 0, sizeof(addr));
        addr.sin_family      = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port        = ((*it).second->Port);
        rc = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr));
        if (rc < 0)
        {
            perror("bind() failed");
            close(listen_sd);
            exit(-1);
        }
        rc = listen(listen_sd, 32);
        if (rc < 0)
        {
            perror("listen() failed");
            close(listen_sd);
            exit(-1);
        }
        SocketList.insert(make_pair(((*it).second->Port),listen_sd));
        FD_ZERO(&master_set);
        max_sd = listen_sd;
        FD_SET(listen_sd, &master_set);
    }
}

下一步: 2) 一些如何在服务器管理器中等待某些事件(使用套接字描述符列表选择)

void CheckSockets(map<int,int> SocketList, fd_set master_set)
 {
    fd_set working_set;
    do
    {
        memcpy(&working_set, &master_set, sizeof(master_set));
        ready_descriptors = select(max_sd + 1, &working_set, NULL, NULL, NULL);

        if (ready_descriptors >0)
        {
            desc_ready = rc;
            for (i=0; i <= max_sd  &&  desc_ready > 0; ++i)
            {
                if (FD_ISSET(i, &working_set))
                {
                    desc_ready -= 1;
                    if (i == listen_sd)
                    {
                        do
                        {
                            new_sd = accept(listen_sd, NULL, NULL);
                            if (new_sd < 0)
                            {
                                //error
                            }
                            FD_SET(new_sd, &master_set);
                            if (new_sd > max_sd)
                                max_sd = new_sd;
                        } while (new_sd != -1);
                    }
                    else
                    {
                        do
                        {
                            //Go into server and recv and send ( Input Parameter = i)
                            CheckServer(i);
                        } while (TRUE);
                    }
                }
            }
        }
        else {endserver=true;}
    }while(endserver=true;)

}

3) 进入服务器并处理问题(recv/send)????

    void CheckServer( int sd)
 {
    rc = recv(sd, buffer, sizeof(buffer), 0);

    //some stuff in between

    rc = send(i, buffer, len, 0); 
 }

这行得通吗?

IBM 非阻塞 IO 源代码使用和更改了某些部分。


感谢您的所有帮助。我能够完成一些事情,但有一件事仍然不起作用。

到目前为止我做了什么:

1) 单个服务器的构造器包括socket操作。 2)我能够返回套接字ID并将其保存在服务器管理器中。 3) 管理器有一个 for 循环,其中包含用于检查套接字上的任何事件的 select 命令。 4) 如果有什么事情发生,所有受影响的socket都会依次响应。

我的问题是:

如果我在从服务器请求数据时始终连接和断开连接,它工作正常。 当我的客户端配置为保持连接时,一切都会阻塞,因为我的代码正在等待断开连接。

这里是每个部分的代码sn-ps:

1)

       Server::Server() 
    {

    listen_sd = socket(AF_INET, SOCK_STREAM, 0);
    ret = setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,(char *)&on, sizeof(on));
    ret = ioctl(listen_sd, FIONBIO, (char *)&on);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port        = htons(Server_Port);
    ret = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr));
    ret = listen(listen_sd, 32);
    Socket = listen_sd;
}

2)

Socket= new_Server->GetSocket();
SocketList.insert(make_pair(Socket,new_Server->ServerID));

3)

 while (TRUE)
    {
        FD_ZERO(&working_set);
        for (i=0;i < max_conn;i++) 
{
            if (SocketArray[i] >= 0) {FD_SET(SocketArray[i], &working_set);}
        }

        ret = select(max_sd+1, &working_set, NULL, NULL, NULL);

    desc_ready= ret;
        for (i=0; i <= max_sd  &&  desc_ready > 0; ++i)
        {

            if (FD_ISSET(i, &working_set)) //jeder Peer der was hat
            {
                desc_ready -= 1;
//delete all loops to get the correct object 
                        (Server).second->DoEvent(i);

            }
        }
    }

4)

new_sd = accept(new_sd, NULL, NULL);
    if (new_sd < 0)
    {
        if (errno != EWOULDBLOCK)
        {
            perror("  accept() failed");
        }
    }
do
{


    rc = recv(new_sd, buffer, sizeof(buffer), 0);
//edit datastream and create response
            rc = send(new_sd, buffer, len, 0);
        if (rc < 0)
        {
            perror("  send() failed");
            close_conn = TRUE;
            break;
        }

    }while (TRUE);

我只是删除了errorhandling dor listen/bind等,只是为了缩短这里的代码......原来它在那里。

【问题讨论】:

  • 这个问题太笼统了,请缩小到可以回答的范围内。
  • 您已经签出Beej's Guide to Network Programming 了吗?它具有所有基础知识(不过是 c 风格)。
  • 看看反应堆模式...
  • 我想知道是否还没有一些类/库。
  • 我需要非阻塞服务器套接字。我想编程的内容:1)建立所有必要的套接字2)在一个或多个这些套接字上等待事件3)如果事件而不是响应这些特定套接字上的每个请求......我的问题是将这个想法转换成源代码,因为我不确定哪些命令最适合我......同时考虑运行时、响应时间和最大套接字数量的限制......希望我把它缩小一点

标签: c++ linux unix tcp port


【解决方案1】:

大致步骤如下:您可以让多个 TCP 服务器(也称为服务器套接字)监听每个端口。接下来,您可以使用 select() 并为每个服务器套接字传递文件描述符。如果您在其中任何一个上获得连接,则 select 将返回一个读取事件并标记具有该连接的服务器套接字的 fd。您需要在该服务器 fd 上调用 accept()。

你不能让一个 TCP 套接字监听多个端口。

【讨论】:

  • 感谢您的回答。我正在考虑选择,但我不想有任何超时 - 所以我需要非阻塞服务器套接字。用select可以吗?既然接受确实等待对吗?
  • Select 是一个阻塞调用。您可以将超时指定为要选择的参数,但不指定它(这意味着将其作为 NULL 传递)将意味着选择仅在有事件时才会超时(在这种情况下为读取事件)。顺便说一句,你总是可以有多个线程,一个线程可以为所有服务器 fds 做选择。
  • 谢谢。所以还有一个问题。如果我有一个带有很多服务器的“服务器管理器”,我可以按照以下方式进行操作吗?在服务器管理器中:1)for 循环创建所有服务器套接字(函数:socket/setsockopt/ioctl/bind/listen)2)如何在服务器管理器中等待某些事件(使用套接字描述符列表选择)3)进入服务器处理问题(recv/send)????
  • 是的,您需要为每个端口创建 TCP 服务器(绑定/监听 stesp)——使用 for 循环就可以了。接下来,您可以使用 select 并传递 select fd 集中每个服务器的文件描述符并等待读取事件(请阅读有关 select!)。一旦你有一个传入的连接,选择将取消阻塞并返回具有挂起连接的服务器的 fd(或 fds)。我们可以在这些 fd 上调用 accept()。 accept() 将返回子连接的 fd,我们可以对这个子 fd 进行接收/发送。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-23
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
相关资源
最近更新 更多