在 C 中轮询 TCP 连接

Posted

技术标签:

【中文标题】在 C 中轮询 TCP 连接【英文标题】:Polling TCP connections in C 【发布时间】:2016-12-19 07:49:54 【问题描述】:

我正在做一个学校任务,我必须制作一个获取 TCP 连接的服务器。然后这些连接传递数据。我使用 poll() 查看是否有新的连接请求,或者是否有来自现有连接的传入数据。它与环回地址(127.0.0.1)一起使用。

当我有 1 个连接时它工作正常,但是当我打开第二个连接时,它会停止轮询并等待来自新连接的数据。 奇怪的是,如果我打开第三个连接,它也会给出第三个连接的数据,但仍然没有轮询,只是等待。

我读了很多关于它的文章,我坚持了一段时间。

我在 connmgr.c 中编写了服务器代码(有很多 printf 用于发现我的错误)。我会在下面解释:

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <assert.h>

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <syslog.h>
#include <poll.h>

#include "connmgr.h"

#define _GNU_SOURCE

static FILE * write_file;
struct pollfd *poll_list;
int serversd;
int conn_counter = 0;

int dplist_errno;

dplist_t * list = NULL;
list_node_t * dummy = NULL;


void * element_copy(void *element);
void element_free(void **element);
int element_compare(void *x, void *y);


void add_poll(struct pollfd * polllist, tcpsock_t *client, tcpsock_t *server)
conn_counter++;
polllist = realloc(polllist,sizeof(struct pollfd)*conn_counter+1);
int clientsd;
tcp_get_sd(client, &clientsd);
polllist[conn_counter].fd= clientsd;
//printf("fd in add_poll = %d \n",clientsd);
polllist[conn_counter].events = POLLIN;

    int stop = 0;
    tcpsock_t *server, *client;

void connmgr_listen(int port_number)

    sensor_data_t data;
dummy = malloc(sizeof(list_node_t));

write_file = fopen("sensor_data_recv", "w");

poll_list = malloc(sizeof(struct pollfd));
list = dpl_create(&element_copy, &element_free, &element_compare);

if(tcp_passive_open(&server,port_number)!=TCP_NO_ERROR)exit(EXIT_FAILURE);
tcp_get_sd(server, &serversd);

poll_list[0].fd = serversd;
poll_list[0].events = POLLIN;

printf("server fd = %d \n",serversd);

printf("start with fd %d = %d \n", 0,poll_list[0].fd);

int bytes,result;
int test = 0;

while(1)

    test++;
    int sd;
    int return_value,i;

    return_value = poll(poll_list,conn_counter+1,TIMEOUT);

    if (return_value>0)
        if(poll_list[0].revents & POLLIN>0)
            printf("add client\n");

            tcpsock_t * requestclient;
            if (tcp_wait_for_connection(server,&requestclient)!=TCP_NO_ERROR) exit(EXIT_FAILURE);
            tcp_get_sd(requestclient, &sd);
            dummy->sd = sd;
            printf("inserted sd = %d \n",sd);
            dummy->client = requestclient;
            time(&(dummy->ts));
            list = dpl_insert_at_index(list, dummy, conn_counter,true);



            //printf("sd from client = %d \n", tcp_get_sd(client, &sd));

            add_poll(poll_list, dummy->client, server);
            printf("conn_counter = %d \n",conn_counter);

        

        //for (i=0; i<conn_counter;i++)
        i=0;
        while(i<conn_counter)
            if(poll_list[i+1].revents & POLLIN>0)

                printf("poll in %d \n",i+1);

                dummy = (list_node_t *) dpl_get_element_at_index(list,i);
                time(&(dummy->ts));
                client = dummy->client;
                sd = dummy->sd;

                /*for(int l = 0; l<conn_counter;l++)
                        printf("sensor %d \n",l);

                */

                bytes = sizeof(data.id);
                printf("@ line %d en i = %d \n", __LINE__,i);result = tcp_receive(client,(void *)&data.id,&bytes); 

                bytes = sizeof(data.value);
                printf("@ line %d \n", __LINE__);result = tcp_receive(client,(void *)&data.value,&bytes);

                bytes = sizeof(data.ts);
                printf("@ line %d \n", __LINE__);result = tcp_receive(client,(void *)&data.ts,&bytes);


                if ((result==TCP_NO_ERROR) && bytes)
                printf("sensor id = %" PRIu16 " - temperature = %g - timestamp = %ld\n", data.id, data.value, (long int)data.ts);
                        

                else
                    if(result == TCP_CONNECTION_CLOSED)printf("peer left \n");
                    else"error in peerconnection \n";
                    tcp_close(&client);
                    list = dpl_remove_at_index(list, i, true);
                    
                fflush(stdout);
            
            if (poll_list[i+1].revents & POLLHUP)
                printf("client disconnected \n");
                poll_list[conn_counter+1].fd=-1;
                poll_list[conn_counter+1].events=0;
                fflush(stdout);
            i++;
               
    
    //if(return_value<0)exit(EXIT_FAILURE);
    //if(stop == 1)break;
    printf("end of while %d \n",test);




一旦服务器在 tcp_passive_open 中启动,它将开始轮询,直到 return_value 为高。然后它检查 POLLIN 是否在 [0],这意味着一个新的连接请求。如果没有,我检查 poll_list 中的所有连接,从 1 开始直到 conn_counter。如果没有,我检查 POLLHUP 并将该客户端的 fd 设置为 -1,因此 poll() 将忽略它。

我认为它应该是我使用 while 循环检查 POLLIN 是否来自其中一个连接的地方,因为我可以毫无问题地添加连接。

dpl_... 函数来自我自己编写的双链表库,应该可以正常工作。他们创建一个列表,在索引处获取一个元素...该列表使用 3 个回调函数。

tcp_... 函数是我从教授那里得到的函数。 tcpsock.h 文件应该提供足够的信息:

typedef struct tcpsock tcpsock_t;

int get_size_tcpsock();

// All functions below return TCP_NO_ERROR if no error occurs during execution

int tcp_passive_open(tcpsock_t ** socket, int port);
/* Creates a new socket and opens this socket in 'passive listening mode' (waiting for an active connection setup request) 
 * The socket is bound to port number 'port' and to any active IP interface of the system
 * The number of pending connection setup requests is set to MAX_PENDING
 * The newly created socket is returned as '*socket'
 * This function is typically called by a server
*/

int tcp_active_open(tcpsock_t ** socket, int remote_port, char * remote_ip);
/* Creates a new TCP socket and opens a TCP connection to the system with IP address 'remote_ip' on port 'remote_port'
 * The newly created socket is return as '*socket'
 * This function is typically called by a client 


int tcp_close(tcpsock_t ** socket); 
/* The socket '*socket' is closed , allocated resources are freed and '*socket' is set to NULL
 * If '*socket' is connected, a TCP shutdown on the connection is executed
 */

int tcp_wait_for_connection(tcpsock_t * socket, tcpsock_t ** new_socket); 
/* Puts the socket 'socket' in a blocking wait mode
 * Returns when an incoming TCP connection setup request is received
 * A newly created socket identifying the remote system that initiated the connection request is returned as '*new_socket'
 */

int tcp_send(tcpsock_t * socket, void * buffer, int * buf_size );
/* Initiates a send command on the socket 'socket' and tries to send the total '*buf_size' bytes of data in 'buffer' (recall that the function might block for a while)
 * The function sets '*buf_size' to the number of bytes that were really sent, which might be less than the initial '*buf_size'
 */

int tcp_receive (tcpsock_t * socket, void * buffer, int * buf_size);
/* Initiates a receive command on the socket 'socket' and tries to receive the total '*buf_size' bytes of data in 'buffer' (recall that the function might block for a while)
 * The function sets '*buf_size' to the number of bytes that were really received, which might be less than the inital '*buf_size'
 */

int tcp_get_ip_addr( tcpsock_t * socket, char ** ip_addr);
/* Set '*ip_addr' to the IP address of 'socket' (could be NULL if the IP address is not set)
 * No memory allocation is done (pointer reference assignment!), hence, no free must be called to avoid a memory leak
 */

int tcp_get_port(tcpsock_t * socket, int * port); 
/* Return the port number of the 'socket'
 */

int tcp_get_sd(tcpsock_t * socket, int * sd); 
/* Return the socket descriptor of the 'socket'
 */

感谢您阅读本文,希望您能帮助我!

ps,当我断开一个 tcp 然后再连接另一个时,我仍然有一些问题,但那是以后的问题:)

【问题讨论】:

【参考方案1】:

在你的函数中

void add_poll(struct pollfd * polllist, tcpsock_t *client, tcpsock_t *server)

您正在重新分配包含套接字 fd 的 pollfd 缓冲区,但您在临时指针变量 (polllist) 中而不是全局变量“poll_list”中获得结果。如果 Realloc 可以扩展它,它可能会也可能不会将内存移动到另一个位置。

当你回到你的循环中,什么都没有改变,你仍然使用同一个缓冲区——可能已经被释放了!——包含服务器套接字和第一个连接,并且在内存中的其他地方,有一个缓冲区用你的 3 个插座。

所以要么在指针上传递一个指针(struct pollfd **polllist),要么改为设置“poll_list”变量:)。

另外,如果我没记错的话,在你的循环中,当你这样做时:

if(poll_list[i+1].revents & POLLIN>0)

“>”运算符的优先级高于“&”运算符,因此相当于:

if(poll_list[i+1].revents & (POLLIN>0))

只需删除“> 0”,或在表达式周围加上括号

【讨论】:

嘿,谢谢你的回答,但我改变了你所说的,它保持不变......我现在做 'poll_list = realloc(poll_list,sizeof(struct pollfd)*conn_counter+1); ' ,但没有效果。您还有其他可能的解决方案吗? :) 当心你的sizeof(struct pollfd)*conn_counter+1,因为它等同于(sizeof(struct pollfd)*conn_counter)+1,与sizeof(struct pollfd)*(conn_counter+1) 不是一回事。除此之外,您是否也更改了polllist[conn_counter].fd= clientsd;(以及下面的类似行)?还有POLLIN&gt;0 的东西?您能否解释一下“如果我打开第三个连接,它确实也提供了第三个连接的数据,但仍然没有轮询,只是在等待”是什么意思? (另外,请确保在创建新投票集时清除“revent”字段) 是的,我做了更改,但这并没有帮助。我的意思是,如果我打开第二个连接,它会停止轮询第一个连接,只提供来自第二个连接的数据。 (这种情况在没有轮询的情况下发生,因为它不再循环通过 wile-loop,只是等待数据。我可以看到,因为虚拟的“while 测试结束”)如果我打开第三个连接,它会提供来自第二个和第三个连接,但再次没有轮询,只是等待数据,而不是循环遍历。所以就像你说的,也许还有一个 POLLIN 标志高?

以上是关于在 C 中轮询 TCP 连接的主要内容,如果未能解决你的问题,请参考以下文章

在python中轮询键盘(检测按键)

在windows中轮询CTS线路的状态

在 IE 中轮询的好的 setTimeout 间隔是多少?

如何避免在 React Native 中轮询服务器以获取新数据?

确定何时播放缓冲区而不在 OpenAL 中轮询源

如何在 Spring Integration 中轮询时根据主题过滤电子邮件