I just created an java chat application that allow a communication between many clients, however, i would like also to have the ability to one client send a message to a specific client, the others client cannot be able to see the message send, just like one client whispering to another one. Thank you in advance.
Here some part of my client
public void sendListener(){
writer.println(clientName2+" : "+broadcastTF.getText() );
writer.flush();
broadcastTF.setText("");
broadcastTF.requestFocus();
}
public class listenServer implements Runnable{
public void run(){
try{
String text;
while((text = reader.nextLine()) != null){
messageTA.append(text+ "\n");
}
}catch(Exception ex){}
}
}
private void setupServer(){
try{
socket = new Socket("127.0.0.1", 7894);
writer = new PrintWriter(socket.getOutputStream());
reader = new Scanner(socket.getInputStream());
listenServer ls = new listenServer();
Thread t = new Thread(ls);
t.start();
}
catch(Exception ex){
}
}
Here some part of my server
public class listenToClient implements Runnable{
Scanner reader;
public listenToClient(Socket socket){
try{
reader = new Scanner(socket.getInputStream());
}catch (Exception ex){}
}
public void run(){
try{
String text;
while((text = reader.nextLine()) != null){
sendToAll(text);
}
}catch(Exception ex){}
}
public void sendToAll(String text){
for (PrintWriter w : writers){
try{
w.println(text);
w.flush();
}catch(Exception ex){}
}
}
}
I think this is less related to specific code, and more related to overall design.
Primarily, you'd need some way of identifying individual clients. If you need fast lookups, you would use some sort of key/value map to store your writers, with the unique ID (user name, user ID, some random string, or whatever suits your situation) as the key. If that's not a concern, then you can store your client connections in a simple numerical array (or array-like structure) and iterate over until you find the target, then send to exclusively that connection.
Then, the sending client needs to be able to discern what its target is, and also have some way of sending the target information along with the message. Simply reading input at the server and echoing it out will not suffice for this - you'll have to do some amount of parsing, and you'll probably need to design some format for arranging that information.
As an example, one client-server communication format I designed had the first byte of a message be the length of the key in a key/value pair. The server would read the first byte, then read the next N bytes as the key. The remaining bytes were then known to be the value. In your case, a key length of 0 would mean it's not going to one specific destination, but to everyone.
Create a collection of clients such as hashmap. So whenever you get a new client connection accept, assign this client an id and put its socket reference against the id in the hashmap.
Clients should know each other ids to make sure that they can identify and send the messages to each other. So whenever a client sends a packet to server and it contains a recepient client id, server should lookup the hashmap. The recepient socket reference should be retrieved and message should be sent using the writer.
Hope it helps!
Related
I am learning about sockets in java, but when I was running a program that sends messages from the client side to server side it doesn't show a message. If I enter some text on the client side it doesn't show up on the server side, but if I type endProcess it stops running. Which means that the message is going through it's just not showing up.
My Client.java code is here:
import java.net.*;
import java.io.*;
public class Client{
Socket soc;
DataInputStream dis;
DataOutputStream dos;
public Client(){
try{
soc = new Socket("(Address)",5000);
System.out.println("Connection Established");
dis = new DataInputStream(System.in);
dos = new DataOutputStream(soc.getOutputStream());
System.out.println("Streams connected");
}catch(UnknownHostException u){
System.out.println(u);
}catch(IOException i){
System.out.println(i);
}
String line = "";
while(!line.equals("endConnection")){
try{
line = dis.readUTF();
dos.writeUTF(line);
}catch(IOException i){
System.out.println(i);
}
}
try {
soc.close();
dis.close();
dos.close();
} catch (Exception e) {
System.out.println(e)
}
}
public static void main(String[] args) {
new Client();
}
}
Here is my Server.java code:
import java.net.*;
import java.io.*;
public class Server {
ServerSocket serSoc;
Socket soc;
DataInputStream dis;
public Server(){
try {
serSoc = new ServerSocket(5000);
System.out.println("Server Online");
soc = serSoc.accept();
System.out.println("Client Connected");
dis = new DataInputStream(new BufferedInputStream(soc.getInputStream()));
String line = "";
System.out.println("Waiting for input...");
while(!line.equals("endConnection")){
line = dis.readUTF();
System.out.println(line);
}
System.out.println("Client disconnected");
soc.close();
dis.close();
} catch (Exception e) {
System.out.println(e);
}
}
public static void main(String[] args) {
new Server();
}
}
There are many problems here.
Duplex protocol issues
line = dis.readUTF();
dos.writeUTF(line);
This isn't going to work; The dis.readUTF() line is going to block (freeze) until a line is read. The problem is, sometimes you have nothing to send in which case you want to read, and something you have nothing to read in which case you want to send. In practice you need to redesign this entirely; you need 2 threads. At which point you get into the issues of multicore, needing synchronization primitives and/or java.util.concurrent classes for all data that is shared between the 2 threads.
Alternatively, adopt a model that is strictly push or pull (where at any given time both parties already know who can send, and if the other party wants to send they simply cannot. For example, every party sends a simply 'NOTHING TO DO' message every second, trading places every time. This is quite an inefficient algorithm, of course. But could be written without involving multiple threads.
Flush and close issues
dos.writeUTF(line);
This doesn't actually send anything, or at least, isn't guaranteed to. To send any data on the internet, it gets wrapped in a packet which has lots of overhead. So, things are buffered until there's a full packet to send. Which means that line doesn't do anything. It just fills a buffer, no packets go out. You first need to close or flush. dos.flush() would help maybe. This is a big problem, because later you do:
soc.close();
dis.close();
dos.close();
You first close the socket, which, well, closes the socket. You then close the streams, which will also send anything that's still stuck in a buffer, except, that will fail, because the socket is already closed. In other words, the line you .writeUTF()-ed? It never gets there. You first shove it in a buffer, then you close the socket, then you send the buffer which won't work as the socket is already closed.
Broken error handling
} catch (Exception e) {
System.out.println(e);
}
Horrible. Don't do this. Your code reacts to any problem by printing something and just keeping right on going. That means if anything goes wrong, the client will start spamming an endless cavalcade of exception traces and locking up the system with any luck. You want the code to stop running when problems occur. Easiest way, by far, is to just stick throws IOException on your constructor and main method, which is allowed. Distant second best option is to configure your 'eh whatever' catch blocks as throw new RuntimeException("unhandled", e); instead of e.printStackTrace().
What you do (System.out.println(e);) is even worse - you are tossing away extremely useful information such as the stack trace and causal chain.
My title is probably not the most descriptive, but I'm going to try and show as much code as possible in hope that it'll help everyone understand my question better. Here's how my client side of my project queries the server for information. This is an example of a typical request:
private String GENERATEGROUPKEY()
{
/* `out` is a PrintWriter using the sockets output stream */
out.println("GENERATEGROUPKEY");
try
{
/* `in` is a BufferedReader using the sockets input stream */
String response = in.readLine();
String[] temp = response.split(" ");
return temp[1];
}
catch (IOException ex)
{
return null; // throw connection error to client
}
}
My issue is that at any time, the server can send an unsolicited message to the client through that same socket with information (think about it like a chat client receiving a message). My unsuccessful idea was to create a thread that listens for such a message as long as we're not in the middle of another query, but that was also unsuccessful because even though I'm interrupting that thread, it's still hogging the messages that should've gone to the client query.
private String GENERATEGROUPKEY()
{
out.println("GENERATEGROUPKEY");
listenThread.interrupt(); // block listenThread from recieving response
try
{
String response = in.readLine();
String[] temp = response.split(" ");
listenThread = new PulseThread(in); // we're done, so allow
listenThread.start();
return temp[1];
}
catch (IOException ex)
{
listenThread = new PulseThread(in); // we're done, so allow
listenThread.start();
return null; // throw connection error to client
}
}
Here's exactly what listenThread is
public class PulseThread extends Thread
{
private BufferedReader in;
public PulseThread(BufferedReader in)
{
this.in = in;
}
#Override
public void run()
{
while (true)
{
if (Thread.currentThread().isInterrupted())
{
break;
}
try
{
String line = in.readLine();
System.out.println(line);
String[] params = line.split(" ");
if (params[0].equals("PULSED"))
{
NotificationManager.sendNotification("You have been pulsed!", "Pulsed by: " + params[1]);
}
}
catch (Exception ex)
{
}
}
}
}
I was previously under the impression that interrupting the thread in the middle of the BufferedReader's blocking call with readLine() would just cancel the blocking call unless I'm doing something else wrong.
Any help would be greatly appreciated, thanks.
EDIT: So looking into my assumption just a few lines above this sentence, it seems interrupting the thread doesn’t cancel readLine(). I guess the interrupting thread idea is a no-go. What’s the right way to do this?
The general pattern here is that you want one thread processing output from the socket (blocking while waiting) and then dispatching messages out to the correct things that requested them.
One implementation I like and have used successfully in multiple projects is to add a randomly generated ID to “requests” as part of a generic header (including a message type as well) and have the server always mirror the ID back in the response, which allows the client to associate a request with a response without caring what type of message it is.
In concrete terms, something like a SocketMessenger class with 2 public functions: sendRequest(type, body, callback) and registerUnsolicitedHandler(type, callback).
sendRequest builds the message header with type and a randomly generated ID, adds it to a list of pending replies alongside a reference to the callback function, and then sends the completed message to the server.
registerUnsolicitedHandler does what it’s name suggests and adds the callback function to a map of message types to be used when an incoming message has no ID.
In the separate thread handling incoming messages, it deserialises incoming data to get the type and ID from the header, if the message has an ID it searches the pending reply list and calls the appropriate callback with the message body (probably scheduled on the main thread, I’m glossing over some detail like locking), else it searches the unsolicited handler list for the specified type and calls that callback.
I am new to java server and client. I want to make a multi-client server. After making server code ready to accept more than one client it is like this :
Server code :
public class Server {
int clientNumber = 1;
ServerSocket mServer = new ServerSocket(9090);
try {
while (true) {
new TheServer(mServer.accept(), clientNumber++).start();
}
} finally {
mServer.close();
}
}
private static class TheServer extends Thread {
private Socket client;
private int clientNumber;
public TheServer(Socket client, int clientNumber) {
this.client = client;
this.clientNumber = clientNumber;
}
public void run() {
try {
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
dos.writeUTF(numberString);
while (true) {
String message = br.readLine();
dos.writeUTF(message);
}
} catch (IOException e) { }
}
}
The clients are added to the server as I expect.
If I have one client, the --- dos.writeUTF(message); --- give the desired result inside client console, so in server console I write down (A) and press enter, then the (A) is printed inside #1 Client, then I write down (B) in server console and press enter, then the (B) is printed inside #1 Client, it seems good.
But if the second client join the server, the result inside both clients console is hold for one time press and is fired on second enter press. To simplify : Inside server console I press (A) and press enter, nothing is happening on clients consoles, then I press (B) inside server console again, and now the first message (A) and second message (B) are printed one (A) inside #1 Client and one (B) inside #2 Client and this is not the behavior that I expect.
What I expect: When 2 Clients exist and server send message (A), I need all Clients (here there are 2 clients) get the message together. Imagine you are playing a game online, If I have a character (like a ball) and I move it inside a game field, all the other players have to see my character moving. I wish I was clear.
I think I have to make an array to keep the clients and send message to that array maybe...
So how can I solve it. If the client code is needed please tell me.
Look into something called the Publish-Subscribe pattern, also known as the Observer pattern, here is an example:
https://www.journaldev.com/1739/observer-design-pattern-in-java
In Java i am trying to store each BufferedOutputStream the server makes into an array aside from the threads in order to broadcast some data to all connected clients.
// initialisation
ArrayList<BufferedOutputStream> connections = new ArrayList<BufferedOutputStream>();
// when a client connects
Socket connection = socket.accept();
connections[id] = connection;
// broadcasting to all clients
for (int i = 0; i < connections.size(); i++) {
try {
OutputStreamWriter osw = new OutputStreamWriter(connections.get(i), "US-ASCII");
osw.write(s + "\n");
osw.flush();
} catch (Exception g) {
// catch
}
}
note: only the essential code is given
The problem: The broadcasting loop only broadcasts to the first in the loop, and sometimes the others as well. No error is thrown and the loop iteration as it should.
What is wrong and how do i fix it?
It's probably something obvious but i'm still a beginner..
Thanks!
We can't tell you exactly what is wrong because you've left out important parts of the code. However, if connections[id] = connection is throwing an NPE, that can only mean that connections is null. And, on the face of it, you don't appear to have initialized connections to a non-null value!
The fix is to initialize connections ... somewhere ... to an array of the appropriate size. However, that brings you other problems. What is an appropriate size for the array, and what what you are going t when id is larger than connections.length?
The root problem is an array is (probably) a poor choice for holding the connections ...
Maybe the socket has close, or the i index in the array is null, so the question is, why don't you utilize the List<OutputStream> over Socket[]?
with List<OutputStream> or List<OutputStreamWriter> you will not need to initialize the OutputStreamWriter every time you want to send some data.
somehow You should store client socket object.
whenever you want to broadcast, iterate over collection get socket, get ouput stream from that, and write to socket.
Its working for me properly.
EDIT: Socket[] array = new Socket[#];
You never initialize your array. That could be the problem.
But I suggest using either a List or a Map.
Apparently, I'm guessing you wanna store the socket in a way that you could retrieve it by a property later (name or ID). In this case, I suggest using a HashMap, with Integer as they key, and Socket as the value. This was, you can use map.get(ID);, and it'll return the socket you want.
You could try using a HashMap, maybe have a var for how many people are currently connected.
When someone logs in, do something like
public class Serer {
HashMap<Integer, Socket> list = new HashMap<Integer, Socket>();
static final int maxConnections = 100;
static int currentConnections = 0;
public Server() {
try {
ServerSocket socket = new ServerSocket(/*port#*/, maxConnections);
}catch(IOException e) { }
}
public void acceptConnections() {
while(currentConnections < maxConnections) {
list.put(currentConnections++, serversocket.accept());
}
}
public Socket getSocket(int ID) {
return list.get(ID);
}
public static void main(String[] args) {
new Server().acceptConnections();
}
This isn't tested, and I highly suggest putting the 'acceptConnections' in a thread, so the while loop isn't holding up your code. I didn't catch any exceptions, but hopefully this will give you an idea of using a HashMap to hold your sockets
Now, if you wanna send data to all the sockets, you would need to create an OutputStream for each socket. I suggest making a class (such as User.java), then when someone connects, make a new User, passing it the socket.
while(true) {
new User(ss.accept());
}
Then in your User.java, have something kind of like:
public class User {
ObjectOutputStream out;
ObjectInputStream in;
Socket socket;
public User(Socket socket) {
this.socket = socket;
initStream();
startChat();
}
public void initStream() {
try{
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputSTream(socket.getInputStream());
}catch(IOException e) { }
}
public void startChat() {
new Thread(new Runnable() {
public void run() {
String input;
try {
while((input = (String) in.readObject) != null) {
//this loop only triggers when something is retrieved through the input stream
Server.sendGlobalMessage(input); //can be done in different ways
//The reason why I call this statically from Server.java is because Server.java
//is the class that contains the HashMap, but that's up to you of where to put it.
//You could make the `HashMap` static, and make the sendGlobalMessage() in User.java
}
}catch(IOException | ClassNotFoundException e) { }
};).start();
}
Now finally for the sendGlobalMessage, you will need to either use an Iterator, or you can turn your hashmap into an array. This code is assuming that instead of using a HashMap for sockets, you pass the socket into a User class, then use the HashMap to store the user. (you need access to the output stream)
HashMap<Integer, User> list = new HashMap<Integer, User>();
public static void sendGlobalMessage(String message) {
for(User user : list.values().toArray(new User[list.size]) {
try {
user.out.writeObject(message);
user.out.flush();
}catch(IOException | ClassNotFoundException e) { }
}
}
I've got a client-server app I'm making and I'm having a bit of trouble when reading objects on the server.
After my server connects to a client socket, I build object input and output streams and pass them along to my service() method. In there, I'm supposed to handle different kinds of messages from the client. I can get a message from the client (that is, a Message object, of my design) just fine. But of course, what I want to do is have a loop so I can get a message, process it, and respond back.
So far, my code only works for a single message. When I added my loop, what happened was on every iteration, my server just kept reading the same message over and over again before my client got a chance to send a new message over the socket (I think this is what's happening, at least).
So what I really need to do is figure out how to make my service() method wait for new input. Any ideas? Or am I approaching this wrong? Do I need to create a new OIS on every iteration or...? Some code:
public void service(ObjectInputStream input, ObjectOutputStream output) throws IOException, Exception {
_shouldService = true;
while (_shouldService) {
// It just keeps reading the same message over and over
// I need it to wait here until the client sends a new message
// Unless I'm just approaching this all wrong!
NetworkMessage message = (NetworkMessage) input.readObject();
NetworkMessageHeader header = message.getHeader();
String headerType = header.getType();
if (headerType.equals(NetworkMessageHeader.NetworkMessageHeaderTypeConnect)) {
doLoginForMessage(message, output);
} else if (headerType.equals(NetworkMessageHeader.NetworkMessageHeaderTypeFiles)) {
doFilesList(message, output);
} else {
System.out.println("Unrecognized header type: " + headerType);
}
}
}
The ObjectOutputStream caches object representations and will not detect if you are resending the same instance over and over again from the client side, but with changes in it. If this is your scenario you need to call reset on the stream before each send.
NetworkMessage message = new NetworkMessage();
for(;;) {
message.setProperty(whatever);
oos.reset();
oos.writeObject(message);
}