Inne implementacje gniazd

Implementacje wysokopoziomowe pokazane są na przykładach języków Python i Java, które są typowe dla języków wysokiego poziomu.

Python

Interfejs odwzorowuje gniazda BSD. Podejście obiektowe, ukryte szczegóły np. endianness. Convenience functions.

Funkcje

  • Tworzenie gniazda (funkcja pakietu socket):

    import socket
    socket([family[, type[, proto]]]) -> socket object
    
    • family: domena komunikacji, AF_INET (IPv4), AF_INET6 (IPv6), etc. Domyślnie AF_INET.
    • type: semantyka komunikacji, SOCK_STREAM (TCP), SOCK_DGRAM (UDP), etc. domyślnie SOCK_STREAM.
    • proto: wybór protokołu z dostępnych opcji, domyślnie 0
    • zwraca obiekt gniazda

Metody obiektu gniazda

  • Związanie gniazda (metoda instancji obiektu gniazda):

    bind(self, address)
    
    • address: para (host, port): adres lub nazwa domenowa urządzenia i port
  • Przygotowanie gniazda na obsługę połączeń i konfiguracja kolejki żądań (serwer):

    listen(self, [backlog])
    
    • backlog: rozmiar kolejki żądań, domyślnie wybierana “sensowna wartość”,
  • Oczekiwanie i obsługa zgłoszenia (serwer):

    accept(self) -> (socket object, address info)
    
    • socket object: nowy obiekt gniazda do komunikacji z klientem,
    • address info: para (host, port) gniazda klienta
  • Związanie gniazda z serwerem (zdalnym) i nawiązanie połączenia (klient)

    connect(self, address)
    
    • address: para (host, port)
  • Odczyt/zapis (protokół połączeniowy):

    send(self, buffer [, flags]) -> count
    sendall(self, buffer [, flags])
    
    • buffer: bufor, ciąg danych
    • flags : konfiguracja
    • count: liczba wysłanych bajtów z bufora
    recv(self, buffersize [, flags]) -> data
    
    • buffersize: maksymalny rozmiar odebbranej wiadomości
    • flags: konfiguracja
    • data: oderana wiadomość
  • Odczyt/zapis (protokół bezpołączeniowy):

    sendto(self, buffer [, flags], address) -> count
    
    • buffer: bufor, ciąg danych
    • flags : konfiguracja
    • address : para (host, port) opisuąca gniazdo zdalne
    • count: liczba wysłanych bajtów z bufora
    recvfrom(self, buffersize [, flags]) -> (data, adress)
    
    • buffersize: maksymalny rozmiar odebbranej wiadomości
    • flags: konfiguracja
    • data: oderana wiadomość
    • address : para (host, port) opisuąca gniazdo zdalne
  • Zamknięcie połączenia/gniazda:

    close(self)
    

Przykłady

Klient UDP:

import socket
from socket import AF_INET, SOCK_DGRAM

socket = socket.socket(AF_INET, SOCK_DGRAM)

socket.sendto(b'hello world', ("localhost", 9001))
(buffer, _) = socket.recvfrom(256)
print(buffer)
def sendallto(socket, message, address):
    sent = 0
    while sent < len(message):
        sent += socket.sendto(message[sent:], address)

Serwer UDP:

import socket
from socket import AF_INET, SOCK_DGRAM

server_socket = socket.socket(AF_INET, SOCK_DGRAM)
server_socket.bind(("localhost", 9001))

while True:
    (buffer, address) = serversocket.recvfrom(256)
    response = buffer.upper()
    print("%s -> %s" % (buffer, response))
    serversocket.sendto(response, address)

Klient TCP:

import socket
from socket import AF_INET, SOCK_STREAM

socket = socket.socket(AF_INET, SOCK_STREAM)
socket.connect(("localhost", 9001))
socket.sendall(b'hello world')
buffer = socket.recv(256)
print(buffer)
socket.close()

Serwer TCP:

import socket
from socket import AF_INET, SOCK_STREAM

server_socket = socket.socket(AF_INET, SOCK_STREAM)
server_socket.bind(("localhost", 9001))
server_socket.listen(5)

while True:
    (client_socket, address) = server_socket.accept()
    buffer = client_socket.recv(256)
    response = buffer.upper()
    print("%s -> %s" % (buffer, response))
    client_socket.sendall(response)
    client_socket.close()

Serwery wielowątkowe

import socket
from socket import AF_INET, SOCK_STREAM
from threading import Thread

server_socket = socket.socket(AF_INET, SOCK_STREAM)
server_socket.bind(("localhost", 9001))
server_socket.listen(5)

def handle_client(socket, address):
    buffer = client_socket.recv(256)
    response = buffer.upper()
    print("%s -> %s" % (buffer, response))
    client_socket.sendall(response)
    client_socket.close()

while True:
    (client_socket, address) = server_socket.accept()
    thread = Thread(target=handle_client, args=(client_socket, address))
    thread.start() # FIXME GIL
import socket
from socket import AF_INET, SOCK_STREAM
import gevent

server_socket = socket.socket(AF_INET, SOCK_STREAM)
server_socket.bind(("localhost", 9001))
server_socket.listen(5)

def handle_client(socket, address):
    buffer = client_socket.recv(256)
    response = buffer.upper()
    print("%s -> %s" % (buffer, response))
    client_socket.sendall(response)
    client_socket.close()

while True:
    (client_socket, address) = server_socket.accept()
    thread = gevent.spawn(handle_client, client_socket, address)

    # Yield to spawned cooperative greenlet to kick-start it
    gevent.sleep(0)

Synchronizacja

  • Threading:

    • threading.Lock
    • threading.RLock
    • threading.Semaphore
    • threading.Condition
  • Gevent:

    • gevent.lock.Semaphore
    • gevent.event.Event
    • gevent.queue.Queue

Select

from select import poll as Poll
from select import POLLIN, POLLOUT

poll_handler = Poll()
poll_handler.register(socket1, POLLIN)
poll_handler.register(socket2, POLLIN | POLLOUT)

activity = poll_handler.poll()
for socket, event in activity:
    # do whatever
    pass

Java

Interfejs obiektowy, osobne klasy dla różnego zastosowania gniazd, encapsulation – gniazda są zamknięte wewnątrz obiektu i potrzeba mniej wywołań do stworzenia sprawnego gniazda. Dekorowane strumienie do czytania i zapisu danych.

TCP

Interfejs “niskopoziomowy”:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TCPClient {

    public static void main(String[] argv) throws IOException {

        Socket socket = new Socket("localhost", 9001);

        InputStream inputStream = socket.getInputStream();
        OutputStream outputStream = socket.getOutputStream();

        byte[] buffer = "hello world".getBytes();
        outputStream.write(buffer);

        int count;
        while ((count = inputStream.read(buffer)) > 0) {
            String reply = new String(buffer).substring(0, count);
            System.out.println(reply);
        }

        socket.close();
    }
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {

    public static void main(String[] argv) throws IOException {

        ServerSocket serverSocket = new ServerSocket(9001);

        while (true) {
            Socket clientSocket = serverSocket.accept();

            InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream();

            int count;
            byte[] buffer = new byte[256];
            while ((count = inputStream.read(buffer)) > 0) {
                String message = new String(buffer).substring(0, count);
                String reply = message.toUpperCase();
                System.out.println(message + "->" + reply);
                outputStream.write(reply.getBytes());
            }

            clientSocket.close();
        }

        //serverSocket.close();
    }
}

Idiomatyczna Java (bufory):

import java.io.*;
import java.net.Socket;

public class TCPClientBuffered {

    public static void main(String[] argv) throws IOException {

        Socket socket = new Socket("localhost", 9001);

        OutputStream outputStream = socket.getOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);

        // bufferedWriter.write(string);
        // bufferedWriter.flush();

        bufferedWriter.write("hello world\n"); // Line break!
        bufferedWriter.flush();

        InputStream inputStream = socket.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

        // bufferedReader.ready();
        // bufferedReader.read();
        // bufferedReader.readLine();
        // bufferedReader.skip(int);

        String reply;
        while ((reply = bufferedReader.readLine()) != null) {
            System.out.println(reply);
        }

        socket.close();
    }
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServerBuffered {

    public static void main(String[] argv) throws IOException {

        ServerSocket serverSocket = new ServerSocket(9001);

        while (true) {
            Socket clientSocket = serverSocket.accept();

            InputStream inputStream = clientSocket.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            OutputStream outputStream = clientSocket.getOutputStream();
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
            BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);

            String message;
            while ((message = bufferedReader.readLine()) != null) {
                String reply = message.toUpperCase();
                System.out.println(message + "->" + reply);
                bufferedWriter.write(reply + "\n");
                bufferedWriter.flush();
            }

            clientSocket.close();
        }

        //serverSocket.close();
    }
}

Serwer wielowątkowy TCP:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServerBufferMT {

    public static void main(String[] argv) throws IOException {

        ServerSocket serverSocket = new ServerSocket(9001);

        while (true) {
            final Socket clientSocket = serverSocket.accept();
            Thread thread = new Thread() {
                @Override
                public void run() {
                    try {
                        InputStream inputStream = clientSocket.getInputStream();
                        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                        OutputStream outputStream = clientSocket.getOutputStream();
                        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);

                        String message;
                        while ((message = bufferedReader.readLine()) != null) {
                            String reply = message.toUpperCase();
                            System.out.println(message + "->" + reply);
                            bufferedWriter.write(reply + "\n");
                            bufferedWriter.flush();
                        }

                        clientSocket.close();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            };

            thread.start();
        }

        //serverSocket.close();
    }
}

UDP

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

public class UDPClient {

    public static void main(String[] argv) throws IOException {

        DatagramSocket socket = new DatagramSocket();

        InetSocketAddress address = new InetSocketAddress("localhost", 9001);

        String message = "hello world";
        DatagramPacket outgoingPacket = new DatagramPacket(
            message.getBytes(), message.getBytes().length, address);
        socket.send(outgoingPacket);

        DatagramPacket incomingPacket = new DatagramPacket(new byte[256], 256);
        socket.receive(incomingPacket);

        int count = incomingPacket.getLength();
        String reply = new String(incomingPacket.getData()).substring(0, count);
        System.out.println(reply);

        socket.close();
    }
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServer {

    public static void main(String[] argv) throws IOException {

        DatagramSocket serverSocket = new DatagramSocket(9001);

        while (true) {
            DatagramPacket incomingPacket = new DatagramPacket(new byte[256], 256);
            serverSocket.receive(incomingPacket);

            int count = incomingPacket.getLength();
            String message = new String(incomingPacket.getData()).substring(0, count);
            String reply = message.toUpperCase();
            System.out.println(message + "->" + reply);

            DatagramPacket outgoingPacket = new DatagramPacket(
                reply.getBytes(), reply.getBytes().length, incomingPacket.getSocketAddress());
            serverSocket.send(outgoingPacket);
        }

        //serverSocket.close();
    }
}

Java NIO

Alternatywna implementacja gniazd. Use cases: operacje nieblokujące, polling.

Przykład: http://www.cs.put.poznan.pl/jkonczak/sk2:java

Winsock

Przykłady:

Zadania

1. Napisz system pychat z centralnym koordynatorem, gdzie wielu klientów podłącza się do centralnego serwera. Serwer rozsyła wiadomość otrzymaną od dowolnego z klientów do wszystkich innych klientów w systemie. Serwer obsługuje klientów w sposób wielowątkowy. Zaimplementuj system w języku Python.

2. Napisz system jchat z centralnym koordynatorem, gdzie wielu klientów podłącza się do centralnego serwera. Serwer rozsyła wiadomość otrzymaną od dowolnego z klientów do wszystkich innych klientów w systemie. Serwer obsługuje klientów w sposób wielowątkowy. Zaimplementuj system w języku Java.

3. Prosty system rmi (Remote Method Invocation). Serwer napisany w języku Python lub Scala utrzymuje jakąś populację obiektów. Klient napisany w C wysyła wiadomości do serwera które zawierają polecenia wywołania poszczególnych metod (potencjalnie z argumentami) na poszczególnych obiektach. Serwer wywołuje wskazane metody na wskazanych obiektach i wysyła wynik spowrotem do klienta. Serwer pamięta stan obiektów pomiędzy poszczególnymi wywołaniami metod przez klientów (tj. klient B widzi zmiany spowodowane wywołaniem metod przez wczesniejszego klienta A).

4. Napisz system dist-n-prod-m-cons który rozwiązuje problem producent-konsument w środowisku rozproszonym dla n producentów i m konsumentów. Serwer implementuje kolejkę FIFO \(Q\). Klienci pełniący role producentów generujują kolejne liczby całkowite i wysyłają je do serwera, który dopisuje je na koniec \(Q\). Jeśli w kolejce nie ma miejsca na kolejną wartość, klient czeka aż miejsce się zwolni. Klienci pełniący role konsumentów wsyłają do serwera komunikat z prośbą o odczytanie kolejnych liczb z początku \(Q\), które wypisują na ekran. Serwer usuwa skonsumowane wartości z \(Q\). Jeśli kolejka jest pusta, konsument czeka aż w kolejce znajdą się nowe wartości. Sprawdź zachowanie systemu w przypadku jeśli konsument jest dużo szybszy niż producent i vice versa.