2009-09-16 2 views
3

Я пишу клиент XMLRPC в C++, который предназначен для общения с сервером XMLRPC на python.Ошибка использования сокета при повторном использовании сокетов

К сожалению, в это время, сервер питон XMLRPC способен только выставляя один запрос на соединение, то он выключается, я обнаружил это благодаря, чтобы mhawke ответ на мой предыдущий запрос о related subject

Из-за это, я должен создать новое подключение сокета к моему серверу python каждый раз, когда я хочу сделать запрос XMLRPC. Это означает создание и удаление множества сокетов. Все работает нормально, пока я не подхожу к ~ 4000 запросов. В этот момент я получаю ошибку сокета 10048, Socket in use.

Я пробовал скрыть поток, чтобы winsock исправил свои файловые дескрипторы, трюк, который работал, когда мой клиент python имел идентичную проблему, но безрезультатно. Я попытался следующие

int err = setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,(char*)TRUE,sizeof(BOOL)); 

без успеха.

Я использую Winsock 2.0, так WSADATA :: iMaxSockets не должны вступать в игру, и в любом случае, я проверил и его значение 0 (я предполагаю, что означает бесконечность)

4000 запросов не кажутся странным количеством запросов, которые нужно сделать во время запуска приложения. Есть ли способ использовать SO_KEEPALIVE на стороне клиента, пока сервер постоянно закрывается и снова открывается?

Я что-то не хватает?

ответ

0

Вы закрываете розетки после использования?

+0

да, после каждого запроса, я вызываю closesocket(). Я проверил, и это даже делает последний запрос до возникновения ошибки, поэтому сокет не остается открытым – DanJ

1

Update:

я бросил это в код, и это, кажется, работает.

if(::connect(s_, (sockaddr *) &addr, sizeof(sockaddr))) 
    { 
    int err = WSAGetLastError(); 
    if(err == 10048) //if socket in user error, force kill and reopen socket 
    { 
     closesocket(s_); 
     WSACleanup(); 
     WSADATA info; 
     WSAStartup(MAKEWORD(2,0), &info); 
     s_ = socket(AF_INET,SOCK_STREAM,0); 
     setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,(char*)&x,sizeof(BOOL)); 
    } 
    } 

В принципе, если вы столкнулись с 10048 ошибки (сокет используется), вы можете просто закрыть сокет, вызов очистки, и перезапустить WSA, то сброс сокета и его sockopt

(последний sockopt может не быть необходимости)

я должен был отсутствует WSACleanup/WSAStartup вызовы раньше, потому что closesocket() и гнездо() определенно называют

эта ошибка происходит только один раз каждые 4000ish вызовы.

Мне любопытно, почему это может быть, хотя это, кажется, исправить. Если у кого-то есть какие-либо данные по этому вопросу, мне было бы очень интересно его услышать

11

Проблема возникает из-за того, что в состоянии TIME_WAIT, которое вводится после закрытия гнезда клиента, возникает проблема. По умолчанию сокет будет оставаться в этом состоянии в течение 4 минут, прежде чем он будет доступен для повторного использования. Ваш клиент (возможно, помог другим процессам) потребляет их всех в течение 4-минутного периода. См. this answer за хорошее объяснение и возможное некодевое решение.

Windows динамически выделяет номера портов в диапазоне 1024-5000 (3977 портов), если вы явно не привязываете адрес сокета.Этот код Python демонстрирует проблему:

import socket 
sockets = [] 
while True: 
    s = socket.socket() 
    s.connect(('some_host', 80)) 
    sockets.append(s.getsockname()) 
    s.close() 

print len(sockets)  
sockets.sort() 
print "Lowest port: ", sockets[0][1], " Highest port: ", sockets[-1][1] 
# on Windows you should see something like this... 
3960 
Lowest port: 1025 Highest port: 5000 

Если вы попытаетесь запустить этот immeditaely снова, он должен потерпеть неудачу очень быстро, так как все динамические порты находятся в состоянии TIME_WAIT.

Есть несколько способов обойти это:

  1. Управления ваших собственных назначений портов и использования bind() явно связывают свой клиентский сокет к определенному порту , что вы возрастать после каждый раз, когда ваш создать сокет , У вас все еще будет , чтобы обрабатывать случай, когда порт уже находится , но вы не будете , ограниченный динамическими портами. например

    port = 5000 
    while True: 
        s = socket.socket() 
        s.bind(('your_host', port)) 
        s.connect(('some_host', 80)) 
        s.close() 
        port += 1 
    
  2. Fiddle с SO_LINGER розеткой вариант. Я обнаружил, что это иногда работает в ОС Windows (хотя точно не знаю, почему): s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 1)

  3. Я не знаю, если это поможет в конкретного приложения, однако, можно отправить несколько запросов XMLRPC по сравнению с с использованием метода multicall. В основном это позволяет вам собрать несколько запросов, а затем отправить их сразу . Вы не будете получать ни одного ответа от , пока не отправит приобретенных запросов, так что вы можете Подумайте об этом в тактике Обработка - это подходит к вашим дизайном приложения?

 Смежные вопросы

  • Нет связанных вопросов^_^