Let us start with a bit of information on sockets.
In the case that we want to use the same port to connect and to accept incoming connections, we have to first start connecting to the remote host and then to start accepting incoming connections, because we can't bind to a Socket if it is already listening for connections, which is the case when we start accepting, and after that we won't be able to bind to the same port when we try to connect. Although on some systems it would be possible, it all depends on the sockets implementation in the OS.
As we can read in the Socket man page -
http://www.skrenta.com/rt/man/socket.4.html :
"
SO_REUSEADDR
Indicates that the rules used in validating
addresses supplied in a bind(2) call should allow
reuse of local addresses. For PF_INET sockets this
means that a socket may bind, except when there is
an active listening socket bound to the address.
When the listening socket is bound to INADDR_ANY
with a specific port then it is not possible to
bind to this port for any local address.
"
More on this topic:
https://stackoverflow.com/questions/14388706/socket-options-so-
reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t/14388707#14388707
Now for the connections.
Connect:
We need to bind only using a port. When connecting the local address is usually "address any", that is why we only supply a port number. We are connecting to the public IP of the other machine (IP that is visible from the Internet) and we supply the IP address and the port number.
Socket socket = new Socket();
socket.setReuseAddress(true);
InetSocketAddress inetSocketAddress = new InetSocketAddress(44445);
socket.bind(inetSocketAddress);
InetSocketAddress socketAddress = new InetSocketAddress("11.222.333.555", 44446);
socket.connect(socketAddress, 30000);
Accept:
When accepting a connection we need to bind to the local IP address. If for some reason we don't get the actual local IP addres from getLocalHost (the OS may not allow us to have access to it and instead we either get the "address any" 0.0.0.0 or the loopback 127.0.0.1) we have to supply it ourselves:
InetAddress localAddress = InetAddress.getByName("192.168.100.10");
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
InetAddress localAddress = InetAddress.getLocalHost();
InetSocketAddress socketAddress = new InetSocketAddress(localAddress, 44446);
serverSocket.bind(socketAddress);
Socket socket = serverSocket.accept();
If the clients are in the same LAN network or neither of them are behind a Firewall or a NAT, we can have one of them only accept incoming connection and the other one only connect and they will establish a connection. But if one or both of them are behind a Firewall / NAT we would have both of them connect and accept so that they punch holes in their Firewall / NAT so that the incoming connection can go through.
We have to remember that Peer-to-Peer connection is not guaranteed to succeed, because the hole punching may not be possible depending on the network setup that our hosts are in, either by accident or the network may have been setup by intention to prevent such connections on purpose. In that case we can have a relayed Peer-to-Peer communication between the two hosts - both connect to a server on the Internet and it relays the data between them. More on Peer-to-Peer communications over the Internet -
http://www.brynosaurus.com/pub/net/p2pnat/
Connect and Accept part as Java methods and how to call them:
accepting("111.222.333.444", 44446);
connecting(44445, "111.222.333.555", 44446);
Of course each of these methods has to be called on a separate thread.
1: public void accepting(String aHostLocal, int aPortLocal) {
2: System.out.println("accepting");
3: ServerSocket serverSocket = null;
4: OutputStream outputStream = null;
5: BufferedReader bufferedReader = null;
6: try {
7: serverSocket = new ServerSocket();
8: serverSocket.setReuseAddress(true);
9:
10: System.out.println("accepting, bind to=" + aHostLocal);
11: InetSocketAddress socketAddress = new InetSocketAddress(aHostLocal, aPortLocal);
12: serverSocket.bind(socketAddress);
13:
14: System.out.println("accepting, accept");
15: Socket socket = serverSocket.accept();
16:
17: bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
18: int character = bufferedReader.read();
19: System.out.println("accepting, read character=" + character);
20: outputStream = socket.getOutputStream();
21: outputStream.write(character);
22: outputStream.flush();
23: } catch (IOException e) {
24: e.printStackTrace();
25: } finally {
26: if(bufferedReader != null) {
27: try {
28: bufferedReader.close();
29: } catch (IOException e) {
30: e.printStackTrace();
31: }
32: }
33: if(outputStream != null) {
34: try {
35: outputStream.close();
36: } catch (IOException e) {
37: e.printStackTrace();
38: }
39: }
40: if(serverSocket != null) {
41: try {
42: serverSocket.close();
43: } catch (IOException e) {
44: e.printStackTrace();
45: }
46: }
47: }
48: System.out.println("accepting, end");
49: }
50:
51: public void connecting(int aPortLocal, String aHostRemote, int aPortRemote) {
52: System.out.println("connecting");
53: Socket socket = null;
54: OutputStream outputStream = null;
55: BufferedReader bufferedReader = null;
56: try {
57: socket = new Socket();
58: socket.setReuseAddress(true);
59:
60: if(aPortLocal > 0) {
61: InetSocketAddress inetSocketAddress = new InetSocketAddress(aPortLocal);
62: System.out.println("connecting, bind to=" + inetSocketAddress);
63: socket.bind(inetSocketAddress);
64: }
65:
66: System.out.println("connecting, mHostRemote=" + aHostRemote);
67: InetSocketAddress socketAddress = new InetSocketAddress(aHostRemote, aPortRemote);
68: System.out.println("connecting, connect");
69: socket.connect(socketAddress, 30000);
70:
71: System.out.println("connecting, sending 10");
72: outputStream = socket.getOutputStream();
73: outputStream.write(10);
74: bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
75: int character = bufferedReader.read();
76: System.out.println("connecting received character=" + character);
77: outputStream.flush();
78: } catch (IOException e) {
79: e.printStackTrace();
80: } finally {
81: if(bufferedReader != null) {
82: try {
83: bufferedReader.close();
84: } catch (IOException e) {
85: e.printStackTrace();
86: }
87: }
88: if(outputStream != null) {
89: try {
90: outputStream.close();
91: } catch (IOException e) {
92: e.printStackTrace();
93: }
94: }
95: if(socket != null) {
96: try {
97: socket.close();
98: } catch (IOException e) {
99: e.printStackTrace();
100: }
101: }
102: }
103: System.out.println("connecting, end");
104: }