TCP Hole Punching and setReuseAddress() on Linux - java

I am working on a Java client/server application that involves P2P communication over TCP. I'm trying to implement TCP Hole Punching as described here: http://www.brynosaurus.com/pub/net/p2pnat/#sec-tcp. This requires simultaneously listening on and attempting to establish an outgoing connection using the same local TCP port. Apparently, this should work if the SO_REUSEADDR socket option is used, which I am setting via the setReuseAddress() method in Java. However, this is not working as I expected. Here is some test code:
import java.io.IOException;
import java.net.*;
public class Test {
public static void main(String args[]) {
new Thread() {
public void run() {
try {
ServerSocket ss = new ServerSocket();
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress(7077));
ss.accept();
} catch (Exception e) {
System.out.println("ServerSocket exception: " + e.getMessage());
}
}
}.start();
Socket s;
while (true) {
s = new Socket();
try {
s.setReuseAddress(true);
s.bind(new InetSocketAddress(7077));
s.connect(new InetSocketAddress("192.168.0.103", 7077));
break;
} catch (Exception e) {
System.out.println("Socket exception: " + e.getMessage());
try { s.close(); } catch (IOException e1) { }
try { Thread.sleep(1000); } catch (InterruptedException e1) { }
}
}
}
}
This works as expected in Windows 7: the ServerSocket listens on port 7077 in its own thread and the Socket repeatedly attempts to connect to 192.168.0.103:7077. However, under Linux (Ubuntu) only the first Socket connection attempt works, and subsequent attempts get the "Address already in use" BindException. Shouldn't I be able to establish an outgoing connection from a TCP source port that I'm also listening on simultaneously, and to reuse the local port number immediately after closing the socket, since I have the SO_REUSEADDR option enabled?

In Linux, both sockets need to set SO_REUSEADDR socket option. Thus, if we want two sockets, sock1 and sock2 to be bound ot the same port, then s2 would be able to reuse the port/address only if both sock1 and sock2 set SO_REUSEADDR.

You are never closing your client socket, unless there is an exception, making the point of SO_REUSEADDR a no-op.
....
s = new Socket();
try {
// ...
} catch (Exception e) {
System.out.println("Socket exception: " + e.getMessage());
// remove try block from here
try { Thread.sleep(1000); } catch (InterruptedException e1) { }
} finally {
try { s.close(); } catch (IOException e1) { }
}
....
In the above, I moved the closing of the socket to a newly created finally block so it is always executed, even if you break out the global while loop.
Since the socket is now closed under all conditions, the SO_REUSEADDR will use correctly now.

Related

socket() not throwing an exception despite server not running

I have written a client to run on an android device (android 6) and when the server is up and running it connects, however when the server is down the socket() call should throw an exception however it doesn't.
I originally tried it using the NDK and ran into a very similar issue (Android NDK socket connect() returning 0 when it should fail whilst on 3g).
I am assuming this is a bug with android at this point but any insight into a solution or work around would be much appreciated.
The code in question:
public class Client implements Runnable{
private Socket socket;
private InetAddress IP;
private int port;
public Client(int port){
try {
this.IP = InetAddress.getByName(server ip);
}
catch(UnknownHostException e){
Log.d("App1", "Unknown Host, connection failed");
System.exit(1);
}
this.port = port;
Log.d("App1", "initialised");
}
#Override
public void run(){
try {
this.socket = new Socket(this.IP, this.port);
Log.d("FiX1", "Connected");
listen();
}
catch(IOException e){
Log.d("FiX1,","connection failed");
System.exit(1);
}
finally
{
try{
socket.close(); // dispose
}
catch(IOException e){
System.exit(1);
}
}
}
public void listen() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String cominginText = "";
try {
cominginText = in.readLine();
Log.d("FiX1",cominginText);
} catch (IOException e) {
//error ("System: " + "Connection to server lost!");
System.exit(1);
break;
}
}
} catch (IOException e) {
System.exit(1);
}
}
}
The best solution I could find was to manually send an acknowledgement from the server that a connection had been made, the client would retry the connection if it did not receive this message within a certain time after it claimed to have connected.
There is a difference between a TCP connection "close" vs "disconnect".
If you close the socket connection from server using socket.close() then you will get exception in client side, if you try to read from that connection or vice versa.
However, if one side just disappears(shut down the program) then the other side has no way of knowing that. So the other side will wait for response for read call.
The TCP protocol was designed to be reliable in hostile communication environments and it will not normally decide a connection is closed just because it has not heard from the other side for a while.

How to connect to a Java Socket from a Dartlang Socket

I have a TCP Server written in java. I am trying to connect to it via a TCP client socket written in dartlang but the client written in dartlang times out almost immediately after the initial connection (my five second timeout set in the java code). I was able to send a message to the dart client in a test immediately after it connected but it soon timed out. However the data in the dartlang '{"packet_id":1}' never gets received by the server.
I tried writing a test server in dartlang and it was able to connect and send/receive messages without any issues.
Dart Client:
import 'dart:io';
import 'dart:async';
main(List<String> arguments) {
Future<Socket> future = Socket.connect('localhost', 12345);
future.then((client) {
print('connected to server!');
client.handleError((data){
print(data);
});
client.listen(
(data) {
print(new String.fromCharCodes(data));
},
onDone:(){
print("Done");},
onError: (error) {
print(error);
}
);
String requestHalls = '{"packet_id":1}';
client.write(requestHalls);
}).catchError(() {print('Error connecting');});
print('Hello world: ${dart_test.calculate()}!');
}
My Java client connects to the Java Server socket just fine.
EDIT: adding java code
creating the serversocket.
#Override
public void run() {
// Create Server Socket for clients to connect to
try {
// load data from config manager
ConfigManager config = HDAServer.getConfigManager();
int port = config.getServerPort();
String ip = config.getServerURL();
Inet4Address inet4 = (Inet4Address) Inet4Address.getByName(ip); // this allows for binding to domain name or ipv4 address
HDAServer.getLogger().info(String.format("Opening Server Socket at address(%s)= %s:%s", ip, inet4.getHostAddress(), port));
// attempt to bind
serverSocket = new ServerSocket(config.getServerPort(), 50, inet4);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// wait for incoming client connections
waitForConnections();
}
/**
* Method that waits for incoming connections, adds the connection to a new pool, and creates a
* new thread for the connection to run on.
*/
private void waitForConnections() {
// loop through waiting for incoming connections until server shuts down
while (!shutdown) {
Socket socket = null;
try {
// blocks thread waiting for a connection
socket = serverSocket.accept();
} catch (IOException e) {
if (e.getMessage().equals("socket closed")) {
HDAServer.getLogger().info("Shutting Down Server Socket");
continue;// skip to while check
} else {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// create our connection class, start it in a new thread, and add it to our connection list
HDAServerConnection newConn = new HDAServerConnection(socket);
new Thread(newConn).start();
connections.add(newConn);
HDAServer.getLogger().info(String.format("Client connected from: %1s", newConn.getRemoteIP()));
// reap dead connections
this.reapDeadConnections();
}
}
Servers "Client" HDAConnection class:
protected Socket socket;
protected DataInputStream inputStream;
protected DataOutputStream outputStream;
private boolean shutdown = false;
public HDAConnection(Socket socket) {
this.socket = socket;
// configure socket
try {
socket.setKeepAlive(true);
socket.setSoTimeout(5000);
socket.setTcpNoDelay(true);
} catch (SocketException ex) {
AbstractHDALogger.get().severe("Error configuring Socket Connection.", ex);
}
// create input/output stream for handling data
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
} catch (IOException ex) {
AbstractHDALogger.get().severe("Error creating input/output streams.", ex);
}
}
code that is listening for a message over the stream.
String response = null;
try {
response = new BufferedReader(new InputStreamReader(inputStream)).readLine();
} catch(SocketTimeoutException ste) {
shutdown();
AbstractHDALogger.get().severe(String.format("Socket Timed out(%s), closing Connection", this.getRemoteIP()), ste);
// TODO: handle exception
} catch (IOException e) {
// TODO Make this better?
// IO Exception probably means client disconnected, so we should terminate.
if (e.getMessage().equals("Connection reset")) {
shutdown();
AbstractHDALogger.get().info(String.format("Client Connection Disconnected(%s). Closing connection.", this.getRemoteIP()));
} else {
shutdown();
AbstractHDALogger.get().severe(String.format("Connection Error(%s), closing Connection", this.getRemoteIP()), e);
}
return null;
}
The socket is timing out on the Java Server because the dartlang client is never writing a newline at the end of its message and the Java Server is using the readline method to read. This function will only return when it reads a new line character. After reading the initial message from the dartlang client the Java server waits five seconds for more data to be sent but will time out as nothing more is ever sent.
The dartlang client should use the writeln function or appent a newline character to the end of its messages for the Java server BufferedReader to return.

Java Client/Server over IPv6

I am trying to get some experience with Java network programming. So I made a simple text based game. This is how it looks on serverside:
try {
socket = new ServerSocket(PORT);
while(true) {
new ConnectedPlayer(socket.accept()).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
And this is the client:
System.setProperty("java.net.preferIPv6Addresses","true");
try {
InetAddress ad = InetAddress.getByName("2a02:8070:b84:6b00:a1d1:30d7:346b:7c14");
socket = new Socket(ad, 9001);
output = socket.getOutputStream();
out = new PrintWriter(output);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
The address above is the Ipv6 address of the machine running
the server, I got this one by a website which detects my IP addresses,
it is different from what I get when I use ipconfig though, tried it with both.
When I tested both on the same machine with localhost, it worked.
However, now I wanted to try it on two different machines, one in a different network. Problem is, my internet provider apparently restricts me to something called "dual stack lite" which means my Server could only be reached over Ipv6 if I understand it correctly. But then, no port forwarding is needed there. When I test it now, it won't connect and gives a timeout after some time. I disabled firewalls on both machines.

Server hangs when client disconnects

i am creating a multiple-client/server app whenever any client disconnects from
my server it just hangs.
how can i set any condition that will tell me print some message whenever
any client disconnects from the server
here is my server code
class ServerThread implements Runnable {
public void run() {
Socket socket = null;
try {
System.out.println("server starting.......");
serverSocket = new ServerSocket(SERVERPORT);
} catch (IOException e) {
e.printStackTrace();
}
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("Ready to accept.......");
socket = serverSocket.accept();
System.out.println(" client Connected with ip address =" +socket.getRemoteSocketAddress().toString());
CommunicationThread commThread = new CommunicationThread(socket);
new Thread(commThread).start();
} catch (IOException e) {
e.printStackTrace();
System.out.println("catch block");
}
}
}
}
class CommunicationThread implements Runnable {
private Socket clientSocket;
private BufferedReader input;
public CommunicationThread(Socket clientSocket) {
this.clientSocket = clientSocket;
try {
this.input = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
String read = input.readLine();
updateConversationHandler.post(new updateUIThread(read));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
any help will be appreciated
It's not clear whether you mean disconnect because the conversation was over (ie: everything completed successfully) or the disconnect is because of some network problems (or the client canceled the request).
If it's the first case, then it's easy: the protocol you are using (your own, or http, or whatever) is in charge of defining how to determine that a conversation was over. If that situation arises, then you just close the socket.
If it's the second case, then you'd have to have an algorithm in place to determine whether or not the connection must be closed. For instance, by implementing a timeout, or a slow-read threshold. Take a look at the Socket's javadoc for instructions on how to set a timeout.
It's also worth noting that it's fine to create your own servers when you want to practice or learn something, but you'd be better off using an existing solution, like vert.x or a slimmed down version of Wildfly, for instance. The overhead of such servers is very low, nowadays, while still providing very robust networking capabilities.

Server Sockets Don't accept clients

I'm learning about sockets in java. I was able to connect a client socket to an online server, but I can connect them to my own server socket!
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
class Blargh2 {
public static void main(String[] args) {
Socket client = null;
ServerSocket server = null;
System.out.println("Line one reacehd!");
try {
server = new ServerSocket(4445);
} catch (Exception e) {
System.out.println("Error:" + e.getMessage());
}
System.out.println("Line two reacehd!");
try {
client = server.accept();
} catch (IOException e) {
System.out.println("Accept failed: 4444");
System.exit(-1);
}
System.out.println("Line three reacehd!");
try {
server.close();
client.close();
} catch (IOException e) {
System.out.println("Accept failed: 4444");
System.exit(-1);
}
}
}
The program reaches lines one and two but it never reaches line 3!
Can anyone help me solve this? Firewall also allows this connection...
It never reaches line 3 because you need a remote TCP socket (although it can be local, for testing) to connect to your socket on port 4445. You accept endpoint sockets on the server, which are used for communication with the remote client. There is actually no client here, so it waits indefinitely or until a timeout on the accept() call.
Try running this code and after you see line 2 is executed, then run the windows command:
telnet localhost 4445
Then you should see your line 3 executed.

Categories