Python Network Programming, socket module

ID Python Meetup, 23 Februari 2013

Me

_images/bg.png

Sakti Dwi Cahyono

webdev @ insan infonesia

saktidwicahyono.name

github.com/sakti

@saktidc

Agenda

_images/bg.png

Intro TCP/IP

_images/bg.png

Internet

_images/Internet_map_1024.jpg

source: wikipedia

Internet = Internetwork, interconnecting computer networks

Apa itu TCP/IP

Kumpulan protocol kumunikasi yang digunakan pada Internet atau network sejenis.

Protocol stack paling popular untuk wide area network.

Transmission Control Protocol (675, 793, 1122, 2581), Internet Protocol (ipv4: 791).

Seberapa besar?

IPv4 menggunakan 32-bit untuk alamat IP.

$python -c "print '{0:,}'.format(2 ** 32)"

4,294,967,296

Sudah habis pada tanggal 3 Februari 2011.

Layered Model

Cara Kerja

_images/ip_stack_connections.png

source: wikipedia

Enkapsulasi Data

_images/udp_encapsulation.png

source: wikipedia

Alamat IP (Internet Layer)

IPv4

32 bit

74.125.135.113

IPv6

128 bit

2404:6800:4003:802::1007

Nomor Port (Transport Layer)

16 bit unsigned integer (1 - 65535).

Well-known 1 - 1023
Registered 1024 - 49151
Dynamic 49152 - 65535
$cat /etc/services
.
.
ftp-data    20/tcp
ftp         21/tcp
fsp         21/udp      fspd
ssh         22/tcp              # SSH Remote Login Protocol
ssh         22/udp
telnet      23/tcp
smtp        25/tcp      mail
.

Byte-Order

Jika data lebih panjang dari 1 byte. Bagaimana cara menyusunnya?

int         2 bytes
long        4 bytes
long long   8 bytes

Decimal 11111 = 0x2b67

Big-endian

0x2b 0x67

Little-endian

0x67 0x2b

Socket

_images/bg.png

Apa itu socket?

Everything in Unix is a file.

"a way to speak to other programs using standard Unix file descriptors."

Beej Jorgensen

File descriptor

#include <stdio.h>

int main(void)
{
    fprintf(stdout, "%s", "write to stdout\n");
    fprintf(stderr, "%s", "write to stderr\n");
    printf("%d %d %d\n", fileno(stdin), fileno(stdout),
        fileno(stderr));
    return 0;
}

output:

write to stdout
write to sterr
0 1 2

File descriptor, python version

import sys

if __name__ == '__main__':
    sys.stdout.write('write to stdout\n')
    sys.stderr.write('write to stderr\n')
    print('%d %d %d' % (sys.stdin.fileno(),
        sys.stdout.fileno(),
        sys.stderr.fileno()))

Python socket module

$ipython
In [1]: import socket
In [2]: socket?
..
Docstring:
This module provides socket operations and some related functions.
On Unix, it supports IP (Internet Protocol) and Unix domain sockets.
On other systems, it only supports IP. Functions specific for a
socket are available as methods of the socket object.
.

Object-based Interface untuk low level socket (POSIX)

Internet Socket

In [3]: socket.SOCK
socket.SOCK_DGRAM      socket.SOCK_RDM        socket.SOCK_STREAM
socket.SOCK_RAW        socket.SOCK_SEQPACKET

SOCK_DGRAM

Datagram socket, connectionless socket.

SOCK_STREAM

Stream socket, reliable two-way connected communication stream.

Quiz

Kegunaan TCP vs. UDP?

socket API

socket.socket([family[, type[, proto]]])

Membuat object socket berdasarkan address family (default: AF_INET), tipe stream (SOCK_DGRAM atau default: SOCK_STREAM) dan protocol (0).

Socket Object

accept()

Menerima koneksi, mengembalikan socket yang dibuat dan alamat client

bind(addr)

bind socket ke alamat local

close()

menutup socket

connect(addr)

membuat koneksi dengan alamat remote

getpeername()

mengembalikan alamat remote

getsockname()

mengembalikan alamat local

Socket Object (cont)

listen(n)

memulai listening koneksi yang masuk

recv(buflen[, flags])

menerima data

recvfrom(buflen[, flags])

menerima data dan alamat pengirim

sendall(data[, flags])

mengirim semua data

send(data[, flags])

mengirim data, mungkin tidak semuanya

shutdown(how)

shut down traffic, satu arah maupun dua arah

Client & Server

_images/bg.png

Echo Server

Client mengirim pesan msg, server membalas dengan pesan msg yang sama.

Echo Server UDP

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

 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 s.bind(('127.0.0.1', 5555))

 while True:
     msg, address = s.recvfrom(65535) # bytes
     print('client %s mengirim %s' % (address, msg))
     s.sendto(msg, address)
     print('balasan dikirim')

Echo Client UDP

1
2
3
4
5
6
7
8
 import socket

 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 s.sendto('Isi pesan', ('127.0.0.1', 5555))

 # menerima pesan
 msg, address = s.recvfrom(65535) # bukan hanya dari server
 print('server %s membalas: %s' % (address, msg))

Echo Client UDP 2

1
2
3
4
5
6
7
8
9
 import socket

 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 s.connect(('127.0.0.1', 5555))
 s.send('Isi pesan')

 # menerima pesan
 msg = s.recv(65535)
 print('server membalas: %s' % msg)

Mengirim & Menerima data

Mengapa ada send dan sendall?

Apa yang terjadi ketika method send dan recv dipanggil?

Kemungkinan mengirim

bytes_sent = 0
while bytes_sent < len(msg):
    msg_remaining = message[bytes_sent:]
    bytes_sent += s.send(msg_remaining)

Kemungkinan menerima

def recv_all(sock, length):
    temp = ''
    while len(data) < length:
        more = sock.recv(length - len(temp))
        if not more:
            raise EOFError('panjang tidak sesuai')
        temp += more
    return temp

recv_until

# utils.py
def recv_until(sock, eof_char='.'):
    temp = ''
    char = ''

    while char != eof_char:
        char = sock.recv(1)
        if char:
            temp += char
        else:
            break

    return temp

Echo Server TCP

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

 # passive socket
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 s.bind(('127.0.0.1', 5555))
 s.listen(1)

 while True:
     # mengembalikan active/connected socket
     sc, sockname = s.accept()
     print('koneksi dari %s' % repr(sockname))
     msg = recv_until(sc)
     sc.sendall(msg)
     sc.close()
     print('balasan dikirim')

Echo Client TCP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 import socket
 from utils import recv_until

 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.connect(('127.0.0.1', 5555))
 print('socket local %s' % repr(s.getsockname()))
 s.sendall('Isi pesan.')
 msg = recv_until(s)
 print('balasan server: %s' % msg)
 s.close()

Caesar cipher

_images/caesar_cipher.png

Format pesan

(d/e)(shift)(message).

d: Decrypt (geser kiri) ?
e: Encrypt (geser kanan) ?
shift: jumlah pergeseran (2 digit)
message: plaintext / ciphertext (lowercase)

Caesar cipher module

1
2
3
4
5
6
7
8
 # caesar.py
 from string import maketrans
 from string import ascii_lowercase as al

 def cipher(msg, shift=3):
     shifted = al[shift:] + al[:shift]
     trans_table  = maketrans(al, shifted)
     return msg.translate(trans_table)

Caesar cipher Server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 import socket
 from utils import recv_until
 from caesar import cipher

 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 s.bind(('127.0.0.1', 5555))
 s.listen(1)
 while True:
     sc, sockname = s.accept()
     print('koneksi dari %s' % repr(sockname))
     msg = recv_until(sc)
     try:
         nshift = int(msg[1:3])
     except ValueError:
         nshift = 0
     nshift = -nshift if msg[0] == 'd' else nshift
     msg = cipher(msg[3:], nshift)
     sc.sendall(msg)
     sc.close()

Caesar chiper Client

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

 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.connect(('127.0.0.1', 5555))
 s.sendall('e10isi pesan.')
 msg = recv_until(s)
 print('balasan server: %s' % msg)
 s.close()

 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.connect(('127.0.0.1', 5555))
 s.sendall('d10%s' % msg)
 msg = recv_until(s)
 print('balasan server: %s' % msg)
 s.close()

Simple Port Scanner

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

 if __name__ == '__main__':
     target = raw_input('target: ')

     for port in range(20, 1025):
         s = socket.socket()
         s.settimeout(0.01)  # dalam detik

         result = s.connect_ex((target, port))

         if result == 0:
             print('port %d: OPEN' % port)

         s.close()

End

_images/bg.png

Q & A