The once the client disconnects and I restart the client, the server gives read line timeout. and when I run the server again, it works fine. So after disconnect one time I get read line timeout exception and next time it works.
import java.io.*;
public class TcpServer {
ServerSocket welcomeSocket;
public TcpServer() throws IOException{
createSocket(port);
}
public TcpServer(int port) throws IOException{
createSocket(port);
}
private void createSocket(int port) throws IOException{
welcomeSocket = new ServerSocket(port);
}
#Override
public void listen() throws IOException{
boolean exitServer = false;
Socket connectionSocket = null;
try {
while (!exitServer ) {
if(...){
exitServer = true;
}
}
}
} catch (IOException e) {
try {
welcomeSocket.accept();
listen();
} catch (IOException e1) {
System.out.println("Cannot open connection!!!");
}
}
}
}
The ServerSocket.accept() method blocks and returns a new client socket connection when someone tries to connect. Put it in a while loop and then spawn a thread for this new socket worker. Something similar to this:
ServerSocket welcomeSocket = new ServerSocket(port);
while (true) {
Socket socket = welcomeSocket.accept();
new Thread(new RunnableSocketWorker(socket));
}
If your client does decide to disconnect, that's fine, let them. You want the socket worker that was working on it to exit. If a new client tries to connect, they will do so above with your ServerSocket object and this infinite loop.
A big reason sockets are relatively easy in Java is that this ServerSocket class handles all incoming new clients. Why would you want to code that part yourself?
Just take the socket it returns and have fun!
Related
Relevant code:
#Test
public void serverTest() throws IOException {
GameServer server = new GameServer();
server.start(9000);
GameClient client1 = new GameClient();
GameClient client2 = new GameClient();
client1.startConnection("localhost", 9000);
client2.startConnection("localhost", 9000);
client1.sendMessage("Hey I am client 1");
client2.sendMessage("Hey I am client 2");
}
public class GameServer {
private ServerSocket serverSocket;
public void start(int port) throws IOException {
System.out.println("Server started !!!");
serverSocket = new ServerSocket(port);
while (true) {
new Thread(new GameClientHandler(serverSocket.accept())).start();
}
}
public void stop() throws IOException {
serverSocket.close();
}
private static class GameClientHandler implements Runnable {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public GameClientHandler(Socket socket) {
this.clientSocket = socket;
}
#Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inputLine = in.readLine();
while ((inputLine = in.readLine()) != null){
System.out.print(inputLine);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
in.close();
out.close();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Why can't the server and client be started together in the #Test? I think it gets stuck in the infinite while loop but at the same time, shouldn't there be context switching with the new threads started after accepting the connection?
I expected at least the name of the 2 new threads to be printed but it doesn't happen.
Let us look carefully to your test code:
GameServer server = new GameServer();
Ok, this lines creates a server, and the test thread is ready to execute next line
server.start(9000);
Ok, the test thread starts the server, and will be ready to execute the next line when the start method will return.
What happens in start:
System.out.println("Server started !!!");
Ok, you should see that message
serverSocket = new ServerSocket(port);
Ok, you have created a ServerSocket
while (true) {
new Thread(new GameClientHandler(serverSocket.accept())).start();
}
ok you a waiting for a connection (at serverSocket.accept()), will create a new thread to handle it as soon as you will get one, and loop again.
But as this point, the test thread is waiting and will never go to the following line to start the first connection. And it will remain stuck unless something else (maybe another thread) starts those damned connections.
The method GameServer.start will only return with an exception. That is because you have the while-loop.
So your test execution will start the server and wait for someone to open a connection, but that never happens.
If I have a server and a client and I opened a socket between the two:
1.Is it possible that the client will have a printWriter stream, in order to write things to the socket, but the server won't have in the mean time a bufferReader?
If the answer of 1 is yes, if that client will send a message to the server (who currently doesn't have a reading stream), what will happend to this message until te server will create a reading stream and read the message?
thank you
This is not at all specific to Java, but TCP/IP. There are buffers to keep the data received, so it's not possible that some data would be lost because one end isn't "ready" yet. This is because TCP will retransmit data that hasn't been acknowledged as received, guaranteeing that all the bytes that are written are received on the other (barring obvious cases).
in addition to #Kayaman's answer:
consider this Compile-able simple Java implemented example:
Server Side:
import java.io.*;
import java.net.*;
public class SimpleServer implements Runnable{
int serverPort = 45000;
ServerSocket serverSocket = null;
boolean isStopped = false;
public SimpleServer(int port){
this.serverPort = port;
}
public void run(){
try {
serverSocket = new ServerSocket(serverPort);
} catch (IOException e) {
System.err.println("Cannot listen on this port.\n" + e.getMessage());
System.exit(1);
}
while(!isStopped){
try {
Socket clientSocket = serverSocket.accept();
} catch (IOException e) {
// do nothing
}
}
}
public static void main(String[] args) throws IOException {
SimpleServer server = new SimpleServer(45000);
new Thread(server).start();
System.out.println("Server is waiting to connect");
}
}
Client Side:
import java.io.*;
import java.net.*;
public class SimpleClient {
public static void main(String[] args) throws IOException {
Socket socket = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", 45000);
out = new PrintWriter(socket.getOutputStream(), true);
System.out.println("output stream created");
out.write(9);
System.out.println("message was sent to output with no listener");
} catch (UnknownHostException e) {
// do nothing
} catch (IOException e) {
// do nothing
}
}
}
the example is an implementation of a very basic client server connection in which a socket is created and a stream is defined only on the client side, followed by a write to the stream that will eventually be read by the server (if at all).
therefore, to answer you questions:
1) yes, it's possible to open a one-way connection stream without a "listener"
2) edit: according to #EJP: It will be saved within the socket's buffer until it is read or the socket is closed.
I have a TcpServer class that is responsible to, well, act like a tcp server. You can find the class below :
public class TcpServer {
private ServerSocket serverSocket;
private Socket socket;
private int locallyBoundPort;
public TcpServer() {
}
public TcpServer(int locallyBoundPort) {
try {
this.serverSocket = new ServerSocket(locallyBoundPort);
serverSocket.setReuseAddress(true);
} catch (IOException e) {
System.out.println("Error at binding to port TCP : " + locallyBoundPort + "...cause : " + e.getMessage());
}
socket = null;
}
public void accept() {
try {
socket = serverSocket.accept();
socket.setReuseAddress(true);
} catch (IOException e) {
System.out.println("Error at accept : " + locallyBoundPort);
}
}
public void send(Data data) throws IOException {
if(socket != null) {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(data);
}
}
public Data receive() throws ClassNotFoundException, IOException {
if(socket != null) {
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
return (Data) in.readObject();
} else {
return null;
}
}
public boolean bind(int port) throws IOException {
try {
this.serverSocket = new ServerSocket(port);
this.locallyBoundPort = port;
} catch(IOException e) {
return false;
}
return true;
}
public void close() {
try {
serverSocket.close();
socket.close();
} catch (IOException e) {
OzumUtils.print("IOException in close, TcpServer");
}
}
public int getLocallyBoundPort() {
return locallyBoundPort;
}
public Socket getSocket() {
return socket;
}
public ServerSocket getServerSocket() {
return serverSocket;
}
}
And I have a code piece that does this :
TcpServer tcpServer = new TcpServer(LocalPort);
while(1)
{
tcpServer.accept();
Thread thread = new Thread(new runnable(tcpServer));
thread.start();
tcpServer = new TcpServer(LocalPort);
}
However I am getting a port already in use error. I thought two different socket instances could listen to the same port as multiplexing allows two connections through the same port when the connector has different ip or port ?
What am I missing?
You cannot bind two tcp server sockets to the same port. reuseAddress is really for client sockets, and it does not work the way you think it does ... and the way you are using it would not do anything at all either way (because you are setting it after binding).
You don't really need to bind twice to the same port either. Just remove this line tcpServer = new TcpServer(LocalPort); from the bottom of your while loop, and you'll be all set.
The way this works is that you bind your server socket once and listen to the port. When a connection arrives, it forks a client socket for you to communicate with the client, and the original server socket continues to listen for more connections.
Basically, you need to remove the socket member (and any other state) from your TcpServer, and make the accept method return the accepted socket. Then make your runnable take that socket as a parameter instead of the TcpServer, and use that to serve the client connection. Then just keep calling accept in the loop, and forking threads for new connections same way you do know, except do not recreate the server every time.
Or, alternatively, remove the server socket and port from TcpServer, create the socket outside the loop, then while(true) call accept on it, create a new TcpServer with the returned client socket, and use it in a thread to process the connection.
Do not forget to close client sockets after you are done with them.
No, you can't use a port already in listening state. However any number of clients can connect to this same port. You don't need to listen to the port again, you just spawn a new thread to process the current connection and wait for a new one. For example, supposing you have a class TcpConnectionHanlder that implements Runnable and takes the Socket as parameter, the loop would look like
while (true) { //while(1) is not valid Java syntax
final Socket tcpSocket = tcpServer.accept(); // Get socket for incoming connection
final Thread thread = new Thread(new TcpConnectionHanlder(tcpSocket)); // Create a thread for this socket/client connection
thread.start(); // Launch the thread
// tcpServer = new TcpServer(LocalPort); <- not needed, port still listening.
}
Then in your TcpConnectionHanlder instance you handle this particular client (socket).
When a user connects to my server, I'm accepting the connection, storing the connected clients details in an arraylist then im trying to send that list to the client to create an asynchronous list. However, when I try to send the list it breaks the connection and causes the error;
java.net.SocketException: Socket is closed
My server class;
while (true) {
Socket clientSocket = serverSocket.accept();
ServerClientComs clientThread = new ServerClientComs(clientSocket);
getUsername(clientSocket, clientThread);
updateList();
try {
clientThread.sendList(connections);
clientThread.initialiseCommunication();
clientThread.start();
}
catch (IOException error) {
System.out.println("Server: Unable to initialise client thread! - " + error);
}
}
ServerClientComs Class;
public ServerClientComs(Socket clientSocket) {
super();
this.client = client;
this.clientSocket = clientSocket;
}
public void initialiseCommunication() throws IOException {
output = new PrintWriter(this.clientSocket.getOutputStream(), true);
input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
}
public void sendList(ArrayList connections) throws IOException
{
oos = new ObjectOutputStream(this.clientSocket.getOutputStream());
oos.writeObject(connections);
oos.close();
}
Been playing about with this for a good hour, getting annoying now. any help is greatly appreciated!
A 'Socket closed' exception means that the application that caught the exception closed the socket and then kept trying to use it. Please note that closing the input or output stream of a socket closes the other stream and the socket as well. According to your statements seems you close the stream before using the socket to send data.
here is the server side code of my program, the problem is, its accepting one client. when another client is connected, the isConnected method returns true, but the server does not gets the messages from the server. please help me as this is my first java program in netbeans, i have just finished studying core java.
class Conn extends Thread{
ServerSocket ss;
Socket s;
public void run()
{
status.setText(status.getText()+"connecting");
try{
while(true)
{
s=new Socket();
ss=new ServerSocket(3000);
s=ss.accept();
Read r=new Read(s);
r.start();
}
}catch(Exception e){}
}
}
class Read extends Thread{
DataInputStream inp;
PrintStream outp;
String str;
Read(Socket s)
{
try{
inp=new DataInputStream(s.getInputStream());
outp=new PrintStream(s.getOutputStream());
}catch(Exception sd){}
}
public void run()
{
status.setText(status.getText()+"\nreading");
try{
while(true)
{
str=inp.readLine();
chatwin.append(str);
outp.println(str);
}
}catch(Exception er){}
}
}
Move the while logic after the assignment of ss.
try
{
ss = new ServerSocket(3000);
while (ss.isBound())
{
s=ss.accept();
Read r = new Read(s);
r.start();
}
}
Your problem is that you can't do this multiple times:
ss = new ServerSocket(3000);
You've already created a ServerSocket that sits at port 3000, so when you try to make another one, it'll try to bind itself to that socket, and not succeed because your first ss is still sitting there. You should only create one ServerSocket and grab socket connections off of that one ServerSocket as threads connect to it.
Does this answer your questions?
ss.accept() will block until it receives a connection. How are you connecting to it?