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ślnieAF_INET
.type
: semantyka komunikacji,SOCK_STREAM
(TCP),SOCK_DGRAM
(UDP), etc. domyślnieSOCK_STREAM
.proto
: wybór protokołu z dostępnych opcji, domyślnie0
- 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 danychflags
: konfiguracjacount
: liczba wysłanych bajtów z bufora
recv(self, buffersize [, flags]) -> data
buffersize
: maksymalny rozmiar odebbranej wiadomościflags
: konfiguracjadata
: oderana wiadomość
Odczyt/zapis (protokół bezpołączeniowy):
sendto(self, buffer [, flags], address) -> count
buffer
: bufor, ciąg danychflags
: konfiguracjaaddress
: para(host, port)
opisuąca gniazdo zdalnecount
: liczba wysłanych bajtów z bufora
recvfrom(self, buffersize [, flags]) -> (data, adress)
buffersize
: maksymalny rozmiar odebbranej wiadomościflags
: konfiguracjadata
: 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.
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.