Найти - Пользователи
Полная версия: Проблема с select и сокетами
Начало » Network » Проблема с select и сокетами
1 2
lakerman
Использую функцию 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, под винду.
dimabest
Когда s.recv() возвращает пустую строку - значит писатель на другом конце отправил все данные и корректно закрыл соединение.
lakerman
Спасибо, это всё объясняет. Странно что в документации не упомянуто об этом :о(
lakerman
Кстати, ещё пара вопросов остался не разрешенным.
1. Если я на сервер отправляю 10 раз одинаковое сообщение (см. пример выше), то почему они все принимаются серверов как одно большое? Можно ли их как-то “разделить”?
2. Почему клиентский сокет также оказывается в списке rl2, т.е. в списке содержащем сокеты в которые мы можем писать, я же не вызываю у клиента recv или что-то подобное? Как можно определить можно писать в сокет или нет?
o7412369815963
1, потому что они передаются как поток, данные могут склеиться и могут разорваться в любом месте.
2. select предназначен для работы с блокирующими методами, а отправка данных не является блокирующим методом, отправлять можно в любой момент, метод send вернет кол-во отправленных байт, т.е. можно сделать так, имхо:
rl, rl2, rl3 = select.select(checkList, checkList, [])
dimabest
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
o7412369815963
o7412369815963
1, потому что они передаются как поток, данные могут склеиться и могут разорваться в любом месте.
2. select предназначен для работы с блокирующими методами, а отправка данных не является блокирующим методом, отправлять можно в любой момент, метод send вернет кол-во отправленных байт, т.е. можно сделать так, имхо:
rl, rl2, rl3 = select.select(checkList, checkList, [])
забыл код поправить:
rl, rl2, rl3 = select.select(checkList, [], [])
o7412369815963
dimabest
В момент вызова select() операционная система для каждого сокета проверяет - можно ли дописать в буфер еще данных. Если да - возвращая сокет в write списке.
Тогда, в случае если мне не надо отправлять данные, селект беспрерывно будет возвращать сокет. Оно так и есть?
Или сокет возвращается только в случае опустошения буфера?
dimabest
o7412369815963
Тогда, в случае если мне не надо отправлять данные, селект беспрерывно будет возвращать сокет
Функцию select мы просим проверить 3 списка:
1. сокеты, из которых мы читаем - на наличие новых данных
2. сокеты, в которые мы пишем - на наличие свободного места в буфере
3. все сокеты - на наличие ошибок

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

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

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