如何使用TCP和&的select()函数UDP连接?

时间:2018-03-13 08:42:40

标签: sockets tcp udp

我有一个只有TCP连接的功能服务器代码。现在我希望服务器从UDP连接接收。我使用端口2000用于TCP&端口2001用于UDP。这是我的代码片段

    struct timeval timeout; // timeout for select(), 1ms
    timeout.tv_sec  = 0;
    timeout.tv_usec = 1000;
    fd_set master; // master file descriptor list
    fd_set read_fds; // temp file descriptor list for select()
    int fdmax; // maximum file descriptor number

    FD_ZERO(&master); // clear the master and temp sets
    FD_ZERO(&read_fds);

// TCP port setup
    int sockfd; // listening socket descriptor
    int newsockfd; // newly accept()ed socket descriptor
    struct sockaddr_storage remoteaddr; // client address
    socklen_t addrlen;

    char buf_tcp[256]; // buffer for client data
    char buf_copy_tcp[256];    
    int recv_bytes;

    char remoteIP[INET6_ADDRSTRLEN];

    int yes=1; // for setsockopt() SO_REUSEADDR
    int i, k, rv_getaddrinfo, rv_setsockopt, rv_bind, rv_listen, rv_select;

    struct addrinfo hints, *servinfo, *ptr;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    rv_getaddrinfo = getaddrinfo(NULL, "2000", &hints, &servinfo);

    for(ptr=servinfo; ptr!=NULL; ptr=ptr->ai_next) 
    {
        sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

        rv_setsockopt = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

        rv_bind = bind(sockfd, ptr->ai_addr, ptr->ai_addrlen);

        break;
    }

    if (ptr == NULL) 
    {
        fprintf(stderr, "CLI Server error: failed to bind\n\r");
        exit(2);
    }

    freeaddrinfo(servinfo); // all done with this

    rv_listen = listen(sockfd, 10);
////////////////////////////////////////////////////////////////////////////////////////////////

// UDP port setup
    int sockfd_udp; // listening socket descriptor
    struct sockaddr_storage remoteaddr_udp; // client address
    socklen_t addrlen_udp;

    char buf_udp[256]; // buffer for client data
    char buf_copy_udp[256];    
    int recv_bytes_udp;

    char remoteIP_udp[INET6_ADDRSTRLEN];

    int yes_udp=1; // for setsockopt() SO_REUSEADDR
    int j, rv_getaddrinfo_udp, rv_setsockopt_udp, rv_bind_udp;

    struct addrinfo hints_udp, *servinfo_udp, *ptr_udp;

    memset(&hints_udp, 0, sizeof(hints_udp));
    hints_udp.ai_family = AF_UNSPEC;
    hints_udp.ai_socktype = SOCK_DGRAM;
    hints_udp.ai_flags = AI_PASSIVE;

    rv_getaddrinfo_udp = getaddrinfo(NULL, "2001", &hints_udp, &servinfo_udp);

    for(ptr_udp=servinfo_udp; ptr_udp!=NULL; ptr_udp=ptr_udp->ai_next) 
    {
        sockfd_udp = socket(ptr_udp->ai_family, ptr_udp->ai_socktype, ptr_udp->ai_protocol);

        rv_setsockopt_udp = setsockopt(sockfd_udp, SOL_SOCKET, SO_REUSEADDR, &yes_udp, sizeof(int));

        rv_bind_udp = bind(sockfd_udp, ptr_udp->ai_addr, ptr_udp->ai_addrlen);

        break;
    }

    if (ptr_udp == NULL) 
    {
        fprintf(stderr, "CLI UDP Server error: failed to bind\n\r");
        exit(2);
    }

    freeaddrinfo(servinfo_udp); // all done with this
//////////////////////////////////////////////////////////////////////////////////////////////////////////

    // add the listener to the master set
    FD_SET(sockfd, &master);
    FD_SET(sockfd_udp, &master);

    // keep track of the biggest file descriptor
    if(sockfd > sockfd_udp)
        fdmax = sockfd; // so far, it's this one
    else
        fdmax = sockfd_udp; // so far, it's this one

    do
    {   
        read_fds = master; // copy it

        rv_select = select(fdmax+1, &read_fds, NULL, NULL, &timeout);

        // run through the existing connections looking for data to read
        for(i=0; i<=fdmax; i++) 
        {
            if (FD_ISSET(i, &read_fds)) 
            { // we got one!!
                if (i == sockfd) 
                {
                    // handle new connections
                    addrlen = sizeof(remoteaddr);
                    newsockfd = accept(sockfd, (struct sockaddr *)&remoteaddr, &addrlen);

                    FD_SET(newsockfd, &master); // add to master set
                    if (newsockfd > fdmax)      // keep track of the max
                        fdmax = newsockfd;

                    inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), remoteIP, INET6_ADDRSTRLEN);
                    fprintf(stdout, "CLI Server: new connection from %s on socket %d\n\r", remoteIP, newsockfd);
                } 
                else if (i == sockfd_udp) 
                {
                    // handle new udp connections
                    addrlen_udp = sizeof(remoteaddr_udp);
                    recv_bytes_udp = recvfrom(i, buf_udp, sizeof(buf_udp), 0, (struct sockaddr *)&remoteaddr_udp, &addrlen_udp); 
                    inet_ntop(remoteaddr_udp.ss_family, get_in_addr((struct sockaddr*)&remoteaddr_udp), remoteIP_udp, INET6_ADDRSTRLEN);
                    for(j=0; j<=recv_bytes_udp; j++)
                    {
                        if( (buf_udp[k] == '\r') | (buf_udp[k] == '\n') )
                            buf_udp[k] = '\0';
                    }
                    fprintf(stdout, "CLI UDP Server: received %s from connection %s\n\r", buf_udp, remoteIP_udp);
                } 
                else 
                {   // handle data from a client
                    if ((recv_bytes = recv(i, buf_tcp, sizeof(buf_tcp), 0)) <= 0) 
                    {   // got error or connection closed by client
                        if (recv_bytes == 0) // connection closed
                        {
                            fprintf(stdout, "CLI Server: socket %d hung up\n\r", i);
                        }
                        else 
                        {
                            perror("CLI Server error: recv");
                            exit(6);
                        }

                        close(i); // bye!
                        FD_CLR(i, &master); // remove from master set
                    } 
                    else 
                    {
                        for(k=0; k<=recv_bytes; k++)
                        {
                            if( (buf_tcp[k] == '\r') | (buf_tcp[k] == '\n') )
                                buf_tcp[k] = '\0';
                        }
                        fprintf(stdout, "CLI Server: received %s from socket %d\n\r", buf_tcp, i);
                    }
                } // END handle data from client
            } // END got new incoming connection
        } // END looping through file descriptors
    } while(QUIT);     

我正在每个阶段进行错误检查,但没有将其包含在代码段中。当我编译&amp;运行这个,我可以连接到端口2000但不能连接到2001,我的Tera术语终端关闭连接被拒绝消息。为什么客户端无法连接到端口2001(UDP套接字),而是连接到端口2000(TCP套接字)。服务器只响应客户端消息,直到客户端进入QUIT。

我已经从Beej的网络编程指南selectserver.c代码中建模了这段代码。

1 个答案:

答案 0 :(得分:0)

我上面写的程序是正确的,但我对套接字编程的理解不是。这是一个侥幸,我设法编写正确的代码,但感谢@EJP在评论中进行了扩展讨论,以澄清我的疑问。

我的错误是使用Teraterm的TCP客户端连接到UDP服务器。两种通信都是相互排斥的。因此无法相互沟通。所以我不得不使用UDP客户端。 Netcat使用netcat -u <ip address> <port>提供UDP客户端选项。然后我的UDP服务器能够从UDP客户端接收消息。

另一个错误是在DATAGRAM套接字中将bind()与connect()混淆。连接的DGRAM就是当我在服务器和服务器上使用连接时。客户。

我认为问题出在select()上,因为我错误地认为UDP&amp; TCP套接字不能在select()中同时使用。但上面的代码是你为多个客户端编写UDP / TCP服务器的方法。

再次感谢Beej&amp; @EJP