Домой Edit me on GitHub

2019-06-19

Каналы передачи данных | Сетевое программирование | Базы данных | Основы Веб-программирования

Передача данных через INET сокеты

../../../../_images/socket.svg

TCP пример

Это простой пример эхо-сервера при помощи TCP.

../../../../_images/tcp_socket.gif

TCP клиент

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import socket

TCP_IP = '127.0.0.1'
TCP_PORT = 5005
BUFFER_SIZE = 1024
MESSAGE = b'Hello, World!'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()

print("received data: {}".format(data))

В роли клиента может выступать утилита telnet

$ telnet localhost 5005

TCP сервер

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import socket

TCP_IP = '127.0.0.1'
TCP_PORT = 5005
BUFFER_SIZE = 20  # Normally 1024, but we want fast response

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)

conn, addr = s.accept()
print("Connection address: {}".format(addr))
while 1:
    data = conn.recv(BUFFER_SIZE)
    if not data:
        break
    print("received data: {}".format(data))
    conn.send(data)  # echo
conn.close()

Способы определения длины сообщения:

  1. Передать отдельно
  2. Читать до разделителя (в http это пустая строка)
  3. Передать в заголовке (в http это content-length)
  4. Договориться что размер будет фиксированным (как в примере)
  5. Читать данные пока не вернется 0

UDP пример

Это простой пример приемо-передачи сообщений при помощи UDP.

../../../../_images/udp_socket.gif

UDP клиент

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import socket

UDP_IP = "127.0.0.1"
UDP_PORT = 5005
MESSAGE = b"Hello, World!"

print("UDP target IP: {}".format(UDP_IP))
print("UDP target port: {}".format(UDP_PORT))
print("message: {}".format(MESSAGE))

sock = socket.socket(socket.AF_INET,     # Internet
                     socket.SOCK_DGRAM)  # UDP
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))

В роли клиента может выступать утилита netcat

$ nc 127.0.0.1 5005 -u

UDP сервер

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import socket

UDP_IP = "127.0.0.1"
UDP_PORT = 5005

sock = socket.socket(socket.AF_INET,     # Internet
                     socket.SOCK_DGRAM)  # UDP
sock.bind((UDP_IP, UDP_PORT))

while True:
    data, addr = sock.recvfrom(1024)  # buffer size is 1024 bytes
    print("received message: {}".format(data))

Сырые сокеты

Сырой сокет - разновидность сокетов Беркли, позволяющий собирать TCP/IP-пакеты, контролируя каждый бит заголовка и отправляя в сеть нестандартные пакеты.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import socket
import struct
import binascii

ETH_P_ALL = 0x0003

rawSocket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
                          socket.htons(ETH_P_ALL))

while True:

    packet = rawSocket.recvfrom(2048)

    ethernet_header = packet[0][0:14]
    ethernet_detailed = struct.unpack("!6s6s2s", ethernet_header)

    arp_header = packet[0][14:42]
    arp_detailed = struct.unpack("2s2s1s1s2s6s4s6s4s", arp_header)

    # skip non-ARP packets
    ethertype = ethernet_detailed[2]
    if ethertype != '\x08\x06':
        continue

    print("****************_ETHERNET_FRAME_****************")
    print("Dest MAC:        ", binascii.hexlify(ethernet_detailed[0]))
    print("Source MAC:      ", binascii.hexlify(ethernet_detailed[1]))
    print("Type:            ", binascii.hexlify(ethertype))
    print("************************************************")
    print("******************_ARP_HEADER_******************")
    print("Hardware type:   ", binascii.hexlify(arp_detailed[0]))
    print("Protocol type:   ", binascii.hexlify(arp_detailed[1]))
    print("Hardware size:   ", binascii.hexlify(arp_detailed[2]))
    print("Protocol size:   ", binascii.hexlify(arp_detailed[3]))
    print("Opcode:          ", binascii.hexlify(arp_detailed[4]))
    print("Source MAC:      ", binascii.hexlify(arp_detailed[5]))
    print("Source IP:       ", socket.inet_ntoa(arp_detailed[6]))
    print("Dest MAC:        ", binascii.hexlify(arp_detailed[7]))
    print("Dest IP:         ", socket.inet_ntoa(arp_detailed[8]))
    print("*************************************************\n")
# python 1.raw_socket_sniff.py
****************_ETHERNET_FRAME_****************
Dest MAC:         ffffffffffff
Source MAC:       0024b2841922
Type:             0806
************************************************
******************_ARP_HEADER_******************
Hardware type:    0001
Protocol type:    0800
Hardware size:    06
Protocol size:    04
Opcode:           0001
Source MAC:       0024b2841922
Source IP:        192.168.1.1
Dest MAC:         000000000000
Dest IP:          192.168.1.27
*************************************************

****************_ETHERNET_FRAME_****************
Dest MAC:         ffffffffffff
Source MAC:       0024b2841922
Type:             0806
************************************************
******************_ARP_HEADER_******************
Hardware type:    0001
Protocol type:    0800
Hardware size:    06
Protocol size:    04
Opcode:           0001
Source MAC:       0024b2841922
Source IP:        192.168.1.1
Dest MAC:         000000000000
Dest IP:          192.168.1.27
*************************************************

HTTP клиент

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import socket

# TCP/IP socket
sock_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock_obj.connect(('httpbin.org', 80))
sock_obj.send(b"GET /ip HTTP/1.1\r\nHost: httpbin.org\r\n\r\n")

while True:
    resp = sock_obj.recv(1024)
    if not resp:
        break
    print(resp)

# Close the connection when completed
sock_obj.close()
$ python 1.http_socket.py
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 19 Feb 2015 12:50:07 GMT
Content-Type: application/json
Content-Length: 32
Connection: close
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "origin": "82.186.14.112"
}
Previous: Передача данных через UNIX сокеты Next: Стек протоколов TCP/IP