Qgelm

How to Work with TCP Sockets in Python (with Select Example)

Originalartikel

Backup

<html> <p>A network socket is an endpoint of an interprocess communication across a computer network. The Python Standard Library has a module called

socket

which provides a low-level internet networking interface. This interface is common across different programming languages since it uses OS-level system calls.</p> <p>To create a socket, there is a function called

socket

. It accepts

family

,

type

, and

proto

arguments (see documentation for details). To create a TCP-socket, you should use

socket.AF_INET

or

socket.AF_INET6

for

family

and

socket.SOCK_STREAM

for

type

:</p> <pre>import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</pre> <p>It returns a socket object which has the following main methods:</p> <ul><li>

bind()

</li> <li>

listen()

</li> <li>

accept()

</li> <li>

connect()

</li> <li>

send()

</li> <li>

recv()

</li> </ul><p>

bind()

,

listen()

and

accept()

are specific for server sockets.

connect()

is specific for client sockets.

send()

and

recv()

are common for both types. Here is an example of Echo server from documentation:</p> <pre>import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind1) s.listen(1) conn, addr = s.accept() while 1:

  data = conn.recv(1024)
  if not data:
      break
  conn.sendall(data)

conn.close()</pre> <p>Here we create a server socket, bind it to a localhost and 50000 port, and start listening for incoming connections. To accept an incoming connection we call

accept()

method which will block until a new client connects. When this happens, it creates a new socket and returns it together with the client's address. Then, in an infinite cycle, it reads data from the socket in batches of 1024 bytes using method

recv()

until it returns an empty string. After that, it sends all incoming data back using a convenient method

sendall()

which inside repeatedly calls

send()

. And after that it simply closes the client's connection. This example can serve only one incoming connection because it does not call

accept()

in a cycle.</p> <p>A client-side code looks simplier:</p> <pre>import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect2) s.sendall('Hello, world') data = s.recv(1024) s.close() print 'Received', repr(data)</pre> <p>Here instead of

bind()

and

listen()

it calls only

connect()

and immediately sends data to the server. Then it receives 1024 bytes back, closes the socket, and prints the received data.</p> <p>All socket methods are blocking. For example, when it reads from a socket or writes to it the program can't do anything else. One possible solution is to delegate working with clients to separate threads. However, creating threads and switching contexts between them is not really a cheap operation. To address this problem, there is a so-called asynchronous way of working with sockets. The main idea is to delegate maintaining the socket's state to an operating system and letting it notify the program when there is something to read from the socket or when it is ready for writing.</p> <p>There are a bunch of interfaces for different operating systems:</p> <ul><li>poll, epoll (linux)</li> <li>kqueue, kevent (BSD)</li> <li>select (crossplatform)</li> </ul><p>They are all about the same so let&#8217;s create a server using

select

:</p> <pre>import select, socket, sys, Queue server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setblocking(0) server.bind3) server.listen(5) inputs = [server] outputs = [] message_queues = {} while inputs:

  readable, writable, exceptional = select.select(
      inputs, outputs, inputs)
  for s in readable:
      if s is server:
          connection, client_address = s.accept()
          connection.setblocking(0)
          inputs.append(connection)
          message_queues[connection] = Queue.Queue()
      else:
          data = s.recv(1024)
          if data:
              message_queues[s].put(data)
              if s not in outputs:
                  outputs.append(s)
          else:
              if s in outputs:
                  outputs.remove(s)
              inputs.remove(s)
              s.close()
              del message_queues[s]
  for s in writable:
      try:
          next_msg = message_queues[s].get_nowait()
      except Queue.Empty:
          outputs.remove(s)
      else:
          s.send(next_msg)
  for s in exceptional:
      inputs.remove(s)
      if s in outputs:
          outputs.remove(s)
      s.close()
      del message_queues[s]</pre>

<p>As you can see, there is much more code than in the blocking Echo server. That is primarily because we have to maintain a set of queues for different lists of sockets, i.e. writing, reading, and a separate list for erroneous sockets.</p> <p>Creating server socket looks the same except for one line:

server.setblocking(0)

. This is done to make the socket nonblocking. This server is more advanced since it can serve more than one client. The main point is in

selecting

sockets:</p> <pre>readable, writable, exceptional = select.select(

  inputs, outputs, inputs)</pre>

<p>Here we call

select.select

to ask the OS to check given sockets whether they are ready to write, read, or if there is some exception respectively. That is why it passes three lists of sockets to specify which socket is expected to be writable, readable, and which should be checked for errors. This call will block the program (unless a timeout argument is passed) until some of the passed sockets are ready. In this moment, the call will return three lists with sockets for specified operations.</p> <p>Then it sequentially iterates over those lists and, if there are sockets in them, it performs corresponding operations. When there is the server socket in

inputs

, it means that a new client has arrived. Therefore, it calls

accept()

, adds a returned socket to

inputs

and adds a

Queue

for incoming messages which will be sent back. If there is another socket in inputs, then some messages have arrived and ready to be read so it reads them and places them into the corresponding queue.</p> <p>For writable sockets, it gets pending messages (if any) and writes them to the socket. If there is any error in the socket, it removes the socket from the lists.</p> <p>This is how sockets work at a lower level. However, in most cases, there is no need to implement the logic at such a low level. It is recommended to use some higher level abstractions such as Twisted, Tornado, or ZeroMQ, depending on the situation.</p> <p>We welcome you to look at our <a href=„https://steelkiwi.com/projects/“>case study page.</a></p> <p>Learn how to <a href=„https://steelkiwi.com/blog/websocket-server-on-aiohttp-in-django-project/“>integrate WebSocket server written in aiohttp into existing Django project</a> in our article. Or learn how to <a href=„https://steelkiwi.com/blog/an-example-of-a-simple-chat-written-in-aiohttp/“>write a simple chat</a>.</p> </html>

1) , 2) , 3)
'localhost', 50000
Cookies helfen bei der Bereitstellung von Inhalten. Diese Website verwendet Cookies. Mit der Nutzung der Website erklären Sie sich damit einverstanden, dass Cookies auf Ihrem Computer gespeichert werden. Außerdem bestätigen Sie, dass Sie unsere Datenschutzerklärung gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information