I don't know how to clearly describe my question in title, as for me it is a little complicated. What i am doing is try to implement a TCP peer to peer demo, in which a local port must be both for listening and initiating a socket.
I will give a detailed description.
I will give a java implementation which will listen and initiate connection on a single local port. Code will explain my idea.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Just for testing socket SO_RESUEADDR. If set SO_RESUEADDR to true, we can use
* a single local port to listen for incoming TCP connections, and to initiate
* multiple outgoing TCP connections concurrently. By this way we can implement
* TCP hole punching(establish P2P connection traversal through NAT over TCP).
*/
public class TcpPeer {
// TCP port is a different source from UDP port, it means you can listen on
// same port for both TCP and UDP at the same time.
private int localport = 7890;
private ServerSocket peerSock;
private Socket serverSocket;
public TcpPeer(final String serverHost, final int serverPort, final int localPort)
throws Exception {
this.localport = localPort;
Thread server = new Thread(new Runnable() {
#Override
public void run() {
try {
peerSock = new ServerSocket();
peerSock.setReuseAddress(true);
peerSock.bind(new InetSocketAddress("localhost", localport));
System.out.println("[Server]The server is listening on " + localport + ".");
while (true) {
try {
serverSocket = peerSock.accept();
// just means finishing handshaking, and connection
// established.
System.out.println("[Server]New connection accepted"
+ serverSocket.getInetAddress() + ":" + serverSocket.getPort());
BufferedReader br = getReader(serverSocket);
PrintWriter pw = getWriter(serverSocket);
String req = br.readLine();
System.out.println("[Server][REQ]" + req);
pw.println(req);
pw.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// server.setDaemon(true);
server.start();
Thread.currentThread();
// sleep several seconds before launch of client
Thread.sleep(5 * 1000);
final int retry = 5;
Thread client = new Thread(new Runnable() {
#Override
public void run() {
Socket socket = new Socket();
try {
socket.setReuseAddress(true);
System.out.println("[Client]socket.isBound():" + socket.isBound());
socket.bind(new InetSocketAddress("localhost", localport));
for (int i = 1; i < retry; i++) {
try {
socket.connect(new InetSocketAddress(serverHost, serverPort));
System.out.println("[Client]connect to " + serverHost + ":"
+ serverPort + " successfully.");
break;
} catch (Exception e) {
e.printStackTrace();
System.out.println("[Client]fail to connect " + serverHost + ":"
+ serverPort + ", try again.");
Thread.currentThread().sleep(i * 2 * 1000);
if (i == retry - 1) return;
}
}
PrintWriter pw = getWriter(socket);
String msg = "hello world!";
pw.println(msg);
/**
* Got response from the server socket.
*/
BufferedReader br = getReader(socket);
String resp = br.readLine();
System.out.println("[Client][RESP-1]" + resp);
pw.close();
br.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
client.start();
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut, true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public static void main(String[] args) throws Exception {
if (args.length != 3) {
System.out.println("[Usage] java " + TcpPeer.class.getCanonicalName()
+ " [serverHost] [serverPort] [localPort]");
System.exit(0);
}
new TcpPeer(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]));
}
}
Now we launch 2 jvm processes:
ps#1> java TcpPeer localhost 2000 4000
ps#2> java TcpPeer localhost 4000 2000
Finally when 2 processes got stable, they will give below outputs:
ps#1>
[Server]The server is listening on 2000.
[Client]socket.isBound():false
[Client]connect to localhost:4000 successfully.
[Client][RESP-1]hello world!
ps#2>
[Server]The server is listening on 4000.
[Server]New connection accepted/127.0.0.1:2000
[Server][REQ]hello world!
[Client]socket.isBound():false
java.net.BindException: Address already in use: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
java.net.SocketException: Socket operation on nonsocket: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
java.net.SocketException: Socket operation on nonsocket: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
java.net.SocketException: Socket operation on nonsocket: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
From the output, we can figure out that interaction flow as below:
ps#1 listen on 2000.
ps#2 listen on 4000.
ps#1 connect to ps#2, from localhost:2000 -> localhost:4000.
ps#2 close the connection which established at step#3.
ps#2 try to connect to ps#1 at 2000, failed!
Why ps#2 cannot connect to ps#1 at step#4? I also monitor the net status of OS.
Below is net status right after step#3.
And also the net status right after step#4.
192.168.2.107 is localhost
Could you please give me a comment on my case? thanks!
I have printed out the original exception message when fail to reconnect, but i don't have much idea about those exception.
You shouldn't bind the client socket, and also not set the SO_REUSEADDR on client socket.
If you do not bind a client socket, the system will automatically assign a port number for you.
Also, SO_REUSEADDR doesn't mean the address (ip/port numbers) can be reused while the socket is still open. It's so that when a socket has been closed and is in the TIME_WAIT state, you can bind to that again.
Your client thread exits just after the first exchange of messages, it lacks while(true) part.
If you get through your connection retries without a successful connection you need to quit, not fall through into the I/O code. This is why you are getting the actual exception.
You also need to print out why the connections are failing. At the moment this is the most important information, and you are suppressing it. Print the exception message. This is a general principle: don't make up your own message, use the one you're given. It is almost certainly more specific.
Instead of new InetAddress("localhost") use null when binding the ServerSocket.
I'm not convinced that you can retry a connect on a socket. If it fails, try creating a new Socket.
Related
If I execute the JUnit test below WITHOUT the line "inputStream.close()" (see below), more than 60000 requests can be processed (I killed the process then). WITH this line, I did not manage making more than 15000 requests, because of:
java.net.SocketException: No buffer space available (maximum connections reached?): connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:529)
at java.net.Socket.connect(Socket.java:478)
at java.net.Socket.<init>(Socket.java:375)
at java.net.Socket.<init>(Socket.java:189)
at SocketTest.callServer(SocketTest.java:60)
at SocketTest.testResourceConsumption(SocketTest.java:52)
I run it on Windows, before starting the test I wait for the netstat list to be back to normal.
Questions:
why is calling socketInputStream.close() on the client side harms in this case?
or what is wrong with the code?
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import junit.framework.TestCase;
public class SocketTest extends TestCase {
private static final int PORT = 12345;
private ServerSocket serverSocket;
public void setUp() throws Exception {
serverSocket = new ServerSocket(PORT);
new Thread(new Runnable() {
#Override
public void run() {
while(true) {
try {
final Socket socket = serverSocket.accept();
new Thread(new Runnable() {
#Override
public void run() {
try {
OutputStream outputStream = socket.getOutputStream();
for(int i = 0; i < 100; i++) {
outputStream.write(i);
}
outputStream.close();
// in fact the previous line calls this already:
// socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}).start();
}
public void testResourceConsumption() throws Exception {
for (int i=0; i<1000000; i++) {
callServer();
if (i % 1000 == 0) {
System.out.println(i);
}
}
}
private void callServer() throws Exception {
Socket clientSocket = new Socket("localhost", PORT);
InputStream inputStream = clientSocket.getInputStream();
for (int i = 0; i < 100; i++) {
assertEquals(i, inputStream.read());
}
///////////////// THIS LINE IS INTERESTING
inputStream.close();
// in fact the previous line calls this already:
// clientSocket.close();
}
public void tearDown() throws Exception {
serverSocket.close();
}
}
When you explicitly call inputStream.close() you change the order of TCP graceful connection release. In this case the client side of the connection is closed prior to receiving FIN packet from the server, thus leaving the socket in TIME_WAIT state. At some point all local ports for outgoing connections become occupied with these TIME_WAIT sockets, and no more outgoing connections can be made.
When you don't call inputStream.close(), the connections are shut down by the server side with outputStream.close() call. The client sockets have enough time to receive FIN from the server, and then at garbage collection time they get gracefully closed by the finalizer method.
There are two options to fix the procedure in your test:
Preferable way is to continue reading from inputStream until you receive -1, which means that the other side has initiated the connection shutdown (i.e. FIN is received). Just insert assertEquals(-1, inputStream.read()); before inputStream.close();
The second option is to force the abortive release by setting
clientSocket.setSoLinger(true, 0);
In this case inputStream.close() will force client to send RST and abort the connection.
More about orderly and abortive TCP connection release.
I tried to create a simple chat via sockets and it works for LAN right now and for "localhost" too, of course, but not among different computers through the internet and thats the real point of a chat, isn't it!
socket = new Socket("--ip address--", 7345);
This line works for --ip address-- = localhost and --ip address-- = ""my local ip-address"", but with the ip address of my router, it throws a java.net.ConnectException
" java.net.ConnectException: Connection refused: connect "
I want to use my pc as server and not a real server, maybe there is the problem, but I think that there must be a solution. If that is an absurd simple question, don't doom me, because I'm a real newbie in network programming.
When you are creating a server, you have to use server socket with the ip address of where it's running...
The server socket needs to be running on your machine of your machine's ip address.
With your router, you need to forward the connections to the port you are running on your that is hosting the server.
Then you should be able to connect from outside your local network.
Without the code for what your are doing it's hard to tell if that's the only problem here is a simple chat server that might give you guidance.
import java.net.*;
import java.io.*;
public class ChatServer
{ private Socket socket = null;
private ServerSocket server = null;
private DataInputStream streamIn = null;
public ChatServer(int port)
{ try
{
System.out.println("Binding to port " + port + ", please wait ...");
server = new ServerSocket(port);
System.out.println("Server started: " + server);
System.out.println("Waiting for a client ...");
socket = server.accept();
System.out.println("Client accepted: " + socket);
open();
boolean done = false;
while (!done)
{ try
{ String line = streamIn.readUTF();
System.out.println(line);
done = line.equals(".bye");
}
catch(IOException ioe)
{
done = true;
}
}
close();
}
catch(IOException ioe)
{ System.out.println(ioe);
}
}
public void open() throws IOException
{ streamIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
}
public void close() throws IOException
{ if (socket != null) socket.close();
if (streamIn != null) streamIn.close();
}
public static void main(String args[])
{ ChatServer server = null;
if (args.length != 1)
System.out.println("Usage: java ChatServer port");
else
server = new ChatServer(Integer.parseInt(args[0]));
}
}
I am wondering about, how I can wait a ServerSocket connection on Socket client side.
When I execute the server (send) first, it waits the client connection with the method ServerSocket.accept. The problem is that the client (receive) cannot be executed without the server has been executed first. I would like add a condition to allow the client to wait the connection from the server. Is it possible?
SEND (server)
ServerSocket servsock = new ServerSocket(1234);
Socket sock = servsock.accept();
RECEIVE (client)
Socket sock = new Socket(from, 1234);
ERROR (client)
java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:432)
at java.net.Socket.connect(Socket.java:529)
at java.net.Socket.connect(Socket.java:478)
at java.net.Socket.<init>(Socket.java:375)
at java.net.Socket.<init>(Socket.java:189)
Thank you.
There is no built-in way to do that. You'll have to catch the exception on the client side, and implement a retry mechanism yourself.
Pseudo-code:
Socket sock = null;
while (retryCounter < threshold) {
try {
retryCounter++;
sock = new Socket(form, 1234);
} catch (...) {
// handle exceptions
// possibly add a sleep period
}
}
if (sock == null) {
// you failed to connect
} else {
// you're connected
}
Be careful if you have a GUI: you might need to implement some of that in a separate thread or use timers to avoid freezing your UI.
Thank to Mat, it works.
Socket sock = null;
while (true) {
try {
sock = new Socket(from, 1234);
if (sock != null) { break; }
}
catch (IOException e) { Thread.sleep(1000); }
}
// rest of the code
I am trying to implement this example here: Reading from and Writing to a Socket
I copied and pasted the code into NetBeans. I changed the port name "taranis" to "localhost" and tried to run the example, but I got the error:
run: Couldn't get I/O for the connection to: localhost. Java Result: 1
BUILD SUCCESSFUL (total time: 1 second)
I also tried to substitute localhost for my actual hostname of my laptop, but it gives the similar error. Can you help pinpoint what I am doing wrong?
Edit: In regards to Mark's recommendation, when I substitute
System.err.println("Couldn't get I/O for " + "the connection to: localhost.");
with
e.printStackTrace();
I get:
run:
java.net.ConnectException: Connection refused: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:529)
at java.net.Socket.connect(Socket.java:478)
at java.net.Socket.<init>(Socket.java:375)
at java.net.Socket.<init>(Socket.java:189)
at EchoClient.main(EchoClient.java:12)
Java Result: 1
BUILD SUCCESSFUL (total time: 3 seconds)
The echo service is not listening. Why not write your own? Run the application below and change your client to connect to the same port (8000).
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer {
private static final int PORT = 8000;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(PORT);
}
catch (IOException e) {
System.err.println("Could not listen on port: " + PORT);
System.exit(1);
}
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept();
}
catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
System.out.println("Echo server started");
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("echoing: " + inputLine);
out.println(inputLine);
}
out.close();
in.close();
clientSocket.close();
serverSocket.close();
}
}
Btw, the next example (knock-knock server) does work and gives a nice example of using a 'protocol' class.
I don't think the echo service is running by default, when I tried a quick test it on my Win XP client, it did not work:
H:\>telnet localhost 7
Connecting To localhost...Could not open connection to the host, on port 7:
Connect failed
H:\>
So to make your code work, you could try pointing it to a server that has the echo service running.
For future reference, the echo service is commonly disabled by default. I'm using windows 7, to enable it I followed the instructions found here:
http://www.windowsnetworking.com/articles_tutorials/windows-7-simple-tcpip-services-what-how.html
Example worked fine for me afterwards.
For XP:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/sag_tcpip_pro_simptcpinstall.mspx?mfr=true
Do I have to close all the sockets after using it? Where should I put them in this code? My program just works normally when I run it. However, when I re-run it, it said "Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind". Therefore, I think I did not close all the socket after using it.
import java.io.*;
import java.net.*;
import java.util.*;
public class Server2 {
public static void main(String args[]) throws Exception {
int PORT = 5555; // Open port 5555
//open socket to listen
ServerSocket server = new ServerSocket(PORT);
Socket client = null;
while (true) {
System.out.println("Waiting for client...");
// open client socket to accept connection
client = server.accept();
System.out.println(client.getInetAddress()+" contacted ");
System.out.println("Creating thread to serve request");
ServerStudentThread student = new ServerStudentThread(client);
student.start();
}
}
}
Call server.close() in a finally block.
ServerSocket server = new ServerSocket(PORT);
try {
while (true) {
System.out.println("Waiting for client...");
// open client socket to accept connection
Socket client = server.accept();
System.out.println(client.getInetAddress()+" contacted ");
System.out.println("Creating thread to serve request");
ServerStudentThread student = new ServerStudentThread(client);
student.start();
}
} finally {
server.close();
}
Address already in use: JVM_Bind - means, that you operation system is not closed socket after previous use. It closes on timeout about 30-180 seconds.
I don't realy know how to do this in java, but in C code it may be done like this, before bind system function call:
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
That mean: set the flag (option) SO_REUSEADDR to sockfd socket.
In java must exists appropriate mechanism for do the same.
You are running an infinite while loop , have a boolean variable to say when to stop , i think you are not exiting gracefully, that is why port is not closed.
May be you can try like this
import java.io.*;
import java.net.*;
import java.util.*;
public class Server2 {
static int NUM_CONN_TO_WAIT_FOR=15;
boolean exitServer =false;
public static void main(String args[]) throws Exception {
int PORT = 5555; // Open port 5555
//open socket to listen
ServerSocket server = new ServerSocket(PORT);
Socket client = null;
static int connections =0;
try
{
while (!exitServer ) {
System.out.println("Waiting for client...");
// open client socket to accept connection
if ( connections < NUM_CONN_TO_WAIT_FOR )
{
client = server.accept();
System.out.println(client.getInetAddress()+" contacted ");
System.out.println("Creating thread to serve request");
ServerStudentThread student = new ServerStudentThread(client);
student.start();
} else
{
exitServer =true;
}
connections++;
}
} catch (Exception e)
{
System.out.println(e.printStackTrace());
}
finally
{
if ( client != null)
client.close();
if ( server!= null)
server.close();
}
}
}