Java-Implementierung

Zur Implementierung des Android-TCP-Severs wird eine Klasse SimpleTcpServer als Wrapper für die Klasse AsyncTask angelegt. Dies vereinfacht die Ansteuerung. Die Klasse muss im benutzenden Programm abgeleitet werden, um die Methode onMessageArrived (s.u.) mit einem sinnvollen Inhalt überschreiben.

Das API der Klasse stellt zwei Methoden bereit. void start(int port) dient zu Starten des Servers und die Callback-Funktion void onMessageArrived(String ClientIp, String message) übermittelt die Client-IP und den Nahrichteninhalt eine eingehenden Verbindung.

/**
 * SimpleTcpServer
 * Implementiert einen einfachen TCP-Server
 *
 * @author Ullis Roboter Seite
 * @version 1.0
 * @since 2014-08-29
 */
public class SimpleTcpServer {
    /**
     * Startet den TCP-Server.
     *
     * @param Port
     *            Service-Port, unter dem auf eingehende Verbindungen gewartet wird.
     */
    public void start(int Port) {
        // ...
    }
    /**
     * Rückruffunktion zur Übergabe von IP und Nachrichten-Inhalt bei Eingang
     * einer Nachricht.
     *
     * @param ClientIp
     *            IP des verbundenen Clients.
     * @param Message
     *            Inhalt der übermittelten Nachricht.
     */  
    protected void onMessageArrived(String ClientIp, String Message) {  
    }
}

Die wesentliche Methode des AsyncTask ist doInBackground. Hier wird ein ServerSocket angelegt, das dann in einer Endlos-Schleife auf eine eingehende Verbindunganforderung wartet (tcpServer.accept()). Wird eine Verbindung hergestellt, werden Reader- und Writer-Objekte für den vom Client-Socket bereitgestellten Stream angelegt. Über den Reader wird die übermittelte Nachricht ausgelesen. Über die von AsyncTask bereitgestellte Methode publishProgress kann thread-übergreifend die Client-IP und die Nachricht gemeldet werden. Die Nachricht wird ein wenig modifiziert und als Antwort zurückgesandt. Wichtig ist der Aufruf von flush() um sicher zu stellen, dass die Nachricht unmittelbar versandt wird.

Zum Schluss wird die Verbindung getrennt.

Eine detaillierte Fehlerbehandlung gibt es nicht.

/**
 * Stellt einen asynchronen Task zur Annahme von eingehenden
 * Verbindungsanforderungen bereit.
 */
class SimpleTcpServerAsyncTask extends AsyncTask<Integer, String, String> {
    ServerSocket tcpServer;
    Socket client;
    // Der Hintergrund-Thread  
    @Override  
    protected String doInBackground(Integer... Params) {  
        int port = Params[0]; // Port-Nummer ist im ersten Element enthalten  
        // Server starten  
        try {  
            tcpServer = new ServerSocket(port);  
        } catch (IOException e) {  
            return e.toString();  
        }
        try {  
            while (true) {  
                // auf Verbindungsanforderung eines Clients warten  
                client = tcpServer.accept();
                BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));  
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));  
                // auf eine Nachricht des Clients warten  
                String incomingMsg = reader.readLine();
                // Client-IP und Nachricht melden  
                publishProgress(client.getInetAddress().getHostAddress(), incomingMsg);
                // Antwort aufbereiten  
                String outgoingMsg = prepAnswer(incomingMsg) + System.getProperty("line.separator");  
                // Antwort senden  
                writer.write(outgoingMsg);
                writer.flush();
                // Verbindung trennen  
                client.close();
            } // while  
        } catch (Exception e) {  
            return e.toString();  
        } // try  
    } // doInBackground  
    /**
     * Wrapper für das 'onProgressUpdate'-Ereignis. Wandelt die generischen
     * Parameter in benannte Eigenschaften.
     */  
    protected void onProgressUpdate(String... Progress) {  
        onMessageArrived(Progress[0], Progress[1]);  
    }
}

Die Klasse kann wie folgt genutzt werden:

class SpecialSimpleTcpServer extends SimpleTcpServer {  
    protected void onMessageArrived(String clientIp, String message) {  
        // clientIP & message an Steuerelemente übergeben
        // z.B. textRemoteClientMsg.setText(message);
    }
}
// ...  
SpecialSimpleTcpServer specialSimpleTcpServer;
// ...  
specialSimpleTcpServer = new SpecialSimpleTcpServer();  
specialSimpleTcpServer.start(ServerPort);

Visual Basic Implementierung

Die VB-Implementierung ist sehr ähnlich. Auch hier wurde eine Klasse entworfen, die einen standardisierten Hintergrund-Task kapselt. In .Net ist dies die Klasse BackgroundWorker. Das API besteht wie oben aus der Methode start und –statt der Callback-Funktion– einem Event onMessageArrived zur Übermittlung der Client-IP und Nachricht.

''' <summary>  
''' Implementiert einen einfachen TCP-Server.  
''' </summary>  
Public Class SimpleTcpServer  
   ''' <summary>  
   ''' Event zur Übergabe von IP und Nachrichten-Inhalt bei Eingang einer Nachricht.  
   ''' </summary>  
   ''' <param name="ClientIp">IP des verbundenen Clients.</param>  
   ''' <param name="message">Inhalt der übermittelten Nachricht.</param>  
   Public Event onMessageArrived(ClientIp As String, message As String)  
  
   ''' <summary>  
   ''' Startet den TCP-Server.  
   ''' </summary>  
   ''' <param name="port">Service-Port, unter dem auf eingehende Verbindungen gewartet wird.</param>  
   Public Sub start(port As Integer)  
      ' Thread einrichten  
      serverThread = New BackgroundWorker  
      serverThread.WorkerReportsProgress = True  
      serverThread.RunWorkerAsync(port)  
   End Sub  
End Class

Die Implementierung der Kernfunktion ist analog der Java-Implementierung. Nur das Übermitteln der Verbindungsdaten geschieht ein wenig anders. Hierfür ist beim BackgroundWorker die Methode ReportProgress zuständig. Im ersten Parameter wird eine Angabe zum prozentualen Prozessfortschritt erwartet, im zweiten kann ein beliebiges Objekt übermittelt werden. Um zwei String zu übermitteln, müssen diese also zusammengefasst werden. Dazu dient die Structure State. In der Callback-Funktion serverThread_ProgressChanged von BackgroundWorker wird diese Structure wieder aufgelöst.

Private Sub ServerThread_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles serverThread.DoWork  
   ' Server Starten  
   Dim tcpServer As New TcpListener(IPAddress.Any, CInt(e.Argument))  
   tcpServer.Start()  
  
   While True  
      ' auf Verbindungsanforderung eines Clients warten  
      Dim client As TcpClient = tcpServer.AcceptTcpClient  
      Dim reader As StreamReader = New StreamReader(client.GetStream)  
      Dim writer As StreamWriter = New StreamWriter(client.GetStream)  
  
      'auf eine Nachricht des Clients warten  
      Dim incomingMsg As String = reader.ReadLine  
  
      'Client-IP und Nachricht melden  
      Dim clientIP = DirectCast(client.Client.RemoteEndPoint, IPEndPoint).Address.ToString  
  
      serverThread.ReportProgress(0, New State(clientIP, incomingMsg))  
  
      ' Antwort aufbereiten  
      Dim outgoingMsg As String = prepAnswer(incomingMsg)  
  
      ' Antwort senden  
      writer.WriteLine(outgoingMsg)  
      writer.Flush()  
  
      'Verbindung trennen  
      client.Close()  
   End While  
End Sub  
  
Private Structure State  
   Public ip As String  
   Public msg As String  
   Sub New(ip As String, msg As String)  
      Me.ip = ip  
      Me.msg = msg  
   End Sub  
End Structure  
  
Private Sub serverThread_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) 
                                         Handles serverThread.ProgressChanged  
   Dim state As State = CType(e.UserState, SimpleTcpServer.State)  
   RaiseEvent onMessageArrived(state.ip, state.msg)  
End Sub