Skip to content
On this page

Versuch Python Chat

Die Transportschicht ist zuständig für die Bereitstellung einer verbindungslose (Datagramm), oder verbindungsorientierten Kommunikation zwischen zwei Endpunkten. UDP (verbindungslos) und TCP (verbindungsorientiert) sind hierbei die dominanten Protokolle, welche für Datennetze eingesetzt werden.

Lektüre

Bitte lesen Sie folgende Abschnitte im Buch "Computernetzwerke"

  • 6.4.1 Einführung in UDP
  • 6.5.1 - 6.5.8 TCP

Chat über TCP

Folgen Sie dem Tutorial zu Python TCP hier bis zum Teil "Communication Breakdown ": https://realpython.com/python-sockets/.

Bauen Sie zu zweit eine einfache Chat Applikation in Python - orientieren Sie sich an diesem Beispiel: https://wiki.python.org/moin/TcpCommunication und dem Beispiel von Versuch: Raw Socket .

Gehen Sie wie folgt vor:

  1. Erstellen Sie zuerst einen Server, welcher auf Port 1337 hört.
    1. Lesen Sie vom Port ...
    2. ... und geben Sie die Daten auf stdout aus.
  2. Verwenden Sie auf der Client seite netcat um dem Server daten zu senden.
  3. Erstellen Sie nun in Python separat einen Client.
    1. Sie können die IP Adresse zuerst hardcoden.
    2. Senden Sie eine konstante Nachricht.
  4. Input von STDIN lesen
    1. Schauen Sie sich ein paar Beispiele hier an https://en.wikibooks.org/wiki/Python_Programming/Input_and_Output
    2. senden Sie nun bei jeder neuen Zeile den Input an den Server
  5. Implementieren Sie das einlesen von Input auch auf der Serverseite mittels einfachem Thread
    1. Folgen Sie dem Beispiel, dass Sie hier finden https://realpython.com/intro-to-python-threading/

Frage

  • Was wäre anders, wenn Sie UDP anstatt TCP verwenden würden?
  • Wie können Sie mehrere Clients gleichzeitig bedienen?
  • Bonus: Implementieren Sie einen Multi-Client Chat

Beispiel UDP Client

py
#!/usr/bin/python3

import socket
import sys

PORT = 7890


def main():
    print("Welcome to the chat client using Port {}".format(PORT))
    client_sock = socket.socket(
        socket.AF_INET,
        socket.SOCK_DGRAM)

    name, ip, msg = sys.argv
    client_sock.sendto("{}".format(msg).encode(), (ip, PORT))


if __name__ == "__main__":
    main()

Beispiel UDP Server

py
#!/usr/bin/python3

import socket

PORT = 7890


def main():
    print("Welcome to the chat server! Using PORT: {}".format(PORT))
    server_sock = socket.socket(
        socket.AF_INET,
        socket.SOCK_DGRAM)

    server_sock.bind(("0.0.0.0", PORT))

    while True:
        msg, (ip, port) = server_sock.recvfrom(1024)
        print("{}: {}".format(ip, msg.decode()))


if __name__ == "__main__":
    main()

Beispiel TCP Client

py
import socket
from threading import Thread

PORT = 1337
LEN = 4096


def handle_send(sock: socket.socket):
    try:
        while 1:
            msg = input("")
            sock.send(msg.encode())
    except Exception as e:
        print(e)
        pass


def main():
    hostname = input("hostname of server: ")
    print("connecting to {}".format(hostname))
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((hostname, PORT))

    t = Thread(target=handle_send, args=(s,))
    t.start()

    try:
        while 1:
            data = s.recv(LEN)
            print(data.decode())
    except Exception as e:
        print(e)
        pass


if __name__ == "__main__":
    main()

Beispiel TCP Server

py
import socket
import logging
from threading import Thread, Lock

HOST = '0.0.0.0'
PORT = 1337
LEN = 4096

lock = Lock()
peers = list()


class Peer:
    conn: socket.socket
    addr: object

    def __init__(self, conn: socket.socket, addr: object):
        self.conn = conn
        self.addr = addr


def broadcast(peer: Peer, data):
    if not data:
        return

    lock.acquire()
    safe_peers = peers.copy()
    lock.release()

    for p in safe_peers:
        if p is peer:
            continue
        try:
            print(data)
            p.conn.send('{}: {}\n'.format(p.addr[0], data).encode())
        except:
            logging.exception("error")


def handle_connetion(conn: socket.socket, addr: (str, int)):
    peer = Peer(conn=conn, addr=addr)

    lock.acquire()
    peers.append(peer)
    lock.release()

    broadcast(peer, '[has entered the room]')

    try:
        while 1:
            data = conn.recv(LEN)
            if not data:
                break
            broadcast(peer, data.decode())
    except:
        logging.exception("error")

    broadcast(peer, '[has left the room]')

    print('shutting down connection for host: {}'.format(addr[0]))
    conn.close()

    lock.acquire()
    peers.remove(peer)
    lock.release()


def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

    s.bind((HOST, PORT))
    s.listen(20)

    while 1:
        conn, addr = s.accept()
        print('handling new connection from: {}'.format(addr[0]))
        thread = Thread(target=handle_connetion, args=(conn, addr))
        thread.start()


if __name__ == '__main__':
    main()

Beispiel TCP Server (farbige ausgabe )

py
#!/usr/bin/python3
import socket

PORT = 12000
peers = list()

def main():
    print("Chat Server")

    # erstellen einen Socket
    # festlegen auf welcher IP wir hoehren
    # receive auf socket
    # print payload

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.bind(("0.0.0.0", PORT))

    while True:
        msg, (ip, port) = server_socket.recvfrom(1024)

        push = True

        for v in peers:
            (cip, cport) = v
            if cip == ip and cport == port:
                push = False

        if push == True:
            peers.append((ip, port))

        print("{}@{}: {}".format(ip, port, msg.decode()))
        for v in peers:
            (cip, cport) = v
            print("sending to {}:{}".format(cip, cport))
            server_socket.sendto("\033[92m{}: {}".format(ip, msg.decode()).encode(), (cip, cport))


        # an alle hosts weitergeben

class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

if __name__ == '__main__':
    main()