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.
Related
I have a server (thread) that receives data and has an array of clients (Sockets). Whenever a client connects a new thread is created and a new client is added to the clients array.
Here is the clients:
List<Socket> clients = Collections.synchronizedList(new ArrayList<Socket>());
So then the server send the incoming data to the clients as such:
//for each message:
for(Socket client : clients){ //throws java.util.ConcurrentModificationException
new PrintWriter(client.getOutputStream(), true).println(line);
}
Now there are two issues here:
First of all I occasionally get a java.util.ConcurrentModificationException when thread 2 adds a client.
Also I am creating a new PrintWriter every time I am sending a message. The other option will be to store a PrintWriter for each client but it is a client so it doesn't make sense to store a PrintWriter.
Is this a good approach ?
That's correct if all clients are waiting a println with a getString() on their side, else you may want to change to the UDP protocol.
Creating a new PrintWriter everyloop is not necessary, since the connection will be kept open so just one (for each client) is best, also in the client side you will have one only Reader and that one will wait for messages trough its get methods.
For the concurrent problem you must create a copy of the current list:
for (Socket client : new ArrayList<>(clients)) {
new PrintWriter(client.getOutputStream(), true).println(line);
}
You are trying to access to a object (client.getOutputStream()) while iterating over the list thus the java.util.ConcurrentModificationException. Change your List<> for a ConcurrentLinkedQueue. Also for PrintWriter here you just wrap an existing stream.
I have to implement sending data with specific source port and in the same time listen to that port. Full duplex. Does anybody know how to implement it on java. I tried to create separate thread for listening on socket input stream but it doesnt work. I cannot bind ServerSocket and client socket to the same source port and the the same with netty.
It there any solution for dull duplex?
init(){
socket = new Socket(InetAddress.getByName(Target.getHost()), Target.getPort(), InetAddress.getByName("localhost"), 250);
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
}
private static void writeAndFlush(OutputStream out, byte[] b) throws IOException {
out.write(b);
out.flush();
}
public class MessageReader implements Runnable {
#Override
public void run() {
//this method throw exception EOF
read(in);
}
private void read(DataInputStream in){
while (isConnectionAlive()) {
StringBuffer strBuf = new StringBuffer();
byte[] b = new byte[1000];
while ((b[0] = bufferedInputStream.read(b)) != 3) {
strBuf.append(new String(b));
}
log.debug(strBuf.toString());
}
}
}
What you're trying to do is quite strange: A ServerSocket is a fully implemented socket that accepts connections, it handles its own messages and you definitely cannot piggy-back another socket on top of it.
Full duplex is fairly simple to do with NIO:
Create a Channel for your Socket in non-blocking mode
Add read to the interest OPs
Sleep with a Selector's select() method
Read any readable bytes, write any writable bytes
If writing is done, remove write from interest OPs
GOTO 3.
If you need to write, add bytes to a buffer, add write to interest OPs and wake up selector. (slightly simplified, but I'm sure you can find your way around the Javadoc)
This way you will be completely loading the outgoing buffer every time there is space and reading from the incoming one at the same time (well, single thread, but you don't have to finish writing to start reading etc).
I had run into the same question and decided to answer it myself. I would like to share with you guys the code repo. It is really simple, you can get the idea to make your stuff work. It is an elaborate example. The steps accidentally look like Ordous's solution.
https://github.com/khanhhua/full-duplex-chat
Feel free to clone! It's my weekend homework.
Main thread:
Create background thread(s) that will connect to any target machines(s).
These threads will connect to target machines and transmit data and die
Create an infinite loop
Listen for incoming connections.
Thread off any connection to handle I/O
Classes:
Server
Listens for incoming connections and threads off a Client object
Client
This class is created upon the server accepting the incoming connection, the TcpClient or NetClient (i forget what java calls it) is used to send data. Upon completion it dies.
Target
Is created during the start up and connects to a specific target and send data.
once complete it dies.
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 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.
I am working on a chat implementation with Java sockets. I have focused on few functionalities, like authentication, one person chat and a group chat. I was thinking about adding file transfer functionality, and I wonder what's the good practice about this. Should I have separate socket on the server with different port listening just for file transfers? Right now input and output streams that I get from server socket are binded to Scanner and PrintWriter objects respectively, so I find it hard to use that for file transfer.
Any patterns you guys could recommend or give me good recommendations are very appreciated.
Thanks,
ZeKoU
Well, FTP creates a new socket for each file transfer. Note that the connection can be established on the same port as the one used for chat, but with a different initialization dialog.
If you can use a native lib do that, it will be faster. Failing that unless there is a really good reason use a library which will have had all the kinks worked out.
If you really need to send a file in pure Java. Here's one way based on an old project of mine. Note however that there is some serialisation overhead however much of this could be removed via use of the NIO equivalents as discussed here or you could go slightly further and use Zero Copy which lets you tell the OS to copy a file direct to a socket without any intermediary copies. This only works with files though not other data.
You should do this in a separate thread so that the chat still works while the file is transferring. Create a socket and give it some standard port (you can give it port 0 which just chooses the next available port but then you will need to send that information to the other recipient so just using a standard port is easier). Then split your file into blocks and transmit it using the socket:
//Send file (Server)
//Put this in a thread
Socket socket = new Socket(destinationIP, destinationPort);
ObjectOutputStream sender = new ObjectOutputStream(socket.getOutputStream());
sender.writeObject(dataToSend);
//Receive File (Client)
//Kick off a new thread to receive the file to preserve the liveness of the program
ServerSocket serverSocket = new ServerSocket(ListenPort);
socket = new Socket();
while (true) {
new Thread(new TCPReceiver(socket)).start();
}
//Receive file thread TCPReceiver(Socket socket)
//Get the stream where the object is to be sent
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
/* Where type is the type of data you send... String or anything really...
read part of the file into something and send it then at this end use the same data
type to recieve it and it will magically pull the entire object across.
*/
while(fileIsIncomplete){
type recievedData = (type) objectInputStream.readObject();
//Reconstruct file
}
Hope that is enough for you to get a quick file sender up and running :)
Edit: Removed nonsense statement. Added native point and zero copy mention.