Найти - Пользователи
Полная версия: Конфликт socket в threading
Начало » Network » Конфликт socket в threading
1 2 3
yurtaev
Есть class в котором создается клиент с подключением к серверу, но при создание клиентов в нитях, первый клиент создается удачно и подключается к серверу, а все последующие падают с ошибкой:
    self.s.connect((self.HOST, self.PORT))
File "<string>", line 1, in connect
error: [Errno 10056]
Errno 10056 — это ошибка «Socket is already connected», т.е ругается на то что сокет уже используется. Хотелось бы услышать ваши мнение. Заранее благодарен.
Alex2ndr
Я понимаю ситуацию так: каждое tcp соединение идентифицируется парой сокетов - локальным и удаленных. А сокет в свою очередь является парой ip-адрес:порт. Таким образом получаем идентификатор соединения:
IP_локальный:Port_локальный - IP_удаленный:Port_удаленный

Дальнейшие рассуждения такие - если есть ошибка «Socket is already connected», то значит вы пытаетесь создать НЕ УНИКАЛЬНОЕ tcp соединение. Т е с одной и той же машины, с одного и того же порта на один и тот же адрес и порт удаленной машины пытаетесь пробросить еще один коннешен. Вот он и ругается.
yurtaev
Alex2ndr
Я понимаю ситуацию так: каждое tcp соединение идентифицируется парой сокетов - локальным и удаленных. А сокет в свою очередь является парой ip-адрес:порт. Таким образом получаем идентификатор соединения:
IP_локальный:Port_локальный - IP_удаленный:Port_удаленный

Дальнейшие рассуждения такие - если есть ошибка «Socket is already connected», то значит вы пытаетесь создать НЕ УНИКАЛЬНОЕ tcp соединение. Т е с одной и той же машины, с одного и того же порта на один и тот же адрес и порт удаленной машины пытаетесь пробросить еще один коннешен. Вот он и ругается.
Я тоже так подумал сначала, но если запустить программу допустим в 3-х экземплярах, то будет создано 3 удачных соединения, которые я вижу на сервере.
Alex2ndr
Раз вы их видите, так посмотрите какие именно у них пары сокетов. Возможно что каждый экземпляр запускаясь выбирает свой случайный исходящий порт. Это достаточно просто проверить.
yurtaev
Alex2ndr, Ну это логично что они при соединение открывают разные локальные порты. Вопрос в том почему возникает конфликт, и почему в трэдах происходит соединение через один и тот же порт. В идеале это должно решаться автоматически. Например тут http://keysolutions.ru/articles/osnovy-raboty-s-potokami-v-python в примере создания клиента в потоках не чего лишнего не указывается, и пример работает как надо. Пока копаю в документаций по сокетам, но не чего пока не нашёл.
Alex2ndr
А вы для каждого соединения создаете уникальный сокет(т е вызываете socket.socket() ), или используете один и тот же? (меня self.s.connect насторожило). Дальнейшие советы будут зависеть от вашего кода. Выделите ваш сбойный код в пример , показывающий такое поведение и приведите здесь.
yurtaev
Alex2ndr
А вы для каждого соединения создаете уникальный сокет(т е вызываете socket.socket() ), или используете один и тот же? (меня self.s.connect насторожило). Дальнейшие советы будут зависеть от вашего кода. Выделите ваш сбойный код в пример , показывающий такое поведение и приведите здесь.
import socket
import threading
import time

#**************

class client(threading.Thread):

HOST = "127.0.0.1"
PORT = 411
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def connect(self):
self.s.connect((self.HOST, self.PORT))
return

def run(self):
self.connect()
while 1:
self.dispatch(self.getsocketdata(self.s))
return

#дальше идут функиций работы с данными

def getsocketdata(self, somesocket):
#**************

def dispatch(self, message):
#**************

client().start()
time.sleep(2)
client().start()
Alex2ndr
А оно работает? Что-то сомнительно…
Как я могу чем-то помочь, если не могу воспроизвести ваш глюк. Я конечно допишу то, что не достает, но не факт, что оно будет так же как у вас. Вы сделайте минимальный РАБОЧИЙ пример. Т е чтобы запустив его я получил такую же ошибку как у вас. Кстати, если используете самописный сервер, то его тоже приведите(в минимальном виде).

Из того что пока увидел. Вы создаете один общий сокет на класс. Затем экземпляр одного и того же класса вызываете 2 раза. Вызывайте разные экземпляры или создавайте новый сокет каждый раз при соединении.
yurtaev
Alex2ndr
А оно работает? Что-то сомнительно…
Как я могу чем-то помочь, если не могу воспроизвести ваш глюк. Я конечно допишу то, что не достает, но не факт, что оно будет так же как у вас. Вы сделайте минимальный РАБОЧИЙ пример. Т е чтобы запустив его я получил такую же ошибку как у вас. Кстати, если используете самописный сервер, то его тоже приведите(в минимальном виде).

Из того что пока увидел. Вы создаете один общий сокет на класс. Затем экземпляр одного и того же класса вызываете 2 раза. Вызывайте разные экземпляры или создавайте новый сокет каждый раз при соединении.
Код что я привел воспроизводит ошибку ту самую, коненчо нужно запустить такой вариант:
import socket
import threading
import time

class client(threading.Thread):

HOST = "91.144.171.226"
PORT = 411
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def connect(self):
self.s.connect((self.HOST, self.PORT))
print "connect true"
return

def run(self):
self.connect()
return
print "thread 1"
client().start()
time.sleep(2)
print "thread 2"
client().start()
Клиент для работы с хабами DC++, указал адрес своего хаба для теста. Ошибка воспроизводиться, первый трэд подключается успешно, а второй падает с конфликтом. Могу конечно и весь код показать, но и на данном примере ошибка воспроизводится.
Alex2ndr
Интересная задачка
Попробовал ваш пример, только немного переделал под себя. Зачем стучаться к вашему серверу, если все равно ничего не слать. Стучусь к домашнему веб серверу:
#!/usr/bin/env python
# -*-coding: utf-8 -*-
# vim: sw=4 ts=4 expandtab ai
# pylint: disable-msg=C0301

import socket
import threading
import time

class client(threading.Thread):

HOST = "192.168.0.1"
PORT = 80
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def connect(self):
self.s.connect((self.HOST, self.PORT))
print "connect true"
return

def run(self):
self.connect()
return
print "thread 1"
c1 = client()
c1.start()
time.sleep(30)
print "thread 2"
c2 = client()
c2.start()
Тоже выдает ошибку, только немного другую:
 alex@kubu-book:~/development/PYTHON_projects$ python ~/socks_th0.py 
thread 1
connect true
thread 2
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner
self.run()
File "/home/alex/socks_th0.py", line 22, in run
self.connect()
File "/home/alex/socks_th0.py", line 17, in connect
self.s.connect((self.HOST, self.PORT))
File "<string>", line 1, in connect
error: [Errno 106] Transport endpoint is already connected
В сети в это время происходит вот что:
 alex@kubu-book:~/development/PYTHON_projects$ sudo tcpdump -n host 192.168.0.1 and tcp and port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
22:08:41.155830 IP 192.168.0.5.51828 > 192.168.0.1.80: Flags [S], seq 1150385388, win 5840, options [mss 1460,sackOK,TS val 203898 ecr 0,nop,wscale 6], length 0
22:08:41.156080 IP 192.168.0.1.80 > 192.168.0.5.51828: Flags [S.], seq 2554555935, ack 1150385389, win 5792, options [mss 1460,sackOK,TS val 151545188 ecr 203898,nop,wscale 5], length 0
22:08:41.156125 IP 192.168.0.5.51828 > 192.168.0.1.80: Flags [.], ack 1, win 92, options [nop,nop,TS val 203898 ecr 151545188], length 0
22:08:44.153486 IP 192.168.0.1.80 > 192.168.0.5.51828: Flags [S.], seq 2554555935, ack 1150385389, win 5792, options [mss 1460,sackOK,TS val 151545938 ecr 203898,nop,wscale 5], length 0
22:08:44.153536 IP 192.168.0.5.51828 > 192.168.0.1.80: Flags [.], ack 1, win 92, options [nop,nop,TS val 204647 ecr 151545938,nop,nop,sack 1 {0:1}], length 0
22:08:50.153830 IP 192.168.0.1.80 > 192.168.0.5.51828: Flags [S.], seq 2554555935, ack 1150385389, win 5792, options [mss 1460,sackOK,TS val 151547438 ecr 204647,nop,wscale 5], length 0
22:08:50.153879 IP 192.168.0.5.51828 > 192.168.0.1.80: Flags [.], ack 1, win 92, options [nop,nop,TS val 206147 ecr 151547438,nop,nop,sack 1 {0:1}], length 0
22:09:02.354540 IP 192.168.0.1.80 > 192.168.0.5.51828: Flags [S.], seq 2554555935, ack 1150385389, win 5792, options [mss 1460,sackOK,TS val 151550488 ecr 206147,nop,wscale 5], length 0
22:09:02.354591 IP 192.168.0.5.51828 > 192.168.0.1.80: Flags [.], ack 1, win 92, options [nop,nop,TS val 209198 ecr 151550488,nop,nop,sack 1 {0:1}], length 0
22:09:11.194953 IP 192.168.0.5.51828 > 192.168.0.1.80: Flags [F.], seq 1, ack 1, win 92, options [nop,nop,TS val 211408 ecr 151550488], length 0
22:09:11.195511 IP 192.168.0.1.80 > 192.168.0.5.51828: Flags [F.], seq 1, ack 2, win 181, options [nop,nop,TS val 151552698 ecr 211408], length 0
22:09:11.195558 IP 192.168.0.5.51828 > 192.168.0.1.80: Flags [.], ack 2, win 92, options [nop,nop,TS val 211408 ecr 151552698], length 0
^C
12 packets captured
12 packets received by filter
0 packets dropped by kernel
т е как я и предполагал второй сокет выходит с того же самого порта, что и первый - это и порождает такую ошибку. Причем серверу на той стороне это тоже как-то не нравится(я в wireshark покопался в пакетах)

Немного переделал код -
#!/usr/bin/env python
# -*-coding: utf-8 -*-
# vim: sw=4 ts=4 expandtab ai
# pylint: disable-msg=C0301

import socket
import threading
import time

class client(threading.Thread):

def connect(self):
HOST = "192.168.0.1"
PORT = 80
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
print "connect true"
return

def run(self):
self.connect()
return
print "thread 1"
client().start()
time.sleep(2)
print "thread 2"
client().start()
Теперь он отрабатывает как надо -
alex@kubu-book:~/development/PYTHON_projects$ python ./socks_th.py 
thread 1
connect true
thread 2
connect true
И на сетевом уровне тоже все как полагается -
 alex@kubu-book:~/development/PYTHON_projects$ sudo tcpdump -n host 192.168.0.1 and tcp and port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
22:09:58.224501 IP 192.168.0.5.51836 > 192.168.0.1.80: Flags [S], seq 2368199672, win 5840, options [mss 1460,sackOK,TS val 223165 ecr 0,nop,wscale 6], length 0
22:09:58.224720 IP 192.168.0.1.80 > 192.168.0.5.51836: Flags [S.], seq 3761446025, ack 2368199673, win 5792, options [mss 1460,sackOK,TS val 151564454 ecr 223165,nop,wscale 5], length 0
22:09:58.224760 IP 192.168.0.5.51836 > 192.168.0.1.80: Flags [.], ack 1, win 92, options [nop,nop,TS val 223165 ecr 151564454], length 0
22:09:58.225079 IP 192.168.0.5.51836 > 192.168.0.1.80: Flags [F.], seq 1, ack 1, win 92, options [nop,nop,TS val 223165 ecr 151564454], length 0
22:09:58.225634 IP 192.168.0.1.80 > 192.168.0.5.51836: Flags [F.], seq 1, ack 2, win 181, options [nop,nop,TS val 151564454 ecr 223165], length 0
22:09:58.225672 IP 192.168.0.5.51836 > 192.168.0.1.80: Flags [.], ack 2, win 92, options [nop,nop,TS val 223165 ecr 151564454], length 0
22:10:00.227030 IP 192.168.0.5.51837 > 192.168.0.1.80: Flags [S], seq 2392468491, win 5840, options [mss 1460,sackOK,TS val 223666 ecr 0,nop,wscale 6], length 0
22:10:00.227268 IP 192.168.0.1.80 > 192.168.0.5.51837: Flags [S.], seq 3794847646, ack 2392468492, win 5792, options [mss 1460,sackOK,TS val 151564955 ecr 223666,nop,wscale 5], length 0
22:10:00.227307 IP 192.168.0.5.51837 > 192.168.0.1.80: Flags [.], ack 1, win 92, options [nop,nop,TS val 223666 ecr 151564955], length 0
22:10:00.227470 IP 192.168.0.5.51837 > 192.168.0.1.80: Flags [F.], seq 1, ack 1, win 92, options [nop,nop,TS val 223666 ecr 151564955], length 0
22:10:00.227942 IP 192.168.0.1.80 > 192.168.0.5.51837: Flags [F.], seq 1, ack 2, win 181, options [nop,nop,TS val 151564955 ecr 223666], length 0
22:10:00.227989 IP 192.168.0.5.51837 > 192.168.0.1.80: Flags [.], ack 2, win 92, options [nop,nop,TS val 223666 ecr 151564955], length 0
^C
12 packets captured
12 packets received by filter
0 packets dropped by kernel
т е выходит с разных портов (51836 и 51837).

В данном феномене я еще не разобрался. такое ощущение, что инициализация объекта client происходит только один раз. Вероятно threading просто делает fork первого вызванного процесса/класса. Сейчас покопаюсь в книжном примере, приведенном вами выше.

Вероятно правильным решением будет отдельть объект, создающий socket от объекта, создающего новые потоки. Сделать 2 разных объекта.

PS
yurtaev
Пока копаю в документаций по сокетам, но не чего пока не нашёл.
Копатся надо в документации по threading относительно fork.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB