Socket-Programmierung

Aus Foxwiki

topic - Kurzbeschreibung

Beschreibung

BSD Sockets API

Sowohl Unix Sockets als auch Windows Sockets basieren auf der 1983 veröffentlichten BSD Sockets API. Für die nachfolgenden Programmierbeispiele sind wichtige Funktionen dieser API hier kurz zusammengefasst:

  • socket() Erzeugt ein neues Socket bestimmten Types und alloziert hierfür Systemressourcen. Für die Identifizierung gibt die Funktion eine eindeutige Zahl vom Typ Integer zurück.
  • bind() Bindet den Socket an eine Socket Adressinformation, in der Regel an eine IP-Adresse und Port. Wird typischerweise auf Server-Seite benutzt.
  • listen() Versetzt einen gebundenen STREAM (TCP/IP) Socket in einen Lauschen-Modus. Wird auf Server-Seite benutzt.
  • connect() Macht die Adressinformation (z. B. IP-Adresse und Port) eines anderen Sockets bekannt. Ordnet dem Socket einen freien lokalen Port zu. Im Falle eines STREAM Sockets wird versucht eine neue TCP/IP-Verbindung zu etablieren. Wird auf Client-Seite benutzt.
  • accept() Akzeptiert einen eingehenden Versuch (Anfrage), eine neue TCP/IP-Verbindung zu einem entfernten Client aufzubauen, und erzeugt im Erfolgsfall ein neues Socket, assoziiert mit dem Adress-Paar der Verbindung. Dieses neu erzeugte Socket wird von der Funktion zurückgegeben. Wird auf Server-Seite benutzt.
  • send() und recv(), oder write() und read(), oder sendto() und recvfrom() Schreibt / liest Daten an / von Socket(s). Hinweis: Ein einmaliger Funktionsaufruf von recv() garantiert nicht den Empfang aller vom Sender mittels send() gesendeten Daten, insbesondere nicht bei STREAM Sockets.
  • close() Veranlasst das Betriebssystem alle für das Socket allozierten Ressourcen freizugeben. Liegt ein TCP/IP Socket vor, wird die Verbindung beendet.

C

Die abgeleitete POSIX-Sockets-API von Linux ist in C geschrieben. Ein Beispiel für einen TCP-Verbindungsaufbau eines Clients zu einem Server:

int sockfd = socket(AF_INET, SOCK_STREAM, 0); // Erzeuge Socket

struct sockaddr_in srv;

memset(&srv, 0, sizeof(struct sockaddr_in));

srv.sin_family = AF_INET;
inet_pton(AF_INET, "1.2.3.4", &srv.sin_addr); // Schreibe IP-Adresse des Servers in die sockaddr_in-Struktur
srv.sin_port = htons(1234); // Schreibe Port in Network-Byte-Order (Big Endian) in das Feld sin_port

connect(sockfd, (struct sockaddr*)&srv, sizeof(struct sockaddr_in)); // Verbindung herstellen

// Ab jetzt kann mit write() und read() aus dem Socket gelesen und in das Socket geschrieben werden.

[...] // Datenaustausch

shutdown(sockfd, SHUT_WR); // Sende ein EOF-Byte, sodass der Server beim nächsten read() als Rückgabewert 0 bekommt
                           //und die Verbindung beenden kann

close(sockfd);

C#

// set IP adress and port for remote host
IPAddress ipAddress = IPAddress.Parse("192.168.xxx.yyy");
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 9999);
Socket sock = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
// open socket
sock.Connect(ipEndPoint);
// check if socket is connected
if (sock.Connected)
{
	// endless loop
	while(true)
	{
		// set receive buffer and length, is buffer big enough?
		byte[] bytesReceived = new byte[1024];
        int bytes = 0;
        // send message
		byte[] msg1 = new byte[256];
		// ... set your bytes, ASCII or whatever
        sock.Send(msg1, msg1.Length, SocketFlags.None);
		// receive message
        bytes = sock.Receive(bytesReceived, bytesReceived.Length, SocketFlags.None);
	}
	sock.Shutdown(SocketShutdown.Both);
    sock.Close();
}

Java

Java als plattformunabhängige Programmiersprache unterstützt im Paket java.net unmittelbar die Socket-Programmierung. Das zeigt die Betriebssystemunabhängigkeit des Socket-Konzeptes. Die Implementierung der Sockets für die verschiedenen Plattformen (Linux, Windows, Spezialsysteme) erfolgt in der Klassenbibliothek der virtuellen Maschine.

Die Klassen für die Socket-Programmierung sind Socket und ServerSocket. Folgendes Kurzbeispiel zeigt die Verwendung:

 ServerSocket serverSocket = new ServerSocket(port);    //Serversocket mit bestimmter Port-Nummer erstellen
 while(true)
 {
   Socket clientSocket = serverSocket.accept();         //auf Anfragen warten
   InputStream input   = clientSocket.getInputStream(); //InputStream-Objekt öffnen
   byte[] data         = new byte[1024];                //Datenpuffer deklarieren (anlegen)
   int numBytes        = 0;                             //Variable für Anzahl der tatsächlich gelesenen Bytes
   numBytes            = input.read(data);              //Daten lesen
   /*** gelesene Daten verarbeiten ***/
   clientSocket.close();                                //Verbindung schließen
 }

Weiterhin gibt es in der aktuellen Java-Version die Möglichkeit, Sockets über die NewIO-(nio)-Bibliothek anzusprechen. Der Code ist etwas aufwändiger, kann jedoch schneller ausgeführt werden. Das Multiplexing mehrerer Sockets geschieht über einen so genannten Selector (vergleichbar dem Unix-Systemaufruf select).

Haskell

Die rein funktionale Programmiersprache Haskell bietet durch das Modul Network eine plattformunabhängige Möglichkeit zur Verwendung von Sockets. Das folgende Beispiel beinhaltet den vollständigen Code für einen sehr einfachen Server und einen korrespondierenden Client. Der Client nimmt Textzeilen von der Standardeingabe entgegen und schickt diese über den Netzwerk-Socket an den Server. Dieser wiederum gibt die Textzeilen auf die Standardausgabe aus.

Der hier gezeigte Server ist insofern einfach, als er z. B. eine sichere Beendigung wahrscheinlich nicht ermöglicht. Eine Lösungsmöglichkeit wird in haskell.org[1] unter Verwendung von Software Transactional Memory (STM) aufgezeigt.

Einfacher Server

module Main where

import Control.Concurrent
import Control.Monad
import System.IO
import Network

main : IO ()
main = withSocketsDo $ do
         theSocket <- listenOn (PortNumber 2048)
         forever $ acceptConnectionAndFork theSocket echoServer

type MessageHandler = (Handle, String, PortNumber) -> IO ()

acceptConnectionAndFork : Socket -> MessageHandler -> IO ()
acceptConnectionAndFork theSocket handler = do
  connection <- accept theSocket
  forkIO $ handler connection
  return ()

echoServer : MessageHandler
echoServer (handle,hostname,portnumber) = do
  putStrLn $ "("++hostname++":"++show portnumber++"): Open"
  c <- hGetContents handle
  mapM_ putStrLn ["("++hostname++":"++show portnumber++"): Msg "++show l | l<-lines c]
  putStrLn $ "("++hostname++":"++show portnumber++"): Close"

Einfacher Client

module Main where

import Network
import System.IO
import Control.Concurrent

main :: IO ()
main = withSocketsDo $ do
         handle <- connectTo "localhost" (PortNumber 2048)
         input <- getContents
         sequence_ [do
                      hPutStrLn handle l
                      hFlush handle |
                   l<-lines input]
         hClose handle

Ruby

In Ruby stellt die Standardbibliothek socket ebenfalls plattformunabhängig eine Schnittstelle zur Programmierung von TCP-Sockets zur Verfügung.

Ein sehr einfacher Server (mit Multithreading), der einen Text an den Client schickt:

require 'socket'

port = 2048
server = TCPServer.new(port)
loop do
  client = server.accept
  Thread.start(client) do |c|
    c.puts 'Hello!'
    c.close
  end
end

Python 3

Ein einfaches Programm, das über den Client Nachrichten an den Server schickt.

Server

import socket

# Server

host = "127.0.0.1"      # IP adress of the Server
port = 5000             # Port used by the server
    
s = socket.socket()
s.bind((host, port))
s.listen(1)

while True:

    client, addr = s.accept()
    print(f"Connected to {addr}")

    while True:
        
        data = client.recv(1024)
        if not data:
            break
        
        print(f"Received from client: {data.decode()}")

        client.sendall(data)

Client

import socket


host = "127.0.0.1"      # IP adress of the Server
port = 5000             # Port used by server

s = socket.socket()     
s.connect((host, port))

while True:
    msg = str(input("Message -> "))

    s.sendall(msg.encode())
    
    data = s.recv(1024)
    print(f"Received from server: {data.decode()}")

Node.js

In Node.js ermöglicht das Standardmodul "net" die Programmierung von TCP-Sockets.

var net = require('net');

var server = net.createServer(function (socket) {
  socket.write('Echo server\r\n');
  socket.pipe(socket);
});

server.listen(1337, '127.0.0.1');


Anhang

Siehe auch

Links

Weblinks
  1. https://de.wikipedia.org/wiki/Socket_(Software)
  1. Concurrency demos/Graceful exit auf wiki.haskell.org