Wie schreibe ich einen Server und einen Client, die über Sockets miteinander kommunizieren?

Ein Socket (engl. Sockel) ist eine bidirektionale Netzwerk-Kommunikationsschnittstelle, deren Verwaltung das Betriebssystem übernimmt.
Die Kommunikation findet zwischen einem Server und einem Client über einen definierten Port statt.

Das Beispiel demonstriert das Versenden eines Strings durch einen Client über Port 3141 an einen Server auf dem lokalen Rechner, das Rücksenden durch den Server und die Ausgabe wiederum durch den Client.

In seiner main()-Methode nutzt der Client die Klasse java.net.Socket zur Einrichtung einer Socket-Verbindung über Port 3141 zu localhost.
Deren Methoden getInputStream() und getOutputStream() liefern Input-, bzw. OutputStreams, aus denen gelesen, bzw. in die geschrieben werden kann.
Zum Schreiben von Strings bietet sich ein PrintStream an, dessen Konstruktor das InputStream-Objekt und einen boolschen Wert für das Autoflushing entgegennimmt.
Seine Methode println() schreibt den gewünschten String und fügt automatisch einen Zeilenumbruch an.

Das Lesen erfolgt zur Performance-Optimierung zunächst in einen BufferedReader, der ein Objekt eines InputStreamReaders im Konstrukor entgegennimmt. Sein Inhalt wird schließlich über eine Schleife Zeile für Zeile ausgelesen und auf die Konsole ausgegeben. Die Methode available() erlaubt bei Bedarf die Abfrage der im InputStream verfügbaren Bytes.
Probleme bei den Input-Output-Vorgängen, sowie ein falscher Host-Name können Exceptions werfen, die in einem try-catch-Block abgefangen werden müssen.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

class StringClient {

    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("localhost", 3141);

            OutputStream raus = socket.getOutputStream();
            PrintStream ps = new PrintStream(raus, true);
            ps.println("Hallo Welt!");
            ps.println("Hallo Otto!");

            InputStream rein = socket.getInputStream();
            System.out.println("verf\u00FCgbare Bytes: " + rein.available());
            BufferedReader buff = new BufferedReader(new InputStreamReader(rein));
            
            while (buff.ready()) {
                System.out.println(buff.readLine());
            }

        } catch (UnknownHostException e) {
            System.out.println("Unknown Host...");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("IOProbleme...");
            e.printStackTrace();
        } finally {
            if (socket != null)
                try {
                    socket.close();
                    System.out.println("Socket geschlossen...");
                } catch (IOException e) {
                    System.out.println("Socket nicht zu schliessen...");
                    e.printStackTrace();
                }
        }
    }
}

Die Klasse ServerSocket repräsentiert einen Server, dessen Konstruktor die Nummer desjenigen Ports übergeben bekommt, an dem dieser horchen soll. Nach Einrichten des Sockets wird durch die Methode accept() eine Verbindung innrhalb einer Endlosschleife hergestellt.
In der Methode reinRaus() werden auf gleiche Weise, wie oben für den Client beschrieben, Input- und OutputStreams gelesen und beschrieben. Sie werden wieder vom Socket geliefert.
Nach Beenden des Servers wird der Socket geschlossen.

package server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class StringServer {
    private final ServerSocket server;
    public StringServer(int port) throws IOException {
        server = new ServerSocket(port);
    }

    private void verbinde() {

        while (true) {
            Socket socket = null;
            try {
                socket = server.accept();
                reinRaus(socket);
            }

            catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (socket != null)
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
        }
    }

    private void reinRaus(Socket socket) throws IOException {
        BufferedReader rein = new BufferedReader(new InputStreamReader(socket
                .getInputStream()));
        PrintStream raus = new PrintStream(socket.getOutputStream());
        String s;
        
        while(rein.ready()) {
            s = rein.readLine();
            raus.println(s);
        }
    }

    public static void main(String[] args) throws IOException {
        StringServer server = new StringServer(3141);
        server.verbinde();
    }
}