c - 如何使用select同时读取连接?套接字/c

标签 c sockets 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;
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)

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

最佳答案

How do I make it so the clients can run concurrently without waiting for the first client to finish?

通过注意 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() 更好的选择。

关于c - 如何使用select同时读取连接?套接字/c,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49637893/

相关文章:

c++ - 通过套接字正确发送 HTTP 响应

sql - 选择日期范围之间的计数

Mysql数据库选择关系

c++ - 如何在 C 或 C++ 中获得与 Java 中的 toLowerCase 或 Python 中的 string.lower() 相同的结果?

c - 这个 `ld` 错误 ("undefined reference") 是什么意思?

java - ObjectInputStream 不会从输出流中读取所有对象

c++ - 3次握手丢包

php - Mysql从特定值开始选择和获取表数据

c - 分配连续序列不起作用

C编程: EXC_BAD_ACCESS