lwip несколько ответных сокетов (и один listensocket)

В настоящее время я разрабатываю BSD-подобный API сокетов. Ранее я задавал еще один актуальный вопрос:

Сейчас я использую следующую структуру:

Один поток прослушивания, который открывает новый поток, если соединение установлено:

portTASK_FUNCTION( vModbusServer, pvParameters ) {
int lSocket;
struct sockaddr_in sLocalAddr;

lSocket = lwip_socket(AF_INET, SOCK_STREAM, 0);

if (lSocket < 0) return; // check error

memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr));
sLocalAddr.sin_family = AF_INET;
sLocalAddr.sin_len = sizeof(sLocalAddr);
sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sLocalAddr.sin_port = MODBUS_PORT;

if (lwip_bind(lSocket, (struct sockaddr *)&sLocalAddr, sizeof(sLocalAddr)) < 0) {
    lwip_close(lSocket);
    return;
}

if ( lwip_listen(lSocket, 20) != 0 ) {
    lwip_close(lSocket);
    return;
}

while (1) {
    vTaskDelay(1);
    int clientfd;
    struct sockaddr_in client_addr;
    int addrlen=sizeof(client_addr);

    clientfd = lwip_accept(lSocket, (struct sockaddr*)&client_addr, (socklen_t)&addrlen);
    
    if (clientfd>0) {
        sys_thread_new( "MODBUSResponder", vModbusResponder, ( void * ) &clientfd,
               lwipMODBUS_SERVER_STACK_SIZE,
               lwipMODBUS_SERVER_PRIORITY );
    }
}
lwip_close(lSocket); }

Эта ветка будет создана (ответная ветка):

vModbusResponder( void *pvParameters ) {
char buffer[12];
int nbytes;
int *temp = (int*) pvParameters;
int clientfd = *temp;

do {
    nbytes=lwip_recv(clientfd, buffer, sizeof(buffer),0);
    if (nbytes>0) { //no error

    }
}  while (nbytes>0);
lwip_close(clientfd);
vTaskDelete(NULL); }

Это идеально работает для двух соединений (два ответных потока), например, для двух ПК, которые подключаются к встроенному устройству, но если я попытаюсь подключиться к третьему ПК, первое соединение будет разорвано. Я не понимаю, почему это происходит. Соединение разрывается от встроенного устройства (lwip) с помощью RST. Если я подключаю четвертый компьютер, второе соединение разрывается.

Я пробовал следующие вещи:

отредактируйте эту строку так:

определить SYS_THREAD_MAX 30

Может кто-нибудь дать несколько советов? Я что-то не так?


person Conara    schedule 12.05.2015    source источник
comment
Я не думаю, что это ваша проблема, но то, как вы передаете clientfd, небезопасно. Нет никакой гарантии, что ваш разветвленный поток будет работать и уважать pvParameter до того, как выполнение вернется обратно к исходному потоку (который переназначит clientfd при следующем вызове accept())   -  person Joel Cunningham    schedule 15.05.2015
comment
Для соединений, которые сбрасываются, возвращают ли они код ошибки, который может помочь указать, что происходит не так? Из того, что я видел с LwIP, если у вас закончились печатные платы (блок управления протоколом) для новых соединений, он должен RST для нового соединения, а не для существующего соединения.   -  person Joel Cunningham    schedule 15.05.2015


Ответы (1)


Я нашел решение: я не редактировал следующие конфигурации:

#define MEMP_NUM_TCP_PCB        2 // I edited this to 5
#define MEMP_NUM_NETBUF         3 // I edited this to 5
#define MEMP_NUM_NETCONN        4 // I edited this to 5

Теперь я могу установить 5 одновременных TCP-соединений. И если я попытаюсь открыть 6-е соединение, он отправит RST.

@ Джоэл Каннингем:

Что вы имеете в виду под «но то, как вы передаете clientfd, небезопасно. Нет никакой гарантии, что ваш разветвленный поток будет работать и уважать pvParameter до того, как выполнение вернется обратно к исходному потоку (который повторно назначит clientfd на следующем call to accept())" Что RTOS переключится обратно на прослушивающий сокет до того, как откроет TCP-соединение? Каков наилучший способ предотвратить это? Дать ему более высокий приоритет, чем прослушивающий сокет?

person Conara    schedule 15.05.2015
comment
Самый простой способ безопасно передать сокет — по значению, а не по ссылке. Таким образом, вам не нужно иметь исходное хранилище при получении параметра. При вызове sys_new_thread приведите clientfd к void * вместо приведения &clientfd. Если вы не проделывали этот трюк раньше, вы передаете значение clientfd в пространстве, предоставленном void *, а не адресом clientfd. На принимающей стороне приведите pvParameters к int, и у вас есть сокет :) - person Joel Cunningham; 01.06.2015