Уведомления

Группа в Telegram: @pythonsu

#1 Март 23, 2011 17:54:35

lakerman
От:
Зарегистрирован: 2011-03-23
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

Использую функцию select и одноименного модуля для того чтобы отслеживать “активные” сокеты, т.е. те с которых можно читать и в которые можно писать. Насколько я понял функция возвращает кортеж из трех списков - первый состоит из сокетов из которых можно что-то прочитать (т.е. которые у себя там вызвали send или connect и теперь можно получить их сообщения), второй список - в которые можно что-то написать (т.е. которые сделили у себя recv и теперь мы можем им что-то послать с помощью send). Однако, по-моему функция работает как-то неправильно. (или я что-то не правильно понял?)
Я пишу что-то типа:

  mainSock = socket(AF_INET, SOCK_STREAM)
mainSock.bind(('', port))
mainSock.listen(10)

checkList = []
checkList.append(mainSock)

while True:
rl, rl2, rl3 = select.select(checkList, checkList, [])
for s in rl:
if s == mainSock:
client, adr = s.accept()
checkList.append(client)
else:
try:
print "Received: " + s.recv(maxLength)
except:
checkList.remove(s)
Т.е. все соединения принимаем, и просто печатаем сообщения. Однако почему-то в списке rl оказываются и те сокеты, которые ничего не присылали. Например, если запустить следующий скрипт (типа клиент), то сервер принимает одно большое сообщения состоящее из 10 PING?, а затем крутится в бесконечном цикле и выводит следующую строку “Received: ” - то есть принимает пустое собщение от клиента, который уже давным-давно отключился. Хотя по идее когда клиент отключился или ничего не посылал, то вызов select должен заблокировать сервер.

  s = socket(AF_INET, SOCK_STREAM)
s.connect(("localhost", port)) # socket used to communicate to coordinator

for i in xrange(10):
s.send("PING?")

s.shutdown(0)
s.close()
В общем, надеюсь всё более-менее понятно расписал, если кто сталкивался с такой проблемой - подскажите плз что я делаю не так.

ЗЫ использую CPython 2.7.1, под винду.



Офлайн

#2 Март 23, 2011 19:18:02

dimabest
От:
Зарегистрирован: 2009-02-12
Сообщения: 253
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

Когда s.recv() возвращает пустую строку - значит писатель на другом конце отправил все данные и корректно закрыл соединение.



Отредактировано (Март 23, 2011 19:20:25)

Офлайн

#3 Март 24, 2011 14:48:33

lakerman
От:
Зарегистрирован: 2011-03-23
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

Спасибо, это всё объясняет. Странно что в документации не упомянуто об этом :о(



Офлайн

#4 Март 24, 2011 16:25:30

lakerman
От:
Зарегистрирован: 2011-03-23
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

Кстати, ещё пара вопросов остался не разрешенным.
1. Если я на сервер отправляю 10 раз одинаковое сообщение (см. пример выше), то почему они все принимаются серверов как одно большое? Можно ли их как-то “разделить”?
2. Почему клиентский сокет также оказывается в списке rl2, т.е. в списке содержащем сокеты в которые мы можем писать, я же не вызываю у клиента recv или что-то подобное? Как можно определить можно писать в сокет или нет?



Офлайн

#5 Март 24, 2011 18:30:48

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

1, потому что они передаются как поток, данные могут склеиться и могут разорваться в любом месте.
2. select предназначен для работы с блокирующими методами, а отправка данных не является блокирующим методом, отправлять можно в любой момент, метод send вернет кол-во отправленных байт, т.е. можно сделать так, имхо:

rl, rl2, rl3 = select.select(checkList, checkList, [])

Офлайн

#6 Март 24, 2011 20:16:48

dimabest
От:
Зарегистрирован: 2009-02-12
Сообщения: 253
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

1. способы разделения сообщений http://python.su/forum/viewtopic.php?pid=65262#p65262

2. Как работают сокеты? Для каждого сокета ОС создает два буфера - для записи (отправки) и чтения (приема) данных. Узнать размеры буферов можно так:

# send buffer
print s.getsockopt(SOL_SOCKET, SO_SNDBUF)
# recv buffer
print s.getsockopt(SOL_SOCKET, SO_RCVBUF)
Данные, которые приходят от удаленного клиента накапливаются в RECV буфере. В момент вызова select() операционная система для каждого сокета проверяет - есть ли в его буфере данные. Если есть - сокет возвращается в списке read.

Данные, которые ты хочешь отослать удаленному клиенту, сперва записываются в SEND буфер. По мере отправки данных в сеть буфер освобождается. В момент вызова select() операционная система для каждого сокета проверяет - можно ли дописать в буфер еще данных. Если да - возвращая сокет в write списке. Если в буфере свободно 1000 байт, а ты пытаешся записать 2000 байт, то записано будет только 1000. Поэтому s.send() возвращает количество принятых байт и это нужно учитывать в программе

лучше переименуй свои r1, r12, r13 в:
r - read
w - write
e - error



Офлайн

#7 Март 24, 2011 21:36:38

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

o7412369815963
1, потому что они передаются как поток, данные могут склеиться и могут разорваться в любом месте.
2. select предназначен для работы с блокирующими методами, а отправка данных не является блокирующим методом, отправлять можно в любой момент, метод send вернет кол-во отправленных байт, т.е. можно сделать так, имхо:
rl, rl2, rl3 = select.select(checkList, checkList, [])
забыл код поправить:
rl, rl2, rl3 = select.select(checkList, [], [])

Офлайн

#8 Март 24, 2011 21:41:41

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

dimabest
В момент вызова select() операционная система для каждого сокета проверяет - можно ли дописать в буфер еще данных. Если да - возвращая сокет в write списке.
Тогда, в случае если мне не надо отправлять данные, селект беспрерывно будет возвращать сокет. Оно так и есть?
Или сокет возвращается только в случае опустошения буфера?

Офлайн

#9 Март 24, 2011 23:48:03

dimabest
От:
Зарегистрирован: 2009-02-12
Сообщения: 253
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

o7412369815963
Тогда, в случае если мне не надо отправлять данные, селект беспрерывно будет возвращать сокет
Функцию select мы просим проверить 3 списка:
1. сокеты, из которых мы читаем - на наличие новых данных
2. сокеты, в которые мы пишем - на наличие свободного места в буфере
3. все сокеты - на наличие ошибок

Если из сокета нужно только читать - передаем параметром функции select() в 1м и 3м списках. Если в сокет нужно только писать - передаем во 2м и 3м списках. Если одновременно нужно читать и писать - передаем во всех трех списках.

Вот и все :)



Отредактировано (Март 24, 2011 23:54:06)

Офлайн

#10 Март 25, 2011 15:00:48

lakerman
От:
Зарегистрирован: 2011-03-23
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с select и сокетами

Всем спасибо, кажется я всё понял.

Кстати, есть ли какие-нибудь библиотеки для сетевого взаимодействия, в которых есть более высокоуровенные операции, а все нюансы скрыты?



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version