在 C++网络,使用 select 我首先必须listen() 和accept() 吗?

发布于 2024-08-21 22:31:36 字数 2480 浏览 4 评论 0原文

我试图允许多个客户端使用 select 连接到主机。我是否必须连接每个端口,告诉它们移至不同的端口,然后在新端口上重新连接?或者选择允许我将多个客户端连接到同一端口?

这是客户端代码:

    int rv;
int sockfd, numbytes;

if ((rv = getaddrinfo(hostName, hostPort, &hints, &servinfo)) != 0) {
    cout << "Could not get server address.\n";
    exit(1);
}

// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
        perror("Client: no socket");
        continue;
    }

    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
        close(sockfd);
        perror("Client: connect");
        continue;
    }

    break;
}

if (p == NULL) {
    fprintf(stderr, "Unable to connect to server.\n");
    exit(2);
}

FD_SET(sockfd, &masterSet);

这是服务器代码:

        int rv = getaddrinfo(NULL, port, &hints, &res);
    int yes = 1;//Not sure what this is for, found it in Beej's
    if(rv != 0){
            cout<< "Error, nothing matches criteria for file descriptor.\n";
            exit(1);
    }
    int fdInit;
    for(temp = res; temp != NULL; temp = temp->ai_next){
            if((fdInit = socket(temp->ai_family, temp->ai_socktype, temp->ai_protocol)) == -1){
                    cout << "This is not the fd you're looking for.  Move along.\n";
                    continue; //This is not the fd you're looking for, move along.
            }

            if(setsockopt(fdInit, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
                    cout << "Doom has fallen upon this set socket.\n";
                    perror("setsockopt");
                    exit(1); //Unable to set socket, exit program with code 1
            }

            if(bind(fdInit, temp->ai_addr, temp->ai_addrlen) == -1){
                    cout << "Could not bind fd\n";
                    close(fdInit);
                    continue; //Could not bind fd, continue looking for valid fd
            }
            break; //If a valid fd has been found, stop checking the list
    }
    if(temp==NULL){
            cout<<"Server failed to bind a socket\n";
            exit(2);
    }

    cout << fdInit << endl;
    //Setup the file descriptor for initial connections on specified port
    freeaddrinfo(res);
    FD_SET(fdInit, &masterSet);

任何帮助都会非常好!谢谢。

I'm trying to allow multiple clients to connect to a host using select. Will I have to connect each one, tell them to move to a different port, and then reconnect on a new port? Or will select allow me to connect multiple clients to the same port?

This is the client code:

    int rv;
int sockfd, numbytes;

if ((rv = getaddrinfo(hostName, hostPort, &hints, &servinfo)) != 0) {
    cout << "Could not get server address.\n";
    exit(1);
}

// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
        perror("Client: no socket");
        continue;
    }

    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
        close(sockfd);
        perror("Client: connect");
        continue;
    }

    break;
}

if (p == NULL) {
    fprintf(stderr, "Unable to connect to server.\n");
    exit(2);
}

FD_SET(sockfd, &masterSet);

This is the server code:

        int rv = getaddrinfo(NULL, port, &hints, &res);
    int yes = 1;//Not sure what this is for, found it in Beej's
    if(rv != 0){
            cout<< "Error, nothing matches criteria for file descriptor.\n";
            exit(1);
    }
    int fdInit;
    for(temp = res; temp != NULL; temp = temp->ai_next){
            if((fdInit = socket(temp->ai_family, temp->ai_socktype, temp->ai_protocol)) == -1){
                    cout << "This is not the fd you're looking for.  Move along.\n";
                    continue; //This is not the fd you're looking for, move along.
            }

            if(setsockopt(fdInit, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
                    cout << "Doom has fallen upon this set socket.\n";
                    perror("setsockopt");
                    exit(1); //Unable to set socket, exit program with code 1
            }

            if(bind(fdInit, temp->ai_addr, temp->ai_addrlen) == -1){
                    cout << "Could not bind fd\n";
                    close(fdInit);
                    continue; //Could not bind fd, continue looking for valid fd
            }
            break; //If a valid fd has been found, stop checking the list
    }
    if(temp==NULL){
            cout<<"Server failed to bind a socket\n";
            exit(2);
    }

    cout << fdInit << endl;
    //Setup the file descriptor for initial connections on specified port
    freeaddrinfo(res);
    FD_SET(fdInit, &masterSet);

Any help would be excellent! Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

说好的呢 2024-08-28 22:31:36

TCP 连接由连接两端的 IP 地址和端口号来标识。因此,有很多客户端(通常具有随机分配的端口号)连接到单个服务器端口是很好的。

您创建一个套接字并将其bind()到一个端口,在该端口上listen(),然后等待客户端来敲击它。如果您不介意阻塞,您可以直接调用 accept() ,但您不会执行任何超时循环或任何操作。否则,您可以在侦听套接字上select()(当客户端尝试连接时该套接字将变为可读),然后调用accept()

accept() 将返回一个新创建的套接字,这是与客户端通信的实际套接字。原来的监听套接字继续监听,并且可以在其上接受更多连接。

通常使用 select() 循环来查找侦听套接字和任何已连接套接字的可读性。然后,当 select() 返回时,您只需检查侦听套接字是否可读,如果是,则 accept();否则寻找一个可读的已连接套接字并处理它。

fd_set fds;
int max = 0, reuse = 1;
struct timeval tv;
int server;
std::vector<int> connected;

// create server listening socket
server = socket(PF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto);
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); // optional, but recommended
if (bind(server, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
    // error, could not bind server socket
}
if (listen(server, 8) < 0) {
    // error, could not listen on server port
}

// loop looking for connections / data to handle
while (running) {
    FD_ZERO(&fds);
    FD_SET(server, &fds);
    if (server >= max) max = server + 1;

    for (std::vector<int>::iterator it = connected.begin(); it != connected.end(); ++it) {
        FD_SET(*it, &fds);
        if (*it >= max) max = *it + 1;
    }

    tv.tv_sec = 2; tv.tv_usec = 0;
    if (select(max, &fds, NULL, NULL, &tv) > 0) {
        // something is readable
        if (FD_ISSET(server, &fds)) {
            // it's the listener
            connected.push_back(accept(server, (struct sockaddr *)&addr));
        }
        for (std::vector<int>::iterator it = connected.begin(); it != connected.end(); ++it) {
            if (FD_ISSET(*it, &fds)) {
                // handle data on this connection
            }
        }
    }
}

TCP connections are identified by the IP address and port number of both ends of the connection. So it's fine to have lots of clients (which will generally have randomly assigned port numbers) to connect to a single server port.

You create a socket and bind() it to a port on which to listen(), and then wait for clients to come knocking on it. If you don't mind blocking you can just call accept() on it directly, but you won't get to do any timeout looping or anything. Otherwise you can select() on the listening socket, which will become readable when a client is attempting to connect, and then call accept().

accept() will return a newly created socket, which is the actual socket to talk to the client on. The original listening socket continues to listen, and more connections can be accepted on it.

It's typical to use a select() loop to look for readability on the listening socket and any of the connected sockets. Then when select() returns you simply check whether the listening socket was readable, and if so, accept(); otherwise look for a readable connected socket and handle it.

fd_set fds;
int max = 0, reuse = 1;
struct timeval tv;
int server;
std::vector<int> connected;

// create server listening socket
server = socket(PF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto);
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); // optional, but recommended
if (bind(server, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
    // error, could not bind server socket
}
if (listen(server, 8) < 0) {
    // error, could not listen on server port
}

// loop looking for connections / data to handle
while (running) {
    FD_ZERO(&fds);
    FD_SET(server, &fds);
    if (server >= max) max = server + 1;

    for (std::vector<int>::iterator it = connected.begin(); it != connected.end(); ++it) {
        FD_SET(*it, &fds);
        if (*it >= max) max = *it + 1;
    }

    tv.tv_sec = 2; tv.tv_usec = 0;
    if (select(max, &fds, NULL, NULL, &tv) > 0) {
        // something is readable
        if (FD_ISSET(server, &fds)) {
            // it's the listener
            connected.push_back(accept(server, (struct sockaddr *)&addr));
        }
        for (std::vector<int>::iterator it = connected.begin(); it != connected.end(); ++it) {
            if (FD_ISSET(*it, &fds)) {
                // handle data on this connection
            }
        }
    }
}
聚集的泪 2024-08-28 22:31:36

许多客户端可以连接到同一个端口,

您必须先侦听,然后选择,

当选择告诉您有一个新连接时,然后接受。它告诉您客户端通过标记对套接字 fd 的读取来连接。

many clients can connect to the same port

you must listen first then select

when select tells you you have a new connection then accept. It tells you that client connected by flagging a read on your socket fd.

燃情 2024-08-28 22:31:36

您必须将服务器套接字标记为这样(listen(2)),但仅在从 select(2) 返回时调用 accept(2)它变得可读 - 这意味着新的连接请求正在等待。

请注意,如果您不使用非阻塞套接字select(2)之间就有竞争的机会 code> 返回并调用 accept(2) - 连接客户端可能会在此期间放弃尝试 - 因此您仍然可以阻止。

You have to mark server socket as such (listen(2)) but only call accept(2) upon return from select(2) when it becomes readable - that means new connection request is pending.

Please note that if you are not working with non-blocking sockets there's a chance for a race between select(2) returning and calling accept(2) - the connecting client may drop the attempt during that time - so you can still block.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文