вторник, 13 мая 2014 г.

Типы сокетов

  SOCK_STREAM
          Provides sequenced,  reliable,  two-way,    connection-based  byte
          streams.    An out-of-band data transmission mechanism may be sup-
          ported. (потоковый сокет)

       SOCK_DGRAM
          Supports datagrams (connectionless,  unreliable  messages     of  a
          fixed maximum length). (датаграммный сокет)

       SOCK_SEQPACKET
          Provides    a  sequenced,  reliable, two-way connection-based data
          transmission path for datagrams of fixed maximum length; a  con-
          sumer is required to read an entire packet with each read system
          call. (надёжная служба последовательных пакетов)

       SOCK_RAW
          Provides raw network protocol access.

       SOCK_RDM
          Provides a reliable  datagram  layer  that  does    not  guarantee
          ordering.

       SOCK_PACKET
          Obsolete    and should not be used in new programs; see packet(7).

Basically SOCK_DGRAM is used for UDP packets, SOCK_STREAM for TCP 


Указатели протоколов создаваемого сокета

These constants represent the address (and protocol) families, used for the first argument to socket():

socket.AF_UNIX - локальных сокетов (адрес определяется как строка, содержащая путь в файловой системе /etc/tmp).
socket.AF_INET
- для сетевого протокола IPv4 или
socket.AF_INET6
- для сетевого протокола IPv6


If the AF_UNIX constant is not defined then this protocol is unsupported.

unix, AF_UNIX, AF_LOCAL - Sockets for local interprocess communication

The difference is that an INET socket is bound to an IP address, while a UNIX socket is "bound" to a special file on your filesystem -- only processes having access to that filesystem (i.e. running on the same machine) can communicate through the latter.

http://manpages.ubuntu.com/manpages/lucid/man7/unix.7.html
http://unixhelp.ed.ac.uk/CGI/man-cgi?socket+2

Сокеты теория: SOCK_STREAM и SOCK_DGRAM

Что такое сокет?


Вы постоянно слышите разговоры о каких-то "сокетах" и, наверно, вам интересно, что же это такое. В общем, изначально сокеты - это способ общения программ друг с другом, используя файловые дескрипторы Unix.

Что?

Ок -- возможно, вы слышали от какого-нибуть Unix-хакера фразу типа "господи, всё, что есть в Unix - файлы!" Этот человек, возможно, имел в виду, что программы в Unix при абсолютно любом вводе-выводе читают или пишут в файловый дескриптор. Дескриптор файла - это простое целое число, связанное операционной системой с открытым файлов. Но (и в этом заключается ловушка) файлом может быть и сетевое подключение, и FIFO, и пайпы, и терминал, и реальный файл на диске, и просто что угодно другое. Всё в UNIX - это файл! Итак, просто поверьте, что собираясь общаться с другой программой через интернет, вам придется делать это через дескриптор файла.

"Эй, умник, а откуда мне взять этот дескриптор файла для работы в сети?" Отвечу.
Вы совершаете системный вызов socket(). Он возвращает дескриптор сокета, и вы общаетесь через него с помощью системных вызовов send() и recv() (man send, man recv).

"Но, эй!" могли бы вы воскликнуть. "Если это дескриптор файла, почему я не могу использовать простые функции read() и write(), чтобы общаться через него?". Ответ прост: "Вы можете!". Немного развернутый ответ: "Вы можете, но send() и recv() предлагают гораздо больший контроль над передачей ваших данных."

Что дальше? Как насчет этого: бывают разные виды сокетов. Есть DARPA инернет-адреса (Сокеты интернет), CCITT X.25 адреса (X.25 сокеты, которые вам не нужны), и, вероятно, многие другие в зависимости от особенностей вашей ОС. Этот документ описывает только первые, Интернет-Сокеты.


Два типа интернет-сокетов

Что? Есть два типа интернет сокетов? Да. Ну ладно, нет, я вру. Есть больше, но я не хочу вас пугать. Есть ещё raw-сокеты, очень мощная штука, вам стоит взглянуть на них.

Ну ладно. Какие два типа? Один из них - "потоковый сокет", второй - "сокет дейтаграмм", в дальнейшем они будут называться "SOCK_STREAM" и "SOCK_DGRAM" соответственно. Дейтаграммные сокеты иногда называют "сокетами без соединения" (хотя они могут и connect()`иться, если вам этого действительно захочется. См. connect() ниже.)

Потоковые сокеты обеспечивают надёжность своей двусторонней системой коммуникации. Если вы отправите в сокет два элемента в порядке "1, 2", они и "собеседнику" придут в том же порядке - "1, 2". Кроме того, обеспечивается защита от ошибок.

Что использует потоковые сокеты? Ну, вы наверно слышали о программе Telnet, да? Телнет использует потоковый сокет. Все символы, которые вы печатаете, должны прибыть на другой конец в том же порядке, верно? Кроме того, браузеры используют протокол HTTP, который в свою очередь использует потоковые сокеты для получения страниц. Если вы зайдёте телнетом на любой сайт, на порт 80 и наберёте что-то вроде "GET / HTTP/1.0" и нажмете ввод два раза, на вас свалится куча HTML ;)

Как потоковые сокеты достигают высокого уровня качества передачи данных? Они используют протокол под названием "The Transmission Control Protocol", иначе - "TCP". TCP гарантирует, что ваши данные передаются последовательно и без ошибок. Возможно, ранее вы слышали о TCP как о половине от "TCP/IP", где IP - это "Internet Protocol". IP имеет дело в первую очередь с маршрутизацей в Интернете и сам по себе не отвечает за целостность данных.

Круто. А что насчёт дейтаграммных сокетов? Почему они называются без-соединительными? В чем тут дело? Почему они ненадежны?
Ну, вот некоторые факты: если вы посылаете дейтаграмму, она может дойти. А может и не дойти. Но если уж приходит, то данные внутри пакета будут без ошибок.

Дейтаграммные сокеты также используют IP для роутинга, но не используют TCP; они используют "User Datagram Protocol", или "UDP".

Почему UDP не устанавливает соединения? Потому что вам не нужно держать открытое соединение с потоковыми сокетами. Вы просто строите пакет, формируете IP-заголовок с информацией о получателе, и посылаете пакет наружу. Устанавливать соединение нет необходимости. UDP как правило используется либо там, где стек TCP недоступен, либо там, где один-другой пропущеный пакет не приводит к концу света. Примеры приложений: TFTP (trivial file transfer protocol, младшый брат FTP), dhcpcd (DHCP клиент), сетевые игры, потоковое аудио, видео конференции и т.д.

"Подождите минутку! TFTP и DHCPcd используются для передачи бинарных данных с одного хоста на другой! Данные не могут быть потеряны, если вы хотите нормально с ними работать! Что это за темная магия?"

Нуу, мой человеческий друг, TFTP и подобные программы обычно строят свой собственный протокол поверх UDP. Например, TFTP протокол гласит, что для каждого принятого пакета получатель должен отправить обратно пакет, говорящий "я получил его!" ("ACK"-пакет). Если отправитель исходного пакета не получает ответ, скажем, в течение 5 секунд, он отправит пакет повторно, пока, наконец, не получит ACK. Подобные процедуры очень важны для реализации надёжных приложений, использующих SOCK_DGRAM.

Для приложений, не требующих такой надёжности - игры, аудио или видео, вы просто игнорируете потерянные пакеты или, возможно, пытаетесь как-то их компенсировать. (Игроки в quake обычно называют это явление "проклятый лаг", и "проклятый" - это ещё крайне мягкое высказывание).

Зачем вам может понадобиться использовать ненадежный базовый протокол? По двум причинам: скорость и скорость. Этот способ гораздо быстрее, выстрелил-и-забыл, чем постоянное слежение за тем, всё ли благополучно прибыло получателю. Если вы отправляете сообщение в чате, TCP великолепен, но если вы шлёте 40 позиционных обновлений персонажа в секунду, может быть, не так и важно, если один или два из них потеряются, и UDP тут будет неплохим выбором.

http://masandilov.ru/network/guide_to_network_programming__what_is_socket

понедельник, 12 мая 2014 г.

Non-blocking mode and Timeouts Sockets errors in python

 By default a socket is configured so that sending or receiving data blocks, stopping program execution until the socket is ready (setblocking(1)). Calls to send() wait for buffer space to be available for the outgoing data, and calls to recv() wait for the other program to send data that can be read. This form of I/O operation is easy to understand, but can lead to inefficient operation and even deadlocks, if both programs end up waiting for the other to send or receive data.

There are a few ways to work around this situation. One is to use a separate thread for communicating with each socket. This can introduce other complexities, though, with communication between the threads.

Another option is to change the socket to not block at all, and return immediately if it is not ready to handle the operation. Use the setblocking() method to change the blocking flag for a socket. The default value is 1, which means to block. Passing a value of 0 turns off blocking. If the socket is has blocking turned off and it is not ready for the operation, then socket.error is raised.

A compromise solution is to set a timeout value for socket operations. Use settimeout() to change the timeout of a socket to a floating point value representing the number of seconds to block before deciding the socket is not ready for the operation. When the timeout expires, a timeout exception is raised.

In the case of a non blocking socket that has no data available, recv will throw the socket.error exception and the value of the exception will have the errno of either EAGAIN or EWOULDBLOCK.


import sys
import socket
import fcntl, os
import errno
from time import sleep

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK)

while True:
    try:
        msg = s.recv(4096)
    except socket.error, e:
        err = e.args[0]
        if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
            sleep(1)
            print 'No data available'
            continue
        else:
            # a "real" error occurred
            print e
            sys.exit(1)
    else:
        # got a message, do something :)


The situation is a little different in the case where you've enabled non-blocking behavior via a time out with s.settimeout(n)


import sys
import socket
from time import sleep

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
s.settimeout(2)

while True:
    try:
        msg = s.recv(4096)
    except socket.timeout, e:
        err = e.args[0]
        # this next if/else is a bit redundant, but illustrates how the
        # timeout exception is setup
        if err == 'timed out':
            sleep(1)
            print 'recv timed out, retry later'
            continue
        else:
            print e
            sys.exit(1)
    except socket.error, e:
        # Something else happened, handle error, exit, etc.
        print e
        sys.exit(1)
    else:
        if len(msg) == 0:
            print 'orderly shutdown on server end'
            sys.exit(0)
        else:
            # got a message do something :) 
But remember The socket.makefile() docs say, "the socket must be in blocking mode.
sock.settimeout(None)
Another method:

import select

mysocket.setblocking(0)

ready = select.select([mysocket], [], [], timeout_in_seconds)
if ready[0]:
    data = mysocket.recv(4096)

SIGPIPE ERROR With Python

Have you ever seen a socket.error: [Errno 32] Broken pipe message when running a Python Web server and wondered what that means?
The rule is that when a process tries to write to a socket that has already received an RST packet, the SIGPIPE signal is sent to that process which causes the Broken pipe socket.error exception.
Here are two scenarios that you can try that cause SIGPIPE signal to be fired.
1. Server may send an RST packet to a client to abort the socket connection but the client ignores the packet and continues to write to the socket.
To test that behavior install Cynic, run it
$ cynic
INFO     [2012-06-08 05:06:37,040] server: Starting 'HTTPHtmlResponse'   on port 2000
INFO     [2012-06-08 05:06:37,040] server: Starting 'HTTPJsonResponse'   on port 2001
INFO     [2012-06-08 05:06:37,040] server: Starting 'HTTPNoBodyResponse' on port 2002
INFO     [2012-06-08 05:06:37,040] server: Starting 'HTTPSlowResponse'   on port 2003
INFO     [2012-06-08 05:06:37,040] server: Starting 'RSTResponse'        on port 2020
INFO     [2012-06-08 05:06:37,040] server: Starting 'RandomDataResponse' on port 2021
INFO     [2012-06-08 05:06:37,040] server: Starting 'NoResponse'         on port 2022
INFO     [2012-06-08 05:06:37,041] server: Starting 'LogRecordHandler'   on port /tmp/_cynic.sock
 and then run the client1.py:
import socket

# connect to Cynic's RSTResponse service on port 2020
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('', 2020))
 
# first read gets an RST packet
try:
    s.recv(1024)
except socket.error as e:
    print e
    print
 
# write after getting the RST causes SIGPIPE signal
# to be sent to this process which causes a socket.error
# exception
s.send('hello')
 and see what happens:
$ python ./client1.py
[Errno 104] Connection reset by peer
 
Traceback (most recent call last):
  File "./client1.py", line 17, in
    s.send('hello')
socket.error: [Errno 32] Broken pipe
2. Server can send an RST to the client’s SYN request to indicate that there is no process wating for connections on the host at the specified port, but the client tries to write to the socket anyway.
To test it run the client2.py:
import socket
 
# connect to the port that nobody is listening on
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
# gets us an RST packet
try:
    s.connect(('', 30301))
except socket.error as e:
    print e
    print
 
# write after getting RST causes SIGPIPE signal
# to be sent to this process which causes an exception
s.send('hello')
Here is the output: 
$ python ./client2.py
[Errno 111] Connection refused
 
Traceback (most recent call last):
  File "./sigpipe2.py", line 15, in
    s.send('hello')
socket.error: [Errno 32] Broken pipe
 I hope that clarifies a SIGPIPE’s nature a little bit.

воскресенье, 11 мая 2014 г.

Как остановить python скрипт: exit, quit...


  • Use exit() or quit() in the REPL.

  • Use sys.exit() in scripts, or raise SystemExit() if you prefer.
    sys.exit raises SystemExit and the variable "e" will contain the exit code

  • Use os._exit() for child processes to exit after a call to os.fork().
    os._exit(1) executing direct exit, without throwing an exception

Let me give some information on them:
  1. quit raises the SystemExit exception behind the scenes.
    Furthermore, if you print it, it will give a message:
    >>> print (quit)
    Use quit() or Ctrl-Z plus Return to exit
    >>>
    
    This functionality was included to help people who do not know Python. After all, one of the most likely things a newbie will try to exit Python is typing in quit.
    Nevertheless, quit should not be used in production code. This is because it only works if the site module is loaded. Instead, this function should only be used in the interpreter.
  2. exit is an alias for quit (or vice-versa). They exist together simply to make Python more user-friendly.
    Furthermore, it too gives a message when printed:
    >>> print (exit)
    Use exit() or Ctrl-Z plus Return to exit
    >>>
    
    However, like quit, exit is considered bad to use in production code and should be reserved for use in the interpreter. This is because it too relies on the site module.
  3. sys.exit raises the SystemExit exception in the background. This means that it is the same as quit and exit in that respect.
    Unlike those two however, sys.exit is considered good to use in production code. This is because the sys module will always be there.
  4. os._exit exits the program without calling cleanup handlers, flushing stdio buffers, etc. Thus, it is not a standard way to exit and should only be used in special cases. The most common of these is in the child process(es) created by os.fork.
    Note that, of the four methods given, only this one is unique in what it does.
Summed up, all four methods exit the program. However, the first two are considered bad to use in production code and the last is a non-standard, dirty way that is only used in special scenarios. So, if you want to exit a program normally, go with the third method: sys.exit.

Or, even better in my opinion, you can just do directly what sys.exit does behind the scenes and run:
raise SystemExit
This way, you do not need to import sys first.
However, this choice is simply one on style and is purely up to you.