Неблокирующие сокеты python

Я хотел бы написать небольшое приложение Bluetooth-сервера на телефон Nokia в PyS60. Он должен иметь возможность отправлять ответ на запрос клиента и иметь возможность передавать данные клиенту.

вариант 1 : если я использую socket.recv(1024) , программа ждет, пока что-то не будет получено, поэтому сервер не может передавать данные клиенту. В реализации Python для S60 отсутствует метод socket.settimeout() , поэтому я не мог написать правильный неблокирующий код.

oprion 2 : Метод socket.makefile() выглядел неплохо, но не мог заставить его работать. Когда я заменил conn.recv(1024) на fd = socket.makefile() fd.readline() , он ничего не прочитал.

вариант 3 : просмотрел функцию select() , но не повезло с ней. Когда я изменил conn.recv() на r,w,e = select.select([conn],[],[]) как было предложено, клиент даже не подключается. Он зависает в «Ожидание клиента …». Странный…

Я знаю, что есть неплохие серверные реализации и асинхронные API-интерфейсы, но мне нужен только базовый материал. Заранее спасибо!

вот что у меня есть:

 sock = btsocket.socket(btsocket.AF_BT, btsocket.SOCK_STREAM) channel = btsocket.bt_rfcomm_get_available_server_channel(sock) sock.bind(("", channel)) sock.listen(1) btsocket.bt_advertise_service(u"name", sock, True, btsocket.RFCOMM) print "Waiting for the client..." conn, client_mac = sock.accept() print "connected: " + client_mac while True: try: data = conn.recv(1024) if len(data) != 0: print "received [%s]" % data if data.startswith("something"): conn.send("something\r\n") else: conn.send("some other data \r\n") except: pass 

Это, очевидно, блокирование, поэтому «некоторые другие данные» никогда не отправляются, но это лучшее, что я получил до сих пор. По крайней мере, я могу отправить что-то в ответ клиенту.

    3 Solutions collect form web for “Неблокирующие сокеты python”

    Наконец-то нашел решение!

    Функция select не работала с модулем btsocket новых портов PyS60. Кто-то написал новый_btsocket (доступный здесь ) с рабочей функцией выбора.

    Вот простой пример, основанный на эхо-сервере

     #!/usr/bin/python import socket import select server = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) server.bind( ('localhost', 12556) ) server.listen( 5 ) toread = [server] running = 1 # we will shut down when all clients disconenct while running: rready,wready,err = select.select( toread, [], [] ) for s in rready: if s == server: # accepting the socket, which the OS passes off to another # socket so we can go back to selecting. We'll append this # new socket to the read list we select on next pass client, address = server.accept() toread.append( client ) # select on this socket next time else: # Not the server's socket, so we'll read data = s.recv( 1024 ) if data: print "Received %s" % ( data ) else: print "Client disconnected" s.close() # remove socket so we don't watch an invalid # descriptor, decrement client count toread.remove( s ) running = len(toread) - 1 # clean up server.close() 

    Тем не менее, я по-прежнему считаю, что socketserver чище и проще. Внедрить handle_request и вызвать serve_forever

    Вот реализация сервера Epoll (неблокирующая)

    http://pastebin.com/vP6KPTwH (то же самое, что и ниже, считал, что это может быть проще для копирования)

    используйте python epollserver.py для запуска сервера.

    Проверьте его с помощью wget localhost:8888

     import sys
     импорт, выберите
     import fcntl
     импортировать email.parser
     импортировать StringIO
     import datetime
    
    
     «»»
     Видеть:
     http://docs.python.org/library/socket.html
     «»»
    
     __author__ = ['Caleb Burns', 'Ben DeMott']
    
     def main (argv = None):
         EOL1 = '\ n \ n'
         EOL2 = '\ n \ r \ n'
         response = 'HTTP / 1.0 200 OK \ r \ nДата: Пн, 1 янв 1996 01:01:01 GMT \ r \ n'
         response + = 'Content-Type: text / plain \ r \ nContent-Length: 13 \ r \ n \ r \ n'
         ответ + = 'Привет, мир!'
         serversocket = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
         # Сообщите дескриптору файла сокета сервера, чтобы уничтожить его, когда закончится эта программа.
         socketFlags = fcntl.fcntl (serversocket.fileno (), fcntl.F_GETFD)
         socketFlags | = fcntl.FD_CLOEXEC
         fcntl.fcntl (serversocket.fileno (), fcntl.F_SETFD, socketFlags)
    
         serversocket.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         serversocket.bind (('0.0.0.0', 8888))
         serversocket.listen (1)
         # Используйте асинхронные сокеты.
         serversocket.setblocking (0)
         # Разрешить очередь до 128 запросов (соединений).
         serversocket.listen (128)
         # Слушайте события сокета на сокете сервера, определенные вышеуказанным вызовом bind ().
         epoll = select.epoll ()
         epoll.register (serversocket.fileno (), select.EPOLLIN)
         print "Начался запуск сервера Epoll ..."
    
         пытаться:
             # Словарь соединений сопоставляет файловые дескрипторы (целые числа) с соответствующими объектами сетевого подключения.
             connections = {}
             request = {}
             ответы = {}
             в то время как True:
                 # Спросите epoll, если в каких-либо сокетах есть события и подождите до 1 секунды, если события не присутствуют.
                 events = epoll.poll (1)
                 # fileno - это дескриптор файла.
                 # event - код события (тип).
                 для fileno, событие в событиях:
                     # Проверьте наличие события чтения в сокете, потому что может присутствовать новое соединение.
                     if fileno == serversocket.fileno ():
                         # connection - новый объект сокета.
                         # адрес - это IP-адрес клиента.  Формат адреса зависит от семейства адресов сокета (например, AF_INET).
                         соединение, адрес = serversocket.accept ()
                         # Установите новое сокет-соединение в неблокирующий режим.
                         connection.setblocking (0)
                         # Прослушивание событий чтения в новом сокет-соединении.
                         epoll.register (connection.fileno (), select.EPOLLIN)
                         соединения [connection.fileno ()] = соединение
                         request [connection.fileno ()] = b ''
                         ответы [connection.fileno ()] = ответ
                     # Если произошло событие чтения, прочитайте новые данные, отправленные с клиента.
                     elif event & select.EPOLLIN:
                         запросы [fileno] + = соединения [fileno] .recv (1024)
                         # Как только мы закончим чтение, перестанем слушать события чтения и начнем прослушивать события EPOLLOUT (это скажет нам, когда мы сможем отправить данные обратно клиенту).
                         если EOL1 в запросах [fileno] или EOL2 в запросах [fileno]:
                             epoll.modify (fileno, select.EPOLLOUT)
                             # Распечатайте данные запроса на консоль.
                             epoll.modify (fileno, select.EPOLLOUT)
    
                             data = запросы [fileno]
                             eol = data.find ("\ r \ n") # Это конец строки FIRST
                             start_line = data [: eol] #get содержимое первой строки (которая является информацией протокола)
                             # метод POST | GET и т. д.
                             метод, uri, http_version = start_line.split ("")
                             # re-used facebook httputil library (хорошо работает для нормализации и разбора заголовков)
                             headers = HTTPHeaders.parse (данные [eol:])
                             print "\ nCLIENT: FD:% s% s: '% s'% s"% (fileno, method, uri, datetime.datetime.now ())
    
    
                     # Если клиент готов к приему данных, отправьте ответ.
                     elif event & select.EPOLLOUT:
                         # Отправить ответ на один бит за раз, пока не будет отправлен полный ответ.
                         # ПРИМЕЧАНИЕ. Здесь мы будем использовать sendfile ().
                         byteswritten = connections [fileno] .send (ответы [fileno])
                         ответы [fileno] = ответы [fileno] [byteswritten:]
                         если len (ответы [fileno]) == 0:
                             # Скажите сокет, что мы больше не заинтересованы в событиях чтения / записи.
                             epoll.modify (fileno, 0)
                             # Сообщите клиенту, что мы закончили отправку данных, и он может закрыть соединение.  (хорошая форма)
                             соединения [fileno] .shutdown (socket.SHUT_RDWR)
                     # EPOLLHUP (зависание) означает, что клиент отключился, чтобы очистить / закрыть сокет.
                     elif event & select.EPOLLHUP:
                         epoll.unregister (fileno)
                         соединения [fileno] .close ()
                         del connections [fileno]
         в конце концов:
             # Закройте оставшийся открытый сокет после завершения программы.
             epoll.unregister (serversocket.fileno ())
             epoll.close ()
             serversocket.close ()
    
    
     #! / usr / bin / env python
     #
     # Copyright 2009 Facebook
     #
     # Лицензия на лицензию Apache, версия 2.0 («Лицензия»);  вы можете
     # не использовать этот файл, кроме как в соответствии с Лицензией.  Вы можете получить
     # копия Лицензии на
     #
     # http://www.apache.org/licenses/LICENSE-2.0
     #
     # Если это не предусмотрено действующим законодательством или не согласовано в письменной форме, программное обеспечение
     #, распространяемый по лицензии, распространяется по ОС «AS IS» БЕЗ
     # ГАРАНТИИ ИЛИ УСЛОВИЯ ЛЮБОГО ВИДА, явные или подразумеваемые.  См.
     # Лицензия на конкретный язык, регулирующий разрешения и ограничения
     # по лицензии.
    
     "" "Код утилиты HTTP, разделяемый клиентами и серверами." ""
    
     class HTTPHeaders (dict):
         "" "Словарь, который поддерживает Http-Header-Case для всех ключей.
    
         Поддерживает несколько значений на ключ с помощью пары новых методов,
         add () и get_list ().  Регулярный интерфейс словаря возвращает один
         значение на ключ, с несколькими значениями, соединенными запятой.
    
         >>> h = HTTPHeaders ({"content-type": "text / html"})
         >>> h.keys ()
         ['Тип содержимого']
         >>> h ["Content-Type"]
         'Текст / html'
    
         >>> h.add («Set-Cookie», «A = B»)
         >>> h.add («Set-Cookie», «C = D»)
         >>> h ["set-cookie"]
         'А = В, C = D'
         >>> h.get_list ("set-cookie")
         ['A = B', 'C = D']
    
         >>> для (k, v) в отсортированном (h.get_all ()):
         ... print '% s:% s'% (k, v)
         ...
         Content-Type: text / html
         Set-Cookie: A = B
         Set-Cookie: C = D
         «»»
         def __init __ (self, * args, ** kwargs):
             # Не пропускайте args или kwargs для dict .__ init__, поскольку он будет обходить
             # наш __setitem__
             ДИКТ .__ __ INIT (Я)
             self._as_list = {}
             self.update (* args, ** kwargs)
    
         # новые общедоступные методы
    
         def add (self, name, value):
             "" "Добавляет новое значение для данного ключа." ""
             norm_name = HTTPHeaders._normalize_name (имя)
             если norm_name в себе:
                 # обход нашего переопределения __setitem__, так как он изменяет _as_list
                 dict .__ setitem __ (self, norm_name, self [norm_name] + ',' + значение)
                 self._as_list [norm_name] .append (значение)
             еще:
                 self [norm_name] = значение
    
         def get_list (self, name):
             "" "Возвращает все значения для данного заголовка в виде списка." ""
             norm_name = HTTPHeaders._normalize_name (имя)
             return self._as_list.get (norm_name, [])
    
         def get_all (self):
             "" "Возвращает итерацию всех (имя, значение) пар.
    
             Если заголовок имеет несколько значений, несколько пар будут
             с тем же именем.
             «»»
             для имени, списка в self._as_list.iteritems ():
                 для значения в списке:
                     доходность (имя, стоимость)
    
    
         def items (self):
             return [{ключ: значение [0]} для ключа, значение в self._as_list.iteritems ()]
    
         def get_content_type (self):
             return dict.get (self, HTTPHeaders._normalize_name ('content-type'), None)
    
         def parse_line (self, line):
             "" "Обновляет словарь одной строкой заголовка.
    
             >>> h = HTTPHeaders ()
             >>> h.parse_line («Content-Type: text / html»)
             >>> h.get ('content-type')
             'Текст / html'
             «»»
             name, value = line.split (":", 1)
             self.add (name, value.strip ())
    
         @classmethod
         def parse (cls, headers):
             "" "Возвращает словарь из текста заголовка HTTP.
    
             >>> h = HTTPHeaders.parse («Content-Type: text / html \\ r \\ nContent-Length: 42 \\ r \\ n")
             >>> sorted (h.iteritems ())
             [('Content-Length', '42'), ('Content-Type', 'text / html')]
             «»»
             h = cls ()
             для строки в headers.splitlines ():
                 если строка:
                     h.parse_line (линия)
             return h
    
         переопределение # dict
    
         def __setitem __ (self, name, value):
             norm_name = HTTPHeaders._normalize_name (имя)
             dict .__ setitem __ (self, norm_name, value)
             self._as_list [norm_name] = [значение]
    
         def __getitem __ (self, name):
             return dict .__ getitem __ (self, HTTPHeaders._normalize_name (имя))
    
         def __delitem __ (self, name):
             norm_name = HTTPHeaders._normalize_name (имя)
             dict .__ delitem __ (self, norm_name)
             del self._as_list [norm_name]
    
         def get (self, name, default = None):
             return dict.get (self, HTTPHeaders._normalize_name (имя), по умолчанию)
    
         def update (self, * args, ** kwargs):
             # dict.update обходит наш __setitem__
             для k, v в dict (* args, ** kwargs) .iteritems ():
                 self [k] = v
    
         @staticmethod
         def _normalize_name (имя):
             "" "Преобразует имя в Http-Header-Case.
    
             >>> HTTPHeaders._normalize_name ("coNtent-TYPE")
             'Тип содержимого'
             «»»
             return "-". join ([w.capitalize () для w в name.split ("-")])
    
    
     if (__ name__ == '__main__'):
         sys.exit (основной (sys.argv))
    
     
    Interesting Posts for Van-Lav

    Предотвращение наследования дескриптора файла в многопроцессорной библиотеке lib

    cx_Oracle 'ORA-01843: недействительный месяц' с параметром unicode

    Как удалить последний символ строки RTL в python?

    Установка двоичных модулей Python в пользовательское местоположение в Windows

    Контуры рассеянности в Matplotlib

    Проблема с regexp python и sqlite

    «Интернет», исправление обезьяны функции

    Matplotlib: Можно ли построить линию от одного набора осей к другому?

    Python / Django: отправка писем в фоновом режиме

    Присоединиться к дате даты, основанной на DataFrame, находится между датами от другого DataFrame

    Python проверяет, находится ли объект в списке объектов

    Python numpy.nan и логические функции: неправильные результаты

    Получение даты и времени в этом формате и преобразование в 4-байтовый шестнадцатеричный

    Проверьте, является ли файл более новым, а затем другим файлом?

    Python: эффективно найти дубликат в контейнере

    Python - лучший язык программирования в мире.