I am creating a chat client. I have multiple clients connecting but I am having a hard time getting them to talk to each other. How do I get the server to send the message incoming from client1 to the rest of the clients without client1 having an echo? I haven't discovered any way to identify each client.
public class connect1 extends Thread {
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4444);
while (acceptMore) {
Socket send1socket = serverSocket.accept();
new Thread(new sendRunnable(send1socket)).start();
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
} //this thread starts my Runnable where I have my PrintWriter
The simplest way to do it is have an Array or list of Sockets, and add to it every time a new client connects/subtract when client leaves.
Then when a message is received, loop through that array/list and send message to other clients. (make sure to check the client you are on against the current client in list so you dont send message to the client that sent it to server)
While this is fine on a small scale, it will cause problem on a large scale as you have to loop through a large list each time a new message is received.
to have chatRooms or something, you can have a multidimensional array (Array[room][socket])
then you can do loop through certain rooms and only send to those clients, or loop through all, etc.
To send to specific users only, you can have a User.java class that has a username and a socket in it. then, instead of doing an array of sockets, use an array of users and in your loop, check username and only send to the required users.
Flow:
UserA connects to server, sends username to be bob
server recieves connections, makes new User object, sets username to bob and sets socket to users socket, then adds that User object to User[]
UserB connects to server, sends username john
same deal, server makes new user object etc etc.
john sends a message to server and tells it to only send the message to bob
Server loops through User[] and checks username of them, if username matches bob server calls getSocket on that user object, this returns the socket connection for that user.
server then gets the outputStream of that socket, and creates a printwriter with it. the it sends the message received from john through the newly create printwriter.
If your question is how to send/receive messages over a socket, the following is one way to do it.
To send:
String message = "..." // message to send
output = new DataOutputStream(socket.getOutputStream());
output.writeInt(message.getBytes().length); // first you send how long the message is
output.write(message.getBytes(), 0, message.getBytes().length); // then you send the message
And to read:
DataInputStream input = new DataInputStream(socket.getInputStream());
if(input.available() > 0) { // important - makes sure the code doesn't "block" and wait for input
byte[] bytes = new byte[input.readInt()]; // makes a byte[] of the size of the message it's going to read
input.read(bytes,0,bytes.length); // reads the actual message to the byte array
String message = new String(bytes,0,bytes.length,"UTF-8"); // convert the message back into a String
}
You should see Socket#getOutputStream() and Socket#getInputStream(), and then use methods in the DataInputStream and DataOutputStream class. input.ava
As for your other question, if you want a client to be able to send messages to specific other client(s), the client must know exactly which client(s) it wants to send the message to. When the client sends a message to the server, that information has to be part of the message. The server would use that to selectively send the message to specific Sockets.
There would have to be a system in place of course. For example, for a chat app each client might have a username. You could have a global class HashSet<User> variable - where a User has a Socket and a String (username). The client's message to the server could then be a (series of) username(s) followed by the actual message. The server would then iterate through the HashSet<User> and send the actual message only to Users with matching usernames.
Source: I'm making a chat app right now as well, and this is how I'm doing it. That's not to say there aren't other ways, or even better ways.
Related
I'm a novice in Java socket communication, I'm trying to connect 3 clients to a server without the use of threads as the number of clients connected to the server will as be 3. I have written a condition in the server to tokenize the received input from the client and send write to the respective stream. But the message isn't reaching the server so I'm not sure whether the client isn't writing to the stream or the server is unable to receive the data. I'm trying to establish a cryptographic protocol between the connected clients for demonstration purposes.
I was using BufferedReader and PrintStream earlier since I was testing with keyboard input, then I changed to data streams still doesn't seem to work
Here my code at Server.java to forward messages, the control doesn't appear to come to this loop at all(not sure).
while(true){
String recvd=cin2.readLine();
System.out.println("At server: "+recvd);
StringTokenizer st = new StringTokenizer(recvd);
String MsgToSend=st.nextToken();
String recipient=st.nextToken();
if(recipient.equalsIgnoreCase("client1")){
cout2.writeUTF(MsgToSend);
}
else if(recipient.equalsIgnoreCase("client2")){
cout.writeUTF(MsgToSend);
}
else if(recipient.equalsIgnoreCase("client3")){
cout3.writeUTF(MsgToSend);
}
}
Here is my client-side code,
while(i==0){
String s1="A";
String s2="B";
String s3="client1";
String toSend=String.join(" ",s1+s2,s3);
System.out.println("toSend :"+toSend);
sout.writeUTF(toSend);
sout.flush();
i++;
}
Receiving Client,
while (true){
s=sin.readLine();
System.out.print("Server : "+s+"\n");
}
Not sure whether the client is unable to write or the server is unable to read. Any suggestions or solutions to correct the code?
The socket navigation logic in your server file seems to be strange as you are manually creating a string with keyword client1 and then tokenizing it for navigation. By this approach your flow would get static and establishment of a protocol gets difficult.
Please see:
You are sending the data to client 1 by forming the string AB client1, then your server file's first line is using client 2 socket reading. (Sending data from client1 socket to client1 socket, not right).
Please change that to client1 socket reading, use prinln() and readline() to send and read socket data and then check if your flow is fine!
Also, for the protocol flow, you have to send the data to the other two sockets and not to the one that is sending.
For eg: if you are sending the data from client 1 to client 2, as per your approach, you have to for a string AB client2 and send to the server, tokenize it and navigate the flow to client 2.
Hope this helps!
Thanks,
Areed
I am one server and multiple clients via threads. The client(s) send their message to the server. I have worked out how to make the server send the message back to client like a echo system. If I have two clients, I want them to send their message to the server and the server should send it to the client that did not send the message i.e. the other client. How would I go about send the message back to all the clients apart from the one that send the message?
When the message comes in, determine what the userID / other identifying id the incoming message is associated with. Then re-broadcast to all other sockets, but exclude the Socket associated with the ID that sent the message
Make at the server side a list with all the clients...
every time a new msg is received, then iterate the list and send the msg using the port of the socket as id...
I recently wrote a Chat program too. What I did was, I had a class ClientHandler that handles the connection for each individual client.
Inside ClientHandler I had a HashMap. I added each client that had connected to the HashMap, with the Key being the client id. I used a UUID rather than int for the client id.
Inside this handler class, I had a sendMessage(String str) method. Within this method, a for-each loop that loops through each ClientHandler object, checking the values inside the HashMap. Inside this for-each loop, I have an if statement that checks whether you are writing to the ClientHandler object with this id. If the check returns false, you go ahead and write the message on the PrintWriter and the message won't be sent to the client writing the message.
This worked for me. Might not work for you.
I have written a program (java sockets) where a multi-threaded server can connect to multiple clients. In order to communicate with a single client, the server program has one DataInputStream and one DataOutputStream for each client. I want to broadcast a message from the server to the clients one by one. Each client can respond back differently. Depending on the response, the server takes it next action. In fact I am trying to find which client contains a certain file. To broadcast the message to all the clients I add the DataOutputStream for each client into a HashSet.
private static HashSet<DataOutputStream> comChannels = new HashSet<DataOutputStream>();
I can iterate through the items in the HashSet to broadcast any message to the clients.
I then broadcast the fileName of the file I am looking for from the server. When a client finds that file in one if its directory it sends a positive response back. If a client does not have that file it sends back a negative response.
The problem I am facing is I cannot get a reply from the clients. When I create a client thread, I create a corresponding DataInputStream for the client as well. I tried to create a HashSet of ArrayList where each ArrayList contains the DataOutputStream and DataInputStream for one single client.
HashSet<ArrayList<Object>> comChannels = new HashSet<ArrayList<Object>>();
I then create an ArrayList containing the input and output stream of a client every time a new client thread is created which I then add to the HashSet of ArrayLists. I thought i can iterate through the HashSet which would return an ArrayList in each iteration. I can then select the 1st object of the list and that would be my outputstream and the 2nd object would be my inputstream. But this does not work.
for (ArrayList<Object> client : comChannels) {
//select outputstream from the Arraylist and write message to client
//select inputstream from the Arraylist and read response from client
}
How can I broadcast and get replies from clients in a way where I can identify which response came from which client? Please note that I want to send the message to one client at a time and terminate the program as soon as a positive response comes back. This saves me from sending the message to all the clients unnecessarily. So creating separate HashSets of DataInputStreams and DataOutputStreams and then writing to all the clients and reading in all the different responses is not feasible.
A pair of streams representing a client is a domain concept in your application and should probably be captured in a meaningful class. Something like this at a bare minimum:
public class Client {
public DataOutputStream outStream;
public DataInputStream inStream;
public Client(DataOutputStream out, DataInputStream in) {
this.outStream = out;
this.inStream = in;
}
}
Whenever you create your streams for a client stick them in an instance of this Client class. Then you can put the Client objects in a List and iterate all you want.
for (Client client : clientList) {
// do whatever you want with client.outStream and client.inStream
}
Now you end up with a List<Client>, which is much easier for a person to grok than a HashSet<ArrayList<Object>>.
I'm using this program to test a PULL socket with ROUTER. I create/bind a ROUTER, then connect a PULL socket with an identity to it; the ROUTER then sends a message addressed specifically for the client using its identity (basic zeromq enveloping)
Test Program
public static void main(String[] o){
ZContext routerCtx = new ZContext();
Socket rtr = routerCtx.createSocket( ZMQ.ROUTER);
rtr.setRouterMandatory(true);
rtr.bind("tcp://*:5500");
ZContext clientCtx = new ZContext();
Socket client1 = clientCtx.createSocket( ZMQ.PULL);
client1.setIdentity("client1".getBytes());
client1.connect("tcp://localhost:5500");
try{
//Thread.currentThread().sleep(2000);
rtr.sendMore("client1");
rtr.sendMore("");
rtr.send("Hello!");
System.out.println( client1.recvStr());
System.out.println("Client Received: " + client1.recvStr());
}catch(Exception e1){
System.out.println( "Could not send to client1: " + e1.getMessage());
}
routerCtx.destroy();
clientCtx.destroy();
}
Results
The expected result is to print Client Received: Hello!", but instead the ROUTER throws an exception consistent with unaddressable message; I'm using setRouterMandatory(true) to throw that exception under such circumstances, however, the client explicitly sets an identity and the server sends to that identity, so I don't understand why the exception is raised.
Temporary Fix
If I add a slight delay by uncommenting Thread.currentThread().sleep(2000);, the message is delivered successfully, but I despise using sleeps and waits, it creates messy and brittle code, but more importantly, doesn't answer the "why?"
Questions
Why is this happening? It was my understanding that "late joining" applied only to PUB/SUB sockets.
Is PULL with ROUTER an invalid socket combination? I'm using it for a chat program, and aside from this issue, it works great.
Why is this happening?
You have a race condition. The client1.connect call starts the connection process, but there is no guarantee the actual connection is established when you call rtr.sendMore("client1");. Your sleep() workaround pretty much proves this.
Changing PULL to DEALER is a step in the right direction, because DEALER can send and receive. In order to avoid the need for sleeps and waits you would have to change your protocol. A simple change to the code above would be to have the DEALER connect and then immediately send a "HELLO" message to the ROUTER (could be just an empty message). The router code must be redesigned such that it does nothing until it receives a HELLO message from the DEALER. Once you have received the HELLO message you know the connection is successfully established and you can safely send your chat messages.
Also, this protocol eliminates the need for your router to know the client id in advance. Instead you can extract it from the HELLO message. A message from a DEALER to a ROUTER is guaranteed to be a multi-part message and the first part is the client ID.
I am currently working on a one server many clients system. I am trying to get it so the server can send out one command, through a PrintWriter, that will go through to all of the clients connected on that socket. However in practice the command only goes through to one client. All of the clients are created on one socket, and all use the same Scanner. Is what I am trying to do possible?
Some code(incase it helps)
Creation of the socket:
serverSocketRefresh = new ServerSocket(PORTREFRESH);
refresh = serverSocketRefresh.accept();
Creation of the Print Writer and the Scanner:
networkOutputRefresh = new PrintWriter(refresh.getOutputStream(), true);
networkInput = new Scanner(refresh.getInputStream());
Ceation of the clients:
do
{
// Wait for client...
client = serverSocket.accept();
System.out.println("\nNew client accepted.\n");
handler = new ClientHandler(client,networkOutputRefresh, itemArray, bidderArray);
handler.start();
} while (true);
The command im trying to transmit to all of the clients:
public static void updatePrice()
{
networkOutputRefresh.println("1");
}
I am not sure if I correctly understand your code but it seems you are using a single reference of client. And your client reference will be holding the last client reference and hence the printwrite is writing only for that client. Ideally if you want to publish something to all the clients then you should have a collection of client references.Whenever you get an accept on the server socket, add the new client reference to your collection. And whenever you have to publish to all the clients just iterate over your client collection and publish using their associated printwriters.
Why not just use a BufferedReader and BufferedWriter, and make a new one each time you accept a client?
Edit: Or, if that '1' is the only thing you will ever send over that socket, just send it over the socket directly, as a byte. I believe the method is something like socket.write(new byte[] { 1 }, 0, 1), and to read on the other end, socket.read(buffer, 0, 1), where buffer is a byte array of length 1.