如何使用select同时读取连接?插座/c

问题描述 投票:0回答:2
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int port = 3008;
int listenfd;

extern void makelistener();
int main(int argc, char **argv)
{
    makelistener();
    int clientfd, nready;
    socklen_t len;
    struct sockaddr_in q;
    int i;

    // initialize allset and add listenfd to the
    // set of file descriptors passed into select
    fd_set allset;
    fd_set rset;
    int maxfd;
    FD_ZERO(&allset);
    FD_SET(listenfd, &allset); // set of file descriptors

    maxfd = listenfd;
    int ret;

    while (1)
    {
        // make a copy of the set before we pass it into select
        rset = allset;
        /*select will wait until an exceptional event occurs when tv is NULL*/


        nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
        if (nready == 0) {
            continue;
        }

        if (nready == -1) {
            perror("select");
            continue;
        }

        //FD_ISSET returns 1 when a new connection is attempted
        if(FD_ISSET(listenfd, &rset)){
            //printf("a new client is connecting\n");
            len = sizeof(q); //accept connection of listenfd stream socket
            if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
                perror("accept");
                exit(1);
            }
            FD_SET(clientfd, &allset);
            if (clientfd > maxfd) {
                maxfd = clientfd;
            }

            static char msg[] = "What is your name?\r\n";
            write(clientfd, msg, sizeof msg - 1);
            printf("connection from %s\n", inet_ntoa(q.sin_addr));
            char buf[256];
            ret = read(clientfd, buf, sizeof(buf));
            buf[ret] = '\0';
            printf("%s", buf);
       }



   }





}


void makelistener()
{
    struct sockaddr_in r;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        exit(1);
    }

    memset(&r, '\0', sizeof r);
    r.sin_family = AF_INET;
    r.sin_addr.s_addr = INADDR_ANY;
    r.sin_port = htons(port);
    if (bind(listenfd, (struct sockaddr *)&r, sizeof r)) {
        perror("bind");
        exit(1);
    };

    if (listen(listenfd, 5)) {
        perror("listen");
        exit(1);
    }
}

上面的代码适用于服务器,它执行此操作

$ ./above.c
(does nothing but runs forever)

如何作为客户端连接:

$ nc 127.0.0.1 3000
What is your name?
(waiting for my input) so if I put bob, it would output it to the server

它按预期工作。但我希望它也能与多个客户端同时工作。

例如:

服务器

$ ./above.c
(does nothing but runs forever)

客户1

$ nc 127.0.0.1 3000
What is your name?

客户2

$ nc 127.0.0.1 3000
What is your name? (Currently client2 wont show up until client1 is answered which is what I'm trying to fix)

如何使客户端可以同时运行,而无需等待第一个客户端完成?为了稍微解释一下代码,监听器只是绑定并监听连接。 while(1) 里面是选择和调用的地方。

c sockets posix-select
2个回答
0
投票

如何使客户端可以同时运行,而无需等待第一个客户端完成?

通过注意哪些套接字

select()
向您报告。您要求
select()
监视多个套接字的可读性,但您只检查侦听套接字是否可读,而不检查客户端套接字。您需要跟踪连接的客户端,以便在需要时枚举它们。

尝试这样的事情:

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int port = 3008;
#define MAX_CLIENTS (FD_SETSIZE - 1)

int listenfd = -1;

extern void makelistener();

int main(int argc, char **argv)
{
    int clientfd, nready;
    socklen_t len;
    struct sockaddr_in q;
    int i, j, ret;
    fd_set allset;
    fd_set rset;
    int clients[MAX_CLIENTS];
    int num_clients = 0;
    int maxfd;
    char buf[256];

    makelistener();

    // initialize allset and add listenfd to the
    // set of file descriptors passed into select
    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);
    maxfd = listenfd;

    while (1)
    {
        // make a copy of the set before we pass it into select
        FD_COPY(&allset, &rset);

        // select will wait until an exceptional event occurs when tv is NULL
        nready = select(maxfd + 1, &rset, NULL, NULL, NULL);

        if (nready < 0) {
            perror("select");
            continue;
        }

        if (nready == 0) { // should never happen since no timeout was requested
            continue;
        }

        //FD_ISSET returns 1 when a socket is readable

        if (FD_ISSET(listenfd, &rset)) {
            //printf("a new client is connecting\n");
            len = sizeof(q); //accept connection of listenfd stream socket
            if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
                perror("accept");
                exit(1);
            }

            printf("Client %d connected from %s\n", clientfd, inet_ntoa(q.sin_addr));

            if (num_clients == MAX_CLIENTS) {
                static char msg[] = "Max number of clients are already connected\r\n";
                write(clientfd, msg, sizeof(msg)-1);
                close(clientfd);
            }
            else {
                static char msg[] = "What is your name?\r\n";
                if (write(clientfd, msg, sizeof(msg)-1) < 0) {
                    close(clientfd);
                }
                else {
                    clients[num_clients++] = clientfd;
                    FD_SET(clientfd, &allset);
                    if (clientfd > maxfd) {
                        maxfd = clientfd;
                    }
                }
            }
        }

        for (i = 0; i < num_clients; ++i) {
            clientfd = clients[i];

            if (!FD_ISSET(clientfd, &rset)) {
                continue;
            }

            ret = read(clientfd, buf, sizeof(buf));
            if (ret <= 0) {
                //printf("a client has disconnected\n");
                close(clientfd);
                FD_CLR(clientfd, &allset);
                for (j = i + 1; j < num_clients; ++j) {
                    clients[j-1] = clients[j];
                }
                --num_clients;

                if (clientfd == maxfd) {
                    maxfd = listenfd;
                    for (j = 0; j < num_clients; ++j) {
                        if (clients[j] > maxfd) {
                            maxfd = clients[j];
                        }
                    }
                }

                --i;
                continue;
            }

            printf("Client %d: %.*s", clientfd, ret, buf);
        }
    }

    return 0;
}

请注意,

poll()
epoll()
通常比
select()
更好。


0
投票

如何使客户端可以同时运行,而无需等待第一个客户端完成?

每次对

accept()
的调用返回时,启动一个线程
pthread_create()
来处理与客户端的实际通信。

注意:创建/销毁线程非常耗时,因此建议学习

thread pools
以及如何使用它们。

使用线程时,不会调用

select()
(也不是
poll()
),而是主函数在调用
accept()
时被阻塞,并且当该函数返回时,将关联的套接字传递给线程来处理

stackoverflow.com 上有很多关于服务器如何与多个客户端通信的示例代码

© www.soinside.com 2019 - 2024. All rights reserved.