I'm trying to develop a java chat server using thread pool, but i don't know how to handle incoming message from clients. i've think to save every socket connection in a hashmap and add the task to the queue of thread pool.. but how the server can know when he's receveing a message from a client without instantiate a bufferedreader?
You dont need to initialize a buffered reader for each one of your sockets. You can look through and check if there is data waiting to be read.
for(Socket socket : socketsList)
if(socket.getInputStream().available() > 0) {
// you have data to be read from this socket
}
Your server will need to use agent objects that hold a BufferedReader that reads from their socket. Perhaps you will need to create a collection of these agent objects.
For example,
class ServerAgent implements Runnable {
private OutputStream out;
private BufferedReader br;
public ServerAgent(Socket clientSocket) throws IOException {
out = clientSocket.getOutputStream();
br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// ....
}
#Override
public void run() {
// TODO finish code that reads from br, BufferedReader
}
}
And your Server could have code like:
while (true) {
Socket clientSocket = server.accept();
futureList.add(threadPoolExector.submit(new ServerAgent(clientSocket)));
}
Related
I've written a basic TCP chat in Java and when testing locally (i.e. localhost) it's been working fine. I can connect, type messages to myself and receive without issues. I can connect and disconnect several times a second.
However, when trying to connect through my router's external IP for whatever reason it hangs when trying to initialize the ObjectInputStream on not only the Client side, but also Server side. So I'm guessing it's something to do with my Router firewall, on which I added a firewall rule. I did only create a firewall rule for the specific port 1777 where I make my connection, is it possible that the data sent when flushing ObjectOutputStream is sent on another port? Which wouldn't make sense I guess considering the socket is bound to a specific port.
I am flushing after initializing the ObjectOutputStream and I am creating that before creating the ObjectInputStream, on both sides. What doesn't make sense to me is that it allows for a connection to be made.
I guess code may be irrelevant in this case, but here it is anyway:
This Thread is run at all times, and waits for connections then starts a new Thread for handling the connecting client:
public void run() {
while (connected) {
try (ServerSocket serverSocket = new ServerSocket(port)) {
Socket socket = serverSocket.accept();
new ClientHandler(socket);
} catch (IOException e) {
...
}
}
}
Constructor of ClientHandler class:
public ClientHandler(Socket socket) throws IOException {
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.flush();
inputStream = new ObjectInputStream(socket.getInputStream());
welcomeClient();
start(); // Start listening for messages.
}
This is where the ClientHandler Thread hangs.
Here is Client side code:
// Get IP, port and stuff
socket = new Socket(ip, port);
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.flush();
inputStream = new ObjectInputStream(socket.getInputStream());
// Write a couple objects and read some objects.
They both hang on "inputStream = new ObjectInputStream(socket.getInputStream());"
What could be the problem? I can also connect through my local IP, which makes me think it's still an outbound->inbound firewall issue for whatever reason.
I am trying to make a chat server (as a smaller part of a game I'm coding) that accepts two clients. The way I have done this so far is by creating two completely different sets of a Socket, ObjectInputStream, and ObjectOutput stream.
private JTextField userText;
private JTextArea chatWindow;
private ObjectOutputStream output;
private ObjectInputStream input;
private ObjectOutputStream output2;
private ObjectInputStream input2;
private ServerSocket server;
private Socket connection;
private Socket connection2;
static final int PORT = 6789;
The outputting is working just fine, but the input method is not working as planned. This is the input method:
//runs while conversation is active
private void whileChatting() throws IOException{
waitForConnection();
setupStreams();
String message = " You are now connected! ";
sendMessage(message);
ableToType(true);
do{
message = input.readUTF();
showMessage("\n" + message);
message = input2.readUTF();
showMessage("\n" + message);
}while(!message.contains("END"));
}
The line message = input.readUTF(); is waiting for there to be something to read. Is there a way to check if there is something to read and only set message equal to it if not null? No, if(input.readUTF() != null) does not work. Alternatively, I think there would be a way to do this with multiple threads, but I do not have a good grasp on how threads work, so if someone could give me an example, that would be very helpful.
Thanks.
readUTF() is considered a blocking call, which means when it is called, it will hold up the thread until it returns a value. Even if you were to nullcheck, it will still block your thread from continuing until something came through the stream (whether it be a string or a null).
To handle something like a multithreaded connection:
Create a class that implements Runnable
class User implements Runnable {
}
In that class, add you in\out stream. Make sure they're aren't static so they're instance variables. You are gonna want a new in/out stream for each connection
class User implements Runnable {
DataOutputStream out;
DataInputStream in;
Socket socket;
public User(Socket s) {
socket = s;
}
public void run() {
try {
out = new DataOutputStream(socket.getOutputStream());
//same with inputstream
String input;
while(!(input = in.readUTF()).equals("END")) {
//do something with input
}
}
}
}
When your server accepts a connection...
public static void main(String[] args) {
ExecutorService executors = Executors.newCachedThreadPool(); //contains your threads
ServerSocket ss;
while(true)
executor.execute(new User(ss.accept()));
}
ServerSocket.accept() is also a blocking call, which means the loop it's being called in will block (wait) until a user is accepted. This is called Blocking-IO.
All read calls from java.io are blocking.
DataInputStream.readUTF
DataInputStream.readInt
ObjectInputStream.readObject
If you want a system where methods such as these don't block, I suggest looking into the java.nio package (new IO). It far more advanced in my opinion, especially for someone who doesn't have a grasp on basic networking yet, but non-blocking IO's allow for your underlying OS to inform your application when to read/write (through a selector), thus removing the need for blocking calls.
Other than that, there is no way you can prevent readUTF to stop blocking, or somehow skip it without data coming through it.
i have this TCP socket (i only posted relevant parts and removed exception throwings):
static Socket clientSocket;
static BufferedReader inFromServer;
The connection part (i call it from another class):
static Socket clientSocket = new Socket(ip, port);
static BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
And to recieve text i have a runnable thread wich i call like this:
public static void StartRecievingText(){
TCPScanner.setReader(inFromServer);
Thread t1 = new Thread(new TCPScanner());
t1.start();
}
The thread:
public class TCPScanner implements Runnable {
static BufferedReader inFromServer;
public static void setReader(BufferedReader reader){
inFromServer = reader;
}
public void run() {
while (true) {
String temp = inFromServer.readLine();
System.out.println(temp);
}
}
}
The thread runs to the inFromServer.readline() part and appears to recieve nothing.
It's my first time working with threads and ...well second time working with tcp connections so i don't know if i've done anything wrong.
Thanks for your help (and sorry for spelling mistakes... still learning english)
Your client is reading lines but you aren't sending lines, so the client blocks forever waiting for a line terminator that never arrives. Either add a newline to what is being sent, or use another read method that doesn't require it.
There are other problems with your code. None of these data items should be static. Your read loop should test the result of readLine() for null, and close the socket and exit if true.
I'm having a bit of trouble, now I have looked at this tutorial
http://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html
This tutorial gives you a server that multiple clients can connect to, when they connect to the server they are told to go along with a knock knock job, now I understand how to transfer the data and what not, but how does the threads work?
I'm working on a networked pong game where a server will hold the positions and pass them to the clients, now I have a client connected to the server and the ball position is passed to the client, works fine, a bit jumpy but I'm sure a thread with .sleep will help. but anyways my question is, how can i get my client to become a thread? and how can I store them?
For example here is the knock knock server multiThread class
package knockKnockServer;
import java.net.*;
import java.io.*;
public class KKMultiServerThread extends Thread {
private Socket socket = null;
public KKMultiServerThread(Socket socket) {
super("KKMultiServerThread");
this.socket = socket;
}
public void run() {
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String inputLine, outputLine;
KnockKnockProtocol kkp = new KnockKnockProtocol();
outputLine = kkp.processInput(null);
out.println(outputLine);
while ((inputLine = in.readLine()) != null) {
outputLine = kkp.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye"))
break;
}
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And here in the server we have
package knockKnockServer;
import java.net.*;
import java.io.*;
public class MultiKKServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(-1);
}
while (listening)
new KKMultiServerThread(serverSocket.accept()).start();
serverSocket.close();
}
}
Now looking at the server it will create a new KKMultiServerThread on each connection, but how can i store them? can i make a array of KKMultiServerThread?
I tried to make an array of KKMultiServerThread
and when i try this line
multi[0] = new KKMultiServerThread(serverSocket.accept()).start();
I get this error "cannot convert void to Thread"
If anyone can shine some light on my problem it would be great.
Canvas
Update
I now have my own thread class
package Pong;
import java.net.*;
import java.io.*;
public class PongPlayerThread extends Thread
{
private Socket socket = null;
private String pongData = "";
public PongPlayerThread(Socket socket, int id)
{
super("PongPlayerThread");
this.socket = socket;
}
public void passData(String data)
{
pongData = data;
}
public void run()
{
try
{
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
while(true)
{
out.println(pongData);
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
the pongData is a string that holds all the information together in a string, now if i declare a player1 at the top of my pong server like so
private static PongPlayerThread player1;
and do this line when it is listening
while(listen)
{
PongPlayerThread player1 = new PongPlayerThread(serverSocket.accept(), 0).start();
}
it gives me this error "cannot convert from void to PongPlayerThread" how do i fix this?
Your array declaration is missing the object type
KKMultiServerThread multi[0] = new KKMultiServerThread(serverSocket.accept()).start();
Why bother though? Unless the threads needs to communicate with each other, letting the threads run freely is ok. The Run() method defines the entire lifetime of the socket as far as the server is concerned. Each thread has a separate copy of the state of the game (as long as you don't use statics) and will happily communicate with the client without any extra intervention.
This is a case where the Socket/Thread library in Java is doing you a big favor, don't make it more complicated unless you have a specific need.
When ever a client connects to the server. The server will typically create a new thread specifically for that client. Here is some pseudo code:
WHILE SERVER IS RUNNING
SERVER WAITS FOR A CLIENT TO CONNECT
SERVER ACCEPTS THE CLIENT IF THERE IS ENOUGH MEMORY TO CREATE A NEW THREAD
SERVER CREATES A NEW THREAD ROUTINE FOR THE CLIENT PASSING THE CLIENT INFORMATION TO THE THREAD
SERVER CONTINUES TO LISTEN WHILE EACH THREAD IS SPECIFICALLY TAILORED FOR THE CLIENTS
REPEAT
You asked what steps are needed to reduce lag? Well for starters, set a maximum allowed connections. You do not want 5000 clients having their own thread. Unless your machine can handle all that and still run. Use UDP instead of TCP, and data compression try to minimize bandwidth don't send 50 GB of information at a time; if all you need is a couple of bytes of information to send. Try to send information of positions not as strings but in bytes. For example you can send the position X=5Y=0 as 50 and parse the first decimal digit as X and the second decimal digit as Y.
Instead of passing the client socket inside the thread routine pass a unique identifier for the client. Since Pong is two players limit the connections to two clients. 0 for Player 1 and 1 for Player 2. So
new KKMultiServerThread(clientID).start(); // clientID is of type int
Edit:
int id = 0;
while(serverIsRunning)
{
Client client = server.accept();
if (id > 2) client.Close(); // Do not accept.
Thread.New(id).Start();
id++;
}
The Application
I'm writing a client/server application in Java, that communicates by sending objects over sockets using the ObjectStream classes. Each node in the application looks approximately like this:
class Node {
SocketServer server;
Socket[] clients;
}
Here the server variable is the socket on which this node listens, and the client variables are the sockets on which other nodes listen, and to which this node sends objects.
The code that I use to write objects to one of the client sockets looks like this:
void sendMessage(Message<B, F> msg) throws IOException {
ObjectOutputStream writer = getWriter();
writer.writeObject(msg);
writer.flush();
}
private ObjectOutputStream writer;
ObjectOutputStream getWriter() throws IOException {
if (writer == null)
writer = new ObjectOutputStream(
new BufferedOutputStream(client.getOutputStream()));
return writer;
}
And the code that I use to handle connections and read objects from the node's server socket looks like this:
// the handler will listen for connections
final Thread handler = new Thread(new Runnable() {
public void run() {
try {
// create a new thread to handle the client
final Socket client = server.accept();
final Thread thread = new Thread(new Runnable() {
public void run() {
final ObjectInputStream reader;
try {
reader = new ObjectInputStream(client.getInputStream());
while (true) {
try {
val msg = reader.readObject();
messages.add((Message<B, F>) msg);
}
catch (EOFException e) {
// i noted it seemed to throw eofexceptions
}
catch (IOException e) {
// do something
}
}
}
catch (IOException e) {
// do something
}
}
});
thread.start();
} catch (IOException e) {
// do something
}
}
});
handler.start();
The Problem
I think I'm doing something wrong with the sockets here. Everything works fine when every server is only connected to a single client. However, when multiple clients are talking to the same server things go bad, and I get StreamCorruptedException's from the ObjectInputStream and other strange behaviour (putting in an instance of an UpdateRequest message, and getting out an instance of Integer(0) and some exceptions, for example.)
My intuition tells me that somehow the two object/byte streams are getting intermingled, and this produces the strange results when attempting to deserialize the objects. My question is: why is this happening aka what am I doing wrong, and how could I fix it?
You have an array of Sockets but you don't appear to have an array of writers and readers. So you're probably using the same writer and reader for all connections.
Really you should have a per-connection Connection object, that implements Runnable, and that has the Socket, the writer, and the reader as instance members.
Also when you catch EOFException you must break out of the loop and close the writer.
The problem was occurring due to messages being sent simultaneously, and this resulted in the bytes being mixed. The solution was to make sure that messages would only be received one at a time.