Notes from Fundamental Networking in ...

Notes from Fundamental Networking in Java

IP is the most fundamental element of a family of protocols collectively known as TCP/IP, consisting of sub-protocols such as ARP-address resolution protocol, RARP-reverse address resolution protocol, ICMP-Internet control message protocol, BOOTP-bootstrap protocol,IGMP-Internet group management protocol,UDP-User datagram protocol, and TCP-Transmission

control protocol.

IP consists of an addressing system for hosts, the IP packet format definition, and the protocol proper-the rules about transmitting and receiving packets.

Network Addresses:

Network Interface, IP Addresses, Domain Names,

Port:

Each Internet host supports a large number of IP 'ports', which represent individual services within the host, and are identified by a 16-bit 'port number' in the range 1-65535. Many of these port numbers are preallocated: the 'well-known ports' in the range 1-1023, and the 'registered ports' in the range 1024-49151. Servers at the 'well-known ports' require special permission in some operating systems

A specific TCP or UDP service is addressed by the tuple {IP address, port number}. This tuple is also known as a 'socket address'.

Network Address Classes

In Java, an IP address is represented by a java.net.InetAddress. An IP port is represented in Java by an integer in the range 1-65535, most usually 1024 or above.

An IP socket address is represented in Java either by an { IP address, port number} tuple or by the SocketAddress class which encapsulates the tuple.

InetAddress Represents an IP address or a resolved hostname.

InetSocketAddress

Represents an IP socket address, a pair {IP address, port} or {hostname, port}. Can be constructed with just a {port}, in which case the 'wildcard' local IP address is used, meaning 'all local interfaces'.

NetworkInterface

Represents a local network interface, made up of an interface name (e.g. 'le0') and a list of IP addresses associated with the interface. Used for identifying local interfaces in multicasting.

Inet4Address,Inet6Address

Inet6Address.isIPv4CompatibleAddress returns true if the address an IPv4 compatible Ipv6 address; or false if address is IPv4 address.

Specail IP Addresses:

loopback 127.0.0.1 ::1

wildcard 0.0.0.0 ::0 This is used when creating sockets to indicate that they should be bound to 'all local IP addresses' rather than a specific one. This the normal case. In Java it can be indicated by an absent or null InetAddress.

IPV6

Compatibilty

IPV6 supports IPV4 via IPV4-compatible addresses. These are 128-bit IPV6 address whose high-order 96 bits are zero. the IPV4 address 192.168.1.24 can used in IPV6 as the IPV4-compatible address ::192.168.1.24.

Java’s IPV6support can be controlled via system properties. These allow you to disable IPV6 support, so that only IPV4 clients and servers are supported.

You cannot disable IPV4 support via these properties, although you can achieve the same effect by specifying only IPV6 network interfaces as local addresses when creating or binding sockets or server sockets.

IPV6 system properties

java.net.preferIPV4Stack false(default), true

By default, IPV6 native sockets are used if available, allowing applications to communicate with both IPV4 andIPv6 hosts. If this property is set to true, IPV4 native sockets are always used. The application will not be able to communicate with IPV6 hosts.

java.net.preferIPV6Stack false(default), true

By default, if IPV6 is available, IPV4-mapped addresses are preferred over IPV6 addresses, ‘for backward compatibility—e.g. applications that depend on an IPV4-only service, or on the [“dotted-quad”] representation of IPV4 addresses’.

If this property is set to true, IPV6 addresses are preferred over IPV4-style addresses, ‘allowing applications to be tested and deployed in environments where the application is expected to connect to IPV6 services’.

A TCP endpoint is defined as the tuple {ip address, port}.

There are two kinds of TCP socket: ‘active’ and ‘passive’ (more usually known as ‘listening’). A TCP server creates a TCP socket; ‘binds’ it to a port; puts it into the ‘listening’ state; and loops ‘accepting’ client connections. The client creates an active TCP socket and ‘connects’ it to the server port. The server ‘accepts’ this connection request, receiving in the process a new active socket representing its end of the connection. The server and the client are now connected, and can now reliably send each other any amount of data, in both directions simultaneously if necessary.

The resulting connection is defined by the tuple {local port, local address, remote port, remote address}.

Features of TCP

In TCP , data receptions are automatically acknowledged, sequenced, and resent as necessary. The application cannot receive corrupt or out-of-sequence data, or data ‘holes’.

Transmissions are automatically paced to the capacity of the intervening network, and retransmitted as necessary if not acknowledged.

Available bandwidth is used without saturating the network or being unfair to other network users.

TCP rapidly and reliably adjusts to changing network conditions—varying loads and routes.

TCP implements a ‘negotiated connect’ to ensure that a server is up and running, and that the server host has accepted a client’s connection request, before the client’s connection request completes.

TCP implements a ‘negotiated close’ to ensure that all data in flight is transmitted and received before the connection is finally dropped.

Costs of TCP

(a) Connection negotiation consists of a three-way exchange of packets.

The client sends a SYN; the server responds with a SYN/ACK; and the client responds with an ACK.

If the first SYN produces no response it is retried at increasing intervals a number of times. The first retry interval is implementation-dependent, typically three to six seconds, and is at least doubled on

each failure. The total time spent trying to connect is also implementationdependent, often limited to 75 seconds or three retries. Therefore, in total, a typical time for a completely unsuccessful connection attempt might be 6+12+24 = 42 seconds.

(b) Close negotiation consists of a four-way exchange of packets.

Each side sends a FIN and replies to an incoming FIN with an ACK.

(c) Data sequencing, acknowledgement, and pacing requires quite a bit of computation, which includes maintaining a statistically smoothed estimator of the current round-trip time for a packet travelling between the two endpoints.

(d) The provisions for congestion avoidance require an exponentially increasing retry timer on retransmissions (‘exponential backoff’) and a slow start to the transmission: this implies that the first few packets are generally exchanged at a sub-optimal speed, although the speed increases exponentially to the maximum feasible.

TCP is designed for bulk data transfer.

Socket Initialization - Servers

Backlog

TCP itself can get ahead of a TCP server application in accepting connections. It maintains a ‘backlog queue’ of connections to a listening socket which TCP iself has completed but which have not yet been accepted by the application.

This queue exists between the underlying TCP implementation and the server process which created the listening socket. The purpose of pre-completing connections is to speed up the connection phase, but the queue is limited in length so as not to pre-form too many connections to servers which are not accepting them at the same rate for any reason. When an incoming connection request is received and the backlog queue is not full, TCP completes the connection protocol and

adds the connection to the backlog queue. At this point, the client application is fully connected, but the server application has not yet received the connection as a result value of ServerSocket.accpet. When it does so, the entry is removed from the queue.

The backlog parameter specifies the maximum length of the backlog queue. If backlog is omitted, negative, or zero, a system-chosen default is used, eg, 50. The backlog specified may be adjusted by the underlying platform. If the backlog value is excessive for the platform it is silently adjusted to a legal value. No means exists in Java or the Berkeley Sockets API for discovering the effective backlog value.

Local Address

ServerSocket.getInetAddress/getLocalSocketAddress()

The local address of a server socket is the IP address at which it listens for incoming connections. By default, TCP servers listen at all local IP addresses. They can be made to isten at a single local IP address, by supplying a non-null localAddress to the constructor. If the address is omitted or null, the socket is bound to all local IP addresses.

A multi-homed server may only want to make itself available via one of these IP addresses rather than all of them.

Reusing the local address

ServerSocket.setReuseAddress(boolean reuse)

This setting is useful in development where servers are stopped and started frequently. By default, TCP prevents reuse of a listening port when there is an active or, more typically, a closing connection to the port. Closing connections persist for two minutes or so, for protocol integrity reasons. In development situations, the two-minute wait can be wasteful and annoying. Setting this option stops the waste and abates the annoyance.

The behaviour when changing this setting after a server socket is bound, or constructed with a non-default constructor, is undefined.

Setting the receive buffer size

you must set the receive buffer size for a server socket before binding it. Sockets returned by ServerSocket.accept inherit this setting

After a ServerSocket has been closed it cannot be reused, so it cannot be bound again.

Accept Client Connections

ServerSocket.accept returns a connected Ssocket ready for I/O. The connected socket inherits many of its settings from the server socket, specifically including its local port number, the size of its send and receive buffers, its blocking/non-blocking state, but specifically excluding ts read timeout.

Socket Output

In Java, output to a socket is done via an OutputStream obtained from the socket via Socket.getOutputStream.

All output operations on a TCP socket are synchronous as far as the local send buffer is concerned, and asynchronous as far as network and the remote application are concerned. All that a TCP output operation does is buffer the data locally to be sent according to the timing and pacing rules of TCP . If the local socket sending buffer is full, a write to a socket normally stalls until space in the sending buffer is released as a result of acknowledgements received for previous transmissions. As soon as enough local buffer space is available, control is returned to the application. If buffer space is available for part of the data, that part of it is buffered and the application stalls until further space appears; this continues until all the data has been written to the buffer. Obviously this means that if the amount of data to be written exceeds the send-buffer size, the initial excess will have been written to the network, and only the final non-excess part of the data will be buffered locally, when the write method returns.

After writing to a socket, there is no assurance that the data has been received by the application (or TCP) at the other end. The only way an application can be assured that a data transmission has arrived at the remote application is by receiving an acknowledgement explicitly sent by the remote application.

It is best to attach a BufferedOutputStream to the output stream obtained from the socket.Ideally the BufferedOutputStream’s buffer should be as large as the maximum request or response to be transmitted, if this is knowable in advance and not unreasonably large; otherwise it should be at least as large as the socket’s send-buffer. This minimises context-switches into the kernel, and it gives TCP more data to write at once, allowing it to form larger segments and use the network more efficiently. It also minimizes switching back and forth between the JVM and JNI. You must flush the buffer at appropriate points, ie, after completing the writing of a request message and before reading the reply, to ensure that any data in the BufferedOutputStream’s buffer gets to the socket.

To send Java data types, use a DataOutputStream attached either directly to the socket output stream or, better, to a BufferedOutputStream as shown above:

DataOutputStream dos = new DataOutputStream(out);

To send serializable Java objects, wrap an ObjectOutputream around your output stream:

ObjectOutputream oos = new ObjectOutputream(out);

oos.writeObject(obj);

ObjectOutputream extends DataOutputStream. However be aware that ObjectOutputream adds its own protocol to the data stream, so you can only use it for output if you use an ObjectOutputream at the other end. You can’t write data types with an ObjectOutputream and read them with a DataOutputStream.

Object Stream Deadlock

The following code fragment will always deadlock if present at both client and server:

ObjectInputStream in = new ObjectInputStream(socket.getInputStream());

ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());

The reason is that the ObjectInputStream at one end is trying to read the object stream header written by the ObjectInputStream at the other end, as part of their initialization, while the other end is trying to do the same thing in reverse.

Always create an ObjectOutputStream before an ObjectInputStream for the same socket.

Socket Input

Similarly, input from a socket is done via an input stream obtained from the socket via socket.getInputStream.

An input operation on a TCP socket blocks until at least some data has been received.

However, the length of data received may be less than the length of data requested. If some data had already been received into the socket receive buffer, the input operation will probably return just that data. If the receive buffer is empty, input blocks until some data has been received, probably a single TCP segment, and will probably return just that data.

This behaviour is reflected in the read methods inherited from InputStream of the socket input stream itself, and of the read methods in any interposed I/O stream, eg BufferedIunputStream, DataIutputStream, ObjectInputStream.

However, the DataInput.readFully method loops internally until the data requested is completely read, or until EOF or an exception occurs, whichever occurs first. The readFully method is called internally by the read methods and by ObjectInputStream.readObject, so these methods also either read the full amount of data required or throw an exception.

A count of -1 is received if and only if the other end has closed the socket or shutdown its output

The InputStream.available method of a socket input stream returns the count of data currently in the socket receive buffe.

It is best to attach a BufferedIunputStream to the input stream obtained from the socket. This minimises context switches into the kernel, and drains the socket receive buffer more quickly, which in turn reduces stalling at the sender. It also minimizes switching back and forth between the JVM and JNI. Ideally the BufferedIunputStream’s buffer should be at least as large as the socket’s receive buffer so that the receive buffer is drained as quickly as possible:

InputStream in = socket.getInputStream();

in = new BufferedInputStream(in, socket.getReceiveBufferSize());

Termination

The simplest way to terminate a connection is to close the socket, which termi- nates the connection in both directions and releases the platform’s socket resources.

Before closing a socket, the TCP ‘shutdown’ facility provides a means of terminating socket for input and output independently.

Output shutdown (half close)/Input shutdown

Closing a connected socket

In fact there are several ways to accomplish this:

(a) close the socket itself with Socket.close();

(b) close the output stream obtained from the socket by calling the method Socket.getOutputStream().close().

(c) close the input stream obtained from the socket by calling the method Socket.getIutputStream().close().

Any one of these is sufficient, and exactly one of them is necessary, to close the socket and release all its resources. You can’t use more than one of these techniques on any given socket. As a general rule you should close the output stream rather than the input stream or the socket, as the output stream may require flushing.

Both the server and the client must close the socket.

Shutting down a TCP Server ServerSocket.close()

Any concurrent or subsequent executions of ServerSocket.accept on that socket will throw a SockeException. However any existing accepted sockets are not affected by closing the ServerSocket from which they were accepted.

Exceptions in TCP

Exception Thrown bt & Clause

java.net.BindException

Thrown by constructors of Socket and ServerSocket, and their bind methods, if the requested local address or port cannot be assigned.

java.net.ConnectException

Thrown by constructors of Socket and its connect method on an error connecting to a remote address and port, usually because the connection is refused (nothing is listening at the specified {address, port}).

java.nio.IllegalBlockingModeException

Thrown by Socket.connect, operations on Socket input and output streams, and ServerSocket.accept if the socket has an associated channel which is in non-blocking mode; from JDK 1.4.

java.io.InterruptedIOException

Thrown by operations on Socket input streams if a timeout has occurred; prior to JDK 1.4.

java.io.IOException

Base I/O exception class. Derived exception classes relevant to TCP include BindException, ConnectException, EOFException, InterruptedIOException, NoRouteToHostException, ProtocolException, SockeException, and UnknownHostException.

java.net.NoRouteToHostException

Thrown by non-default constructors of Socket and its connect method, indicating that an error has occurred while connecting to a remote address and port, most usually if the remote host cannot be reached because of an intervening firewall, or if an intermediate router is down.

java.net.ProtocolException

Thrown by constructors and methods of Socket and ServerSocket, and operations on Socket input and output streams, indicating that an error occourred in the underlying protocol, such as a TCP error.

java.lang.SecurityException

Thrown by several methods of Socket and ServerSocket if a required SocketPermission is not granted.

java.net.SockeException

Thrown by many Socket methods and operations on Socket input streams indicating that an underlying TCP error has occurred, or the socket was closed by another thread other than via InterruptibleChannel.close.

If the message contains the text ‘Connection reset’, the other end of the connection has issued a reset (RST): the Socket is useless from this point on, and it should be closed and discarded.

Many exception classes are derived from this one, including BindException and ConnectException.

java.net.SockeTimeoutException

Thrown by operations on Socket input streams indicating that a timeout has occurred; from JDK 1.4; extends InterruptedIOException for backwards compatibility with pre-JDK 1.4 programs.

java.net.UnknownHostException

Thrown by factory methods of InetAddress, and when using String hostnames which are implicitly resolved by those methods, indicating that the IP address of the named host cannot be determined from the naming service.

Socket Options

Socket Timeouts

Socket.setSoTimeout(timeout)

where timeout is specified in milliseconds, and must be either positive, indicating a finite timeout, or zero, indicating an infinite timeout. By default, the read timeout is infinite.

If the timeout has been set to a positive (finite) value prior to a blocking read operation on the socket, the read will block for up to the timeout period if data is not available, and will then throw an InterruptedIOException(prior jdk1.4) or SockeTimeoutException(from jak1.4).

If the timeout is infinite, the read will block forever, or until an error occurs.

For clients which have just transmitted a request and are waiting for a reply, the duration of the timeout should take account of the expected transmission times in both directions plus the latency of the request—the execution delay at the other end while the reply is being retrieved or computed. How long you should wait in relation to this total expected time is a policy question: as a starting

point, the time-out might be set to twice the sum of the expected time. In general, timeouts should be set slightly too long rather than slightly too short.

Good networking programming practice requires that retries of transactions which have timed out should occur at intervals which are initially random within a reasonable interval, to avoid the ‘thundering herd’ problem, and which increase exponentially, to reduce the network load which may have been part of the initial problem.

For servers which are waiting for a client request, the timeout value is strictly a matter of policy: how long is the server prepared to wait for a request before abandoning the connection? The period chosen should be long enough to support heavy network loads and a reasonable amount of client processing, but not so long as to tie up precious server resources for absurd lengths of time. (The

resources allocated to a server connection consist of the connected socket itself and, usually, a thread and some sort of client context.)

ServerSocket.setSoTimeout(int timeout)

This setting determines how long an application will block in ServerSocket.accept before getting

an InterruptedIOException. This setting is not inherited by accepted connections, ie, by sockets returned from ServerSocket.accept.

Socket Buffers

TCP allocates a send buffer and a receive buffer to each socket. These buffers exist in the address space of the kernel or the TCP protocol stack (if different), not in the JVM or process address space. The default size of these buffers is determined by the underlying platform’s implementation of TCP , not by Java.

Socket.setReceiveBufferSize/setSendBufferSize(int size)

ServerSocket.setReceiveBufferSize(int size)

where size is specified in bytes. Values supplied to these methods only act as a hint to the underlying platform, and may be adjusted in either direction to fit into the allowable range, or rounded up or down to appropriate boundaries. You can set the send buffer size of a socket at any time before closing it.

In general, the larger the buffer, the more efficiently TCP can operate. Large buffer sizes utilize the capacity of the network more effectively: they reduce the number of physical writes to the network; amortize the 40-byte space costs of the TCP and IP packet headers over a larger packet size; allow more data to be in flight, ‘filling the pipe’; and allow more data to be transmitted before stalling.

The send buffer size should be at least as big as the receive buffer at the other end.

Whatever the buffer size is, you should help TCP by writing in chunks of at least that size, eg, by using a BufferedOutputStream or a ByteBuffer of at least that size.

Multi-Homing - Servers

A TCP server normally listens at all local IP addresses, and such a server need not usually be concerned with the fact that it may be running in a multi-homed host.

The following are situations in which a TCP server may need to be aware of multi-homing.

If the server is to service only one subnet, it should bind itself to the appropriate local IP address

Nagle’s Algorithm

Nagle’s algorithm was a minor modification to TCP adopted in 1984. It deters TCP from sending a sequence of small segments in circumstances when the data is being injected slowly by the application. By default, Nagle’s algorithm is enabled, but it can be disabled by setting an option known as the ‘no delay’ option:

Socket.setTcpNoDelay(boolean noDelay)

This can be confusing: remember that setTcpNoDelay(true) turns Nagle’s algorithm off and that its default state is on.

Nagle’s algorithm operates simply by delaying the transmission of new TCP segments while any data remains unacknowledged, so that they can be coalesced into a smaller number of larger segments. It turns out that this has other benefits, such as preventing retransmissions to a dead host.

In almost all situations where you may think you want to disable the Nagle algorithm, the real answer is to use a BufferedOutputStream with a buffer size at least equal to the largest request or reply size, and write the entire request or reply out in one operation with the BufferedOutputStream.flush method. The entire buffer will be transmitted in a single TCP segment, which is a much better use of the network than tranmitting the same data in many small segments.

Linger on Close

Socket.setSoLinger(boolean linger, int timeout) controls the behaviour of TCP when a socket is closed.

TCP defines three different behaviours for ‘linger on close’.

Linger timeout Description

false ignored Default. When the socket is closed, a the closing thread is not blocked but the socket is not destroyed immediately: it first enters a Closing state while any remaining data is transmitted and the FIN-ACK close protocol is exchanged with the other end; the socket then enters the TIME-WAIT state, which lasts for twice the maximum lifetime of a TCP segment, to ensure that further data transmissions to the socket are rejected with a TCP RST , the interval being chosen so that TCP can retransmit the final part of the close protocol if necessary, and so that the local and remote endpoint pair which define the connection are not reused during the TIME-WAIT; period, so that any delayed data segments from the closed connection won’t be delivered to the new connection. When TIME-WAIT; expires the socket is destroyed.

true ≠0 ‘Linger’. When the socket is closed, a the closing thread is blocked (‘lingers’) while any pending data is sent and the close protocol is exchanged, or the timeout expires, whichever occurs first; the thread then continues. If the timeout expires, either: (1) the connection is ‘hard-closed’, or (2) any remaining data remains queued for delivery, after which the connection is closed via -FIN-ACK as described above.

True 0 ‘Hard close’. When the socket is closed, a any pending data is discarded and the close protocol exchange (FIN-ACK) does not occur: instead, an RST is issued, causing the other end to throw a SockeException ‘connection reset by peer’.

Keep-Alive

Socket.setKeepAlive(boolean keepAlive)

TCP ‘keep-alive’ is a technique which probes an active TCP connection to ensure that the other end is still alive.

If the other end is alive, it will acknowledge the probe. If a keep-alive probe is not acknowledged after a small number of retries, the socket is put into a ‘reset’ state, which will cause a SocketExcpetion to be thrown on the next read, write, or close operation on the socket.

Keep-alive should be viewed as a kind of ‘court of last resort’ for finally terminating dead connections after two hours if it is available. It should not be relied on as a substitute for sensible use of timeouts. You should consider using application-level connection probes (‘pings’) where connections are expected to be of long duration.


Resources:

Fundamental Networking in Java


Post a Comment

Labels

Java (159) Lucene-Solr (110) All (60) Interview (59) J2SE (53) Algorithm (37) Eclipse (35) Soft Skills (35) Code Example (31) Linux (26) JavaScript (23) Spring (22) Windows (22) Web Development (20) Tools (19) Nutch2 (18) Bugs (17) Debug (15) Defects (14) Text Mining (14) J2EE (13) Network (13) PowerShell (11) Chrome (9) Continuous Integration (9) How to (9) Learning code (9) Performance (9) UIMA (9) html (9) Design (8) Dynamic Languages (8) Http Client (8) Maven (8) Security (8) Trouble Shooting (8) bat (8) blogger (8) Big Data (7) Google (7) Guava (7) JSON (7) Problem Solving (7) ANT (6) Coding Skills (6) Database (6) Scala (6) Shell (6) css (6) Algorithm Series (5) Cache (5) IDE (5) Lesson Learned (5) Miscs (5) Programmer Skills (5) System Design (5) Tips (5) adsense (5) xml (5) AIX (4) Code Quality (4) GAE (4) Git (4) Good Programming Practices (4) Jackson (4) Memory Usage (4) OpenNLP (4) Project Managment (4) Python (4) Spark (4) Testing (4) ads (4) regular-expression (4) Android (3) Apache Spark (3) Become a Better You (3) Concurrency (3) Eclipse RCP (3) English (3) Firefox (3) Happy Hacking (3) IBM (3) J2SE Knowledge Series (3) JAX-RS (3) Jetty (3) Restful Web Service (3) Script (3) regex (3) seo (3) .Net (2) Android Studio (2) Apache (2) Apache Procrun (2) Architecture (2) Batch (2) Build (2) Building Scalable Web Sites (2) C# (2) C/C++ (2) CSV (2) Career (2) Cassandra (2) Distributed (2) Fiddler (2) Google Drive (2) Gson (2) Html Parser (2) Http (2) Image Tools (2) JQuery (2) Jersey (2) LDAP (2) Life (2) Logging (2) Software Issues (2) Storage (2) Text Search (2) xml parser (2) AOP (1) Application Design (1) AspectJ (1) Bit Operation (1) Chrome DevTools (1) Cloud (1) Codility (1) Data Mining (1) Data Structure (1) ExceptionUtils (1) Exif (1) Feature Request (1) FindBugs (1) Greasemonkey (1) HTML5 (1) Httpd (1) I18N (1) IBM Java Thread Dump Analyzer (1) JDK Source Code (1) JDK8 (1) JMX (1) Lazy Developer (1) Mac (1) Machine Learning (1) Mobile (1) My Plan for 2010 (1) Netbeans (1) Notes (1) Operating System (1) Perl (1) Problems (1) Product Architecture (1) Programming Life (1) Quality (1) Redhat (1) Redis (1) Review (1) RxJava (1) Solutions logs (1) Team Management (1) Thread Dump Analyzer (1) Troubleshooting (1) Visualization (1) boilerpipe (1) htm (1) ongoing (1) procrun (1) rss (1)

Popular Posts