Socket programmering
Litt om sockets
En socket er interfacet mellom applikasjonen (nærmere bestem en prosess) vår og transportlaget. Når vi sender data inn i en socket dytter vi det ned til transportlaget som får ansvar for å sende det dit det skal.
Vi kan også se på sockets som endepunkter i nettverket som to applikasjoner kommuniserer via. Det vi ønsker å gjøre nå er følgende:
- Opprette en socket (server og klient)
- Vente på en tilkobling / beskjed (server)
- Koble til / sende en beskjed (klient)
- Gi respons på mottat beskjed (server)
En socket identifiseres ved hjelp av IP-adresse + portnummer, der portnummeret på klientsiden er et tilfeldig valgt portnummer pr tilkobling. På serversiden velger vi et kjent portnummer sånn at klienten vet hvor den skal koble seg til. I eksempelet under oppretter vi serveren på port 9876.
Server
{codecitation style="brush: java;"}
import java.io.*;
import java.net.*;
class UDPServer {
public static void main(String args[]) throws Exception {
//Vi oppretter en Datagram (UDP) socket på port 9876
DatagramSocket serverSocket = new DatagramSocket(9876);
//Vi lager en buffer som vi kan bruke for å motta beskjeder
byte[] receiveData = new byte[1024];
//Vi lager en buffer som vi kan bruke for å sende responst på mottate beskjeder
byte[] sendData = new byte[1024];
//Vent på at noen tar kontakt og sender oss beskjeder
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
//Vi mottar data
serverSocket.receive(receivePacket);
//Vi henter ut data
String sentence = new String(receivePacket.getData());
//Vi henter ut IP-adressen vi fikk data fra sånn at vi vet hvor vi skal sende svar til
InetAddress IPAddress = receivePacket.getAddress();
//Vi henter ut portnummeret vi fikk data på sånn at vi vet hvor vi skal sende svar til
int port = receivePacket.getPort();
//Vi tuller litt med dataene før vi returnerer de (typisk skole-oppgave)
String capitalizedSentence = sentence.toUpperCase();
//Klargjør databufferen for sending
sendData = capitalizedSentence.getBytes();
//Send svar tilbake til klienten
DatagramPacket sendPacket = new DatagramPacket(sendData,
sendData.length, IPAddress, port);
serverSocket.send(sendPacket);
//Vi skriver ut en liten beskjed på serversiden sånn at vi ser at serveren kjører
System.out.println("received: " + sentence);
}
}
}
Klient
{codecitation style="brush: java;"}
class UDPClient {
DatagramSocket socket = null;
InetAddress address = null;
public static void main(String args[]) throws Exception {
UDPClient udpclient = new UDPClient();
udpclient.sendData("test", "127.0.0.1");
udpclient.readData();
}
public String sendData(String toSubmit, String server) {
String returnString = "";
try {
socket = new DatagramSocket();
} catch (SocketException e2) {
e2.printStackTrace();
}
byte[] sendBuf = new byte[1024];
sendBuf = toSubmit.getBytes();
try {
address = InetAddress.getByName(server);
DatagramPacket packet = new DatagramPacket(sendBuf, sendBuf.length,
address, 9876);
socket.send(packet);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("IP-adress of the server is: " + address);
return returnString;
}
public void readData() {
boolean readsocket = true;
byte[] receiveData = new byte[1024];
// Create a new socket, wait for datagrams, and process data if any.
try {
socket.setSoTimeout(1000);
while (readsocket) {
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
try {
// The receive method blocks until a DatagramPacket is received
socket.receive(receivePacket);
String sentence = new String(receivePacket.getData());
System.out.println(sentence);
} catch (SocketTimeoutException timeoutexception) {
readsocket = false;
System.out.println("Connection Timed Out");
timeoutexception.printStackTrace();
}
}
} catch (IOException ioexception) {
ioexception.printStackTrace();
}
}
}
Testing av programmet vårt
Vi må selvfølgelig teste om programmet vårt fungerer som forventet. Dette gjøres enkelt ved å starte serveren i et terminalvindu, og så klienten i et annet. Etter at klienten har kjørt ser vi at vi har fått en statusmelding i servervinduet om at det har vært en klient tilkoblet. Vi kan starte klienten vår flere ganger, serveren er tilgjengelig og lytter helt til vi avslutter prosessen.
Server:
$ java UDPServer
Klient:
$ java UDPClient
IP-adress of the server is: /127.0.0.1
TEST
Connection Timed Out
Server:
$ java UDPServer
received: test
Konklusjon
Det skal ikke så veldig mange kodelinjer for å sende data over nettverket. Selv om dette eksempelet kun sendte en testbeskjed kan det utvides til å sende mer, lagre ting som er sendt til fil, og så videre.
I eksempelet over brukte vi datagramer (UDP) så vi har ingen garanti for at dataene våre kommer frem som vi ønsker. Dersom du trenger sånne garantier kan du implementere en form for sekvensnummer/acknowledgement funksjonalitet, eller bytte over til en TCP variant.
I eksempelet brukte jeg IP-adresse 127.0.0.1 i klienten siden jeg kjørte serverkoden på min lokale maskin, men jeg kan kjøre serveren på hvilken som helst maskin som jeg kan nå via nettet.