TCP sockets

advertisement
Laboratorul 1
Retele de calculatoare
Socket
A socket is an abstraction of a communication channel end-point (either network communication or local inter-process
communication), that allows us to exchange data between the peers.
In order to work with sockets we need an API. This API was initially introduced by Berkeley university in 1983, long
before the Internet -- TCP/IP -- was born. The API was initially only available for C programming language, but it was
quickly adopted by all languages as libraries, and it is today a de facto standard for network application interfaces.
The most important operations that are available to us are:
• receiving data;
• sending data;
• closing the socket;
These operations can be executed only when the socket is in a certain state; states which are determined by the life cycle
of the socket:
•
•
•
•
creation -- the socket is created and is ready to be used;
setup -- optionally before any other operation we can execute certain parameter setup;
data exchange -- we are allowed to read and write data;
termination -- releasing all the resources committed to it, and no other operation is allowed after this;
Sockets can be used with many kinds of network protocols:
• connection-oriented (like TCP) or connection-less (like UDP);
• stream (like TCP) or datagram (like UDP);
• reliable (like TCP) or unreliable (like UDP);
TCP sockets
The most common use for sockets is to write TCP applications, which are split in two categories:
• servers:
• applications that usually run on dedicated hardware and software -- on a server machine;
• these applications offer a well defined service to any client that communicates with it;
• clients:
• applications that usually run on the users computer -- a workstation, desktop, laptop or even smart-phone
and PDAs;
• these applications connect to a specific server, send requests, and wait for responses;
As a consequence the sockets are also split in two types:
• server sockets -- used on the server side;
• client sockets -- used on the client side;
As noted previously a client needs to connect to a server, resulting that the two sockets -- the one from the server and the
one from the client -- are related to each other -- they are connected. In order to establish a connection the following main
conditions must be met:
• the server has to listen on a specific IP address and port -- this is the server's socket local address;
• the client has to connect on to that particular IP address and port -- this is the client's socket remote address;
• additionally each client socket must also have a local address;
Thus the client must know the address and port in advance, but usually we skip this by:
• using a DNS name;
• using a default port;
TCP client sockets
Life-cycle
1.
2.
3.
4.
5.
creation -- a client socket object is created;
binding -- the socket's local address is established;
connection -- the socket's remote address is established and a connection is made;
data exchange;
shutting down the channel -- either one way or both ways:
• if we shutdown the input -- incoming -- stream we notify the other peer that we are not accepting any other
data;
• if we shutdown the output -- outgoing -- stream we notify the other peer that we shall not send any more
data;
• each stream can be shutdown only once;
6. receiving the remaining data after shutdown;
7. closing -- releasing all committed resources;
Java API
• java.net:
• Socket:
• bind(SocketAddress) -- used to establish the socket's local address;
• connect(SocketAddress) -- used to establish the socket's remote address, and try to establish a
connection;
• shutdownInput();
• shutdownOutput();
• close() -- used to terminate the connection;
• getInputStream() -- used to obtain the input -- incoming -- byte stream;
• getOutputStream() -- used to obtain the output -- outgoing -- byte stream;
• getLocalSocketAddress() -- used to obtain the local socket address;
• getRemoteSocketAddress() -- used to obtain the remote socket address -- who is on the other side;
Examples
• Creating the client socket:
Socket socket = new Socket ();
• Binding to the local socket address:
InetAddress localIpAddress = InetAddress.getByName ("0.0.0.0");
int localIpPort = 0;
SocketAddress localSocketAddress = new InetSocketAddress (localIpAddress, localIpPort);
socket.bind (localSocketAddress);
• Connecting to the remote socket address:
InetAddress remoteIpAddress = InetAddress.getByName ("www.info.uvt.ro");
int remoteIpPort = 80;
SocketAddress remoteSocketAddress = new InetSocketAddress (remoteIpAddress, remoteIpPort);
socket.connect (remoteSocketAddress);
• Receiving and / or sending data through input and output streams:
InputStream input = socket.getInputStream ();
OutputStream output = socket.getOutputStream ();
input.read (buffer, offset, size); ...
output.write (buffer, offset, size); ...
output.flush ();
• Shutting-down the input and output streams:
socket.shutdownInput ();
socket.shutdownOutput ();
• Closing the socket:
socket.close ();
TCP server sockets
Life-cycle
creation -- a server socket is created;
binding -- the socket's local address is established;
listening -- the socket is put into a state that accepts incoming connections;
accepting -- the application waits for incoming connections; each connection is in the form of a (client) socket; for
each connection the application either:
• handles it, closes it, and continues to accept other connections;
• creates a new process or thread which will handle the connection, and the current process or thread
continues to accept other connections;
• closing -- the socket stops accepting incoming connections, and all committed resources are released; (closing the
server socket, does not close any of the accepted incoming sockets;)
•
•
•
•
Java API
• java.net:
• ServerSocket:
• bind(SocketAddress) -- this establishes the socket's local address -- the one that the client must be
aware of; this also puts the socket in a listening state;
• accept() -- waits for an incoming connection and returns a (client) socket representing that
connection;
• close() -- stops the acceptance of new connections and releases all the socket resources;
• getLocalSocketAddress() -- used to retrieve the local socket address;
Examples
• Creating the server socket:
ServerSocket socket = new ServerSocket ();
• Binding to the local socket address -- this is the one the clients should be connecting to:
InetAddress localIpAddress = InetAddress.getByName ("0.0.0.0");
int localIpPort = 80;
SocketAddress localSocketAddress = new InetSocketAddress (localIpAddress, localIpPort);
socket.bind (localSocketAddress);
• For each connection accepting a client socket, and:
Socket client = socket.accept ();
• Receiving and / or sending data;
• Shutting-down the inbound and outbound streams;
• Closing the client socket;
These steps are just like the ones described above for the client socket.
• Closing the server socket;
socket.close ();
Java IP generic API
In Java we have the following API that allows us to:
• resolve DNS names to IP addresses;
• resolve IP address to DNS names -- reverse query;
• get the IP address of the current computer;
API:
• java.net:
• InetAddress -- an instance of this class is used as an IP address:
• getByName(String) -- used to get an IP address from a DNS name;
• getAllByName(String) -- used to get all the IP addresses from a DNS name;
• getLocalHost() -- used to obtain the IP address of the computer -- not to be confused with localhost
name which always is resolved to 127.0.0.1; getLocalHosts() output is determined by the hosts file.
• getAddress() -- used to obtain the raw -- byte array -- address;
• getHostAddress() -- used to obtain the name of the IP address;
• getCanonicalHostName() -- used to obtain the DNS name of the IP address;
• isReachable(int);
• InetSocketAddress -- an instance of this class is used as a socket address, and it is a wrapper for an IP
address and port:
• constructor(InetAddress, int);
• getAddress() -- used to obtain only the IP address;
• getPort() -- used to obtain only the port;
• java.lang:
• Integer:
• parseInt(String) -- used to parse (obtain) an integer from a string;
Java IO generic API
• java.io:
• InputStream -- is a binary (byte) stream that we can read from:
• read(byte, int, int);
• close();
• OutputStream -- is a binary (byte) stream that we can write to:
• write(byte, int, int);
• flush() -- in case there is a buffer holding information, force the send;
• close();
• InputStreamReader -- adapts (transforms) a binary input stream into a character output stream;
• OutputStreamWriter -- adapts (transforms) a binary output stream into a character output stream;
• BufferedReader:
• readLine() -- used to read an entire line as a string;
• BufferedWriter:
• write(String);
• flush();
Complete example
The following two applications are a simple client and server that allow a user to make the following queries:
• send hello, and receive hello;
• send get-time, and receive the server's time in number of milliseconds elapsed from 1970;
• send get-random, and receive a random number;
The protocol is pretty simple: send one line -- the request -- receive one line -- the response;
The client is started as (where bin is a folder where the byte-compiled classes are found):
java -classpath ./bin Client <host> <port> <request>
java -classpath ./bin Client 127.0.0.1 7654 hello
The server is started as (the port is hard-coded, and it is 7654):
java -classpath ./bin Server
Client
import
import
import
import
import
import
import
java.io.BufferedReader;
java.io.BufferedWriter;
java.io.InputStreamReader;
java.io.OutputStreamWriter;
java.net.InetAddress;
java.net.InetSocketAddress;
java.net.Socket;
public class Client
{
public static void main (String[] arguments) throws Exception
{
// Checking the argument count
if (arguments.length != 3) {
System.err.println ("Bad usage.");
return;
}
// Selecting the arguments
String hostName = arguments[0];
String portName = arguments[1];
String request = arguments[2];
// Creating the client socket
Socket socket = new Socket ();
// Binding the socket
socket.bind (new InetSocketAddress (InetAddress.getByName ("0.0.0.0"), 0));
// Resolving the server address and port
InetAddress address = InetAddress.getByName (hostName);
int port = Integer.parseInt (portName);
// Connecting the socket
socket.connect (new InetSocketAddress (address, port));
// Creating a reader and writer from the socket streams
BufferedReader reader = new BufferedReader (new InputStreamReader (socket.getInputStream ()));
BufferedWriter writer = new BufferedWriter (new OutputStreamWriter (socket.getOutputStream ()));
// Writing the request
writer.write (request);
writer.newLine ();
// Flushing the writer
writer.flush ();
// Reading the response
String response = reader.readLine ();
// Closing the socket
socket.close ();
// Printing the response
System.out.println (response);
}
}
Server
import
import
import
import
import
import
import
import
java.io.BufferedReader;
java.io.BufferedWriter;
java.io.InputStreamReader;
java.io.OutputStreamWriter;
java.net.InetAddress;
java.net.InetSocketAddress;
java.net.ServerSocket;
java.net.Socket;
public class Server
{
public static void main (String[] arguments) throws Exception
{
// Creating the server socket
ServerSocket server = new ServerSocket ();
// Binding the socket
server.bind (new InetSocketAddress (InetAddress.getByName ("0.0.0.0"), 7654));
// Looping
while (true) {
// Accepting a new connection => a new client socket
final Socket client = server.accept ();
// Starting a new Thread for each client
new Thread () {
public void run () {
// Catching any exceptions
try {
// Creating a reader and a writer from the socket streams
BufferedReader reader = new BufferedReader (new InputStreamReader (client.getInputStream ()));
BufferedWriter writer = new BufferedWriter (new OutputStreamWriter (client.getOutputStream ()));
// Reading the first request
String request = reader.readLine ();
// While the connection is still open
while (request != null) {
// Creating a response based on the request
String response;
if (request.equals ("hello"))
response = "hello back";
else if (request.equals ("get-time"))
response = "" + System.currentTimeMillis ();
else if (request.equals ("get-random"))
response = "" + Math.rint (Math.random () * 1000);
else if (request.equals ("quit")) {
break;
} else
response = "please?";
// Writing the response
writer.write (response);
writer.newLine ();
// Flushing the writer
writer.flush ();
}
// Reading the next request
request = reader.readLine ();
// Closing the client socket
client.close ();
} catch (Throwable error) {
// Handling the possible errors
error.printStackTrace ();
}
}
}
}
} .start ();
}
Download