With TCP, a connection is established between two machines. Usually, the client asks for the connection creation and the server accepts it. Once the connection is established, the client and the server are able to read and write data.
There are two channels in a single connection:
One of these channel can be closed without closing the connection.
Reliability / Fiabilité: TCP ensures that no data is lost.
Integrity / Intégrité: TCP ensures that data arrive in the same order they have been sent, and that they are not modified.
No conservation of limits: TCP does not ensure that sent data is received in one go.
If 100 bytes are written on a TCP connection, it is possible to receive 40 bytes first, and then 60. Or possibly 100 in one go. If 50 bytes are written first, and then 50 other bytes, it is also possible to receive 100 bytes in one go.
Usually, several clients connect to a single server.
As in UDP, both client-side and server-side attachment point is done through sockets, linked to an IP address and a port number.
For now, in this lecture, we only consider clients.
A TCP SocketChannel
.
A SocketChannel
is created using the method factory SocketChannel.open()
; it is not connected.
Connection to a server is initiated by a call to socketChannel.connect(SocketAddress)
.
// creation of the socket (not connected) SocketChannel sc = SocketChannel.open(); SocketAddress serverAddress = new InetSocketAddress("www.google.com",80); // connection to server sc.connect(serverAddress);
Once the socketChannel
is connected to the server, it is possible to send bytes to the serveur and to receive bytes from the server.
int socketChannel.read(ByteBuffer bb)
reads bytes from the server and stores them in the ByteBuffer
.socketChannel.write(ByteBuffer bb)
writes bytes of the ByteBuffer
to the server.By default, the methods socketChannel.read
and socketChannel.write
are blocking.
The method socketChannel.read
returns when at least 1 byte has been read or if the end of the connection has been detected. Method socketChannel.write
returns when all data in the work-zone of the buffer has been written.
Reading is done through method int read(ByteBuffer buff)
.
Read data is stored in the work-zone of the
ByteBuffer
(at most buff.remaining()).
Warning there is no guarantee that the work-zone of the ByteBuffer will be entirely filled.
If there is more data to read that the buffer can store, data is not lost; there will be read by a forthcoming call.
Method read returns
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); int read=sc.read(buffer); if (read==-1){ System.out.println("Connection closed for reading"); } else { System.out.println("Read "+ read +" bytes"); }
Writing is done by method write(ByteBuffer buff)
.
Data in the work-zone of the ByteBuffer
is entirely
written. By default, this method
blocks until the write operation is completed.
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); buffer.putLong(1l); buffer.putLong(2l); buffer.flip(); sc.write(buffer);
shutdownOutput()
. The server is notified
(its read returns -1 once it has read all the data written before the call to shutdownOutput()
).
shutdownInput()
. Purely local:
the server is not notified.
close()
.When receiving data, it is not possible a priori:
This problem is solved by the protocol
There are three common solutions:
If we only have a single block of data to send, it is possible to send it, and then shutdown the writing part of the channel. The server will understand that there is no more data when the method read returns -1.
(This is what HTTP 1.0 does)
Problems:
If we have several blocks of data to send, for instance several ASCII encoded strings, it is possible to choose a sequence of bytes as a "end mark" for each string.
(This is done in headers of HTTP, with \r\n)
Problems:
It is possible to send the size (for instance with a long
) of
data before to send them.
Problem:
(this is the principle of chunks in HTTP/1.1)