Take it easy with me, I'm new in socket programming.
I'm about making a program which is similar to Teamviewer. I could make a simple Server/client application which is multithreaded. But the problem is I couldn't figure out how to do something like :
Let's say I have Server and a client connected to server.
I could transfer a file from client to server. I could make a simple chat with a client. But my problem is : How can I chat with the client while file is transferring in the same time? I mean I couldn't make more than one function in a time.Because we have just one input and one output for both server and client. So how could I send more that one function to client and how could the client read more that one function in a time and respond to requests ?
I did something like : I sent a request to client and I got the response in a new thread which contains a new DataInputStream but I couldn't figure out how the main DataInputStream will receive a new response from the client because what is receiving the requests in this way is the new DataInputStream. I'm really lost in this situation because I feel my concept is completely wrong but I couldn't figure out the right concept to do something like that.
Is it possible with ServerSocket or should I look at NIO Socket ?
NOTE :
I don't want a piece of code, I would like to understand the concept of the whole operation for something like that. Thank you
Use a packet based massaging system over a single connection
Outgoing
{"type":"chat", "message":"hi"}
{"type":"xfer", "fileName":"fileX", "data":"some_binary_data"}
Incoming
switch(getString("type")){
case "chat":
System.out.println("User said" + getString("message"));
break;
case "xfer":
File f = new File(getString("fileName"));
f.write(getString("data"));
break;
...
}
Obviously if the binary file is fairly large, you should break it into many different messages and reassemble it on the other side, this would allow chat messages to make their way across the wire while the transfer is also still taking place.
Related
I am trying to set up a program where the server can communicate with multiple clients. The program is written in Java. I got it all working on the same machine so I decided to try LAN. I converted the program to JAR files and I tried connecting my laptop to my PC (both are on the same network). The connection is successful but unfortunately only 1 message arrives to the server. As you can see in the code below, I send multiple messages (Meaning that i write multiple times) via DataOutputStream. One defines the datatype (in the following example 0 means that it's a String) and the other sends the actual message data. I also print the size of the packets in bytes and it always matches the size of the DataOutputStream instance.
DataOutputStream dOut = new DataOutputStream(clientSocket.getOutputStream());
String str = "Hello";
//Type
System.out.println("Type output size: 1");
dOut.writeByte(0);
//Message
System.out.println("Message output size: " + (str.getBytes(StandardCharsets.UTF_8).length + 2));
dOut.writeUTF(str);
System.out.println("Length of all: " + (dOut.size()));
dOut.flush();
So now when the data from the client is sent we need to handle it on the server, which the code below does. It retrieves the InputStream from the Socket called client and inserts it into the DataInputStream. This is were it gets weird on LAN as the stream only contains the first message.
InputStream stream = client.getInputStream();
DataInputStream dIn = new DataInputStream(stream);
while(dIn.available() > 0) {
byte type = dIn.readByte();
switch(type) {
case 0:
System.out.println(dIn.readUTF());
break;
case 1:
System.out.println(dIn.readInt());
break;
case 2:
System.out.println(dIn.readByte());
break;
default:
throw new IllegalStateException("Unexpected value: " + type);
}
}
If you run the Client in the IDE on lets say a laptop connected to the same network and then you run the Server on a PC connected to the same network it will work. However, not if the programs are in JARS.
The actual stacktrace is the following:
java.net.SocketException: Connection reset
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:323)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:961)
at
java.base/java.io.DataInputStream.readInt(DataInputStream.java:393)
The stacktrace does not tell me anything, but it points at case 0: in the switch case. It can't read the String as the DataInputStream does not contain any data (I guess?).
I would also like to state that the Server is multithreaded! I have one thread that adds the sockets when they are accepted through ServerSocket.accept() and I use the second (main thread) to read the data sent from clients.
I have selected the code above as I believe that the issue lies within it, however I am new to Socket Programming and I know that some of you would like to see other parts of the code. I will add more relevant code when I am asked.
I do not know why it acts like this, does anyone know why?
What have i tried?
I have tried waiting for packets - but that has only resulted in the
Server looping forever. With waiting for packets I mean not going forward until the DataInputStream contains enough bytes.
I have disabled Nagels Algorithm through setTCPNoDelay(false).
Tried to send different datatypes, but that also failed
I tried changing the first packet to a String which resulted in the String showing up in the DataInputStream.
I have tried portforwarding the port used and I have tried disabling the firewall on both computers.
Update 1
I have been taking advice from the comments which has led a to a few discoveries:
Closing the DataOutputStream successfully sends all packets to the client.
It is also possible to build your own buffer and decode it in the server. However, it is still not possible to send any more messages after this.
It worked as a JAR because IntelliJ was being nice (Eclipse threw the same error when running in IDE)
Update 2:
I think this post is relevant. It states that SocketException is sent when a client closes it's socket "ungracefully". And because my Client closes (as it is not in a loop) and I don't close the socket properly - it will close "ungracefully" and the data will be lost. Hence the error.
The issue is now solved, and the solution is quite logical. My client does not operate in a loop, rather it sends the data and closes the program. That sounds fine, but I forgot to properly close the socket of the client.
The reason why the second 'packet' never arrived was due to me doing this tiny mistake. The packet was on it's way through the local network but the client socket improperly closed it's socket before the packet arrived to the server, which is why I got a SocketException error. See this.
I solved the issue by putting socket.close() ,where socket is the client's socket, after I had sent all the messages I wanted to send.
My app can transfer files and messages between server and client. Server is multithreaded and clients simply connects to it. While file is being transferred, if sender sends a message, it will be consumed as bytes of file.
I don't want to open more ports,
Can I establish a new connection to the server for file transfer? Or I
should open a separate port for files.
I don't want to block communication while a file is being transferred.
The question was marked as a duplicate but its not, i am trying to send messages and files simultaneously not one by one. I can already receive files one by one. Read again.
Also, as server is multithreaded, I cannot call server socket.accept() again to receive files in new connection because main thread listening for incoming will try to handle it instead. Is there a way around?
Seems to me like trying to multiplex files and messages onto the same socket stream is an XYProblem.
I am not an expert on this, but it sounds like you should do some reading on "ports vs sockets". My understanding is that ip:port is the address of the listening service. Once a client connects, the server will open a socket to actually do the communication.
The trick is that every time a client connects, spawn a new thread (on a new socket) to handle the request. This instantly frees up the main thread to go back to listening for new connections. Your file transfer and your messages can come into the same port, but each new request will get its own socket and its own server thread --> no collision!
See this question for a java implementation:
Multithreading Socket communication Client/Server
you could use some system of all the lines of a file start with a string like this (file:linenum) and then on the other side it puts that in a file then to send text you could do the same thing but with a tag like (text)
Server:
Scanner in = new Scanner(s.getInputStream());
while(true) {
String message = in.nextLine();
if(message.length > 14 && message.substring(0,6).equalsIgnoreCase("(file:") {
int line = Integer.valueOf(message.substring(6).replaceall(")", ""));
saveToFile(message.substring(6).replaceAll(")","").replaceAll("<1-9>",""));
} else {
System.out.println(message);
}
}
I think that code works but I haven't checked it so it might need some slight modifications
You could introduce a handshake protocol where clients can state who they are (probably happening already) and what they want from the given connection. The first connection they make could be about control, and perhaps the messages, and remain in use all the time. File transfer could happen via secondary connections, which may come and go during a session. Having several parallel connections between a client and a server is completely normal, that is what #MikeOunsworth was explaining too.
A shortcut you can take is issuing short-living, one-time tokens which clients can present when opening the secondary connection and then the server will immediately know which file it should start sending. Note that this approach easily can raise various security (if token encodes actual request data) and/or scalability issues (if token is something completely random and has to be looked up in some table).
I want to connect a client to many servers and receive the first answer from one of them. So I decided to use select. I created a socket with each server and I registered those sockets in a selector. But it didn't work. Here's where there is a problem in my code:
Selector selector = Selector.open();//
int i;
for(i=0;i<serveurs.size();i++)//I have an arraylist of servers
{
Socket s=new Socket(serveurs.get(i).getIP(),port);//creating a socket with the server number i
sc=s.getChannel();//here's the problem: sc is null
sc.configureBlocking(false);//exception here because sc=null
sc.register(selector, SelectionKey.OP_READ);//register the socket channel to recieve data form the server
}
selector.select();
// reading data in the socket registered in the selector
So what can I do to solve this problem ?
Refreshing the book "TCP/IP Sockets in Java, 2nd Edition" came across playing with time server and client. The small demo code for TcpTimeClient.java basically does what you described.
Problems I can see in your code:
To get a SocketChannel, use SocketChannel.open(). The JavaDoc of Socket.getChannel() tells what to do.
Use interest OP_CONNECT to try all connections first, then use interest OP_READ to find the winner.
Concerns depending on the application protocol:
Avoid deadlock, e.g. if server expects reading a message from client before responding and client just waits for response without writing the message first.
The first winner might not be the best, consider any other QoS approaches. Or keep all connections open and switch if necessary.
Ok. I'm trying to grasp some multithreading Java concepts. I know how to set up a multiclient/server solution. The server will start a new thread for every connected client.
Conceptually like this...
The loop in Server.java:
while (true) {
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress().getHostAddress() + " connected");
new ClientHandler(socket).start();
}
The ClientHandler.java loop is:
while(true)
{
try {
myString = (String) objectInputStream.readObject();
}
catch (ClassNotFoundException | IOException e) {
break;
}
System.out.println(myClientAddress + " sent " + myString);
try {
objectOutputStream.writeObject(someValueFromTheServer);
objectOutputStream.flush();
}
catch (IOException e) {
return;
}
}
This is just a concept to grasp the idea. Now, I want the server to be able to send the same object or data at the same time - to all clients.
So somehow I must get the Server to speak to every single thread. Let's say I want the server to generate random numbers with a certain time interval and send them to the clients.
Should I use properties in the Server that the threads can access? Is there a way to just call a method in the running threads from the main thread? I have no clue where to go from here.
Bonus question:
I have another problem too... Which might be hard to see in this code. But I want every client to be able to receive messages from the server AND send messages to the sever independently. Right now I can get the Client to stand and wait for my gui to give something to send. After sending, the Client will wait for the server to send something back that it will give to the gui. You can see that my ClientHandler has that problem too.
This means that while the Client is waiting for the server to send something it cannot send anything new to the server. Also, while the Client is waiting for the gui to give it something to send, it cannot receive from the server.
I have only made a server/client app that uses the server to process data it receives from the Client - and the it sends the processed data back.
Could anyone point me in any direction with this? I think I need help how to think conceptually there. Should I have two different ClientHandlers? One for the instream and one for the outstream? I fumbling in the dark here.
"Is there a way to just call a method in the running threads from the main thread?"
No.
One simple way to solve your problem would be to have the "server" thread send the broadcast to every client. Instead of simply creating new Client objects and letting them go (as in your example), it could keep all of the active Client objects in a collection. When it's time to send a broadcast message, it could iterate over all of the Client objects, and call a sendBroadcast() method on each one.
Of course, you would have to synchronize each client thread's use of a Client object outputStream with the server thread's use of the same stream. You also might have to deal with client connections that don't last forever (their Client objects must somehow be removed from the collection.)
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.