I want to send and receive data (unicode utf-8 strings through udp sockets, DatagramSocket. I have been looking into SO for answers, but could only find solutions for TCP transfer using InputStream and like.
Is there a similar way for UDP?
UDP Client
package testsocket;
import java.io.IOException;
import java.net.*;
public class TestSocketSender {
private DatagramSocket sock;
public static void main(String[] args) {
new TestSocketSender();
}
public TestSocketSender(){
try {
sock = new DatagramSocket();//, Inet4Address.getLocalHost());
sendData("Hello World\u00A9");
} catch (SocketException ex) {
ex.printStackTrace();
} catch(IOException e){
e.printStackTrace();
}
}
private void sendData(String data) throws IOException{
DatagramPacket datagram = new DatagramPacket(data.getBytes("utf-8"), data.length(), InetAddress.getLocalHost(), 5060);
sock.send(datagram);
}
}
UDP Server
package testsocket;
import java.io.IOException;
import java.net.*;
public class TestSocketListerner {
private DatagramSocket sock;
public static void main(String[] args) {
new TestSocketListerner();
}
public TestSocketListerner(){
try {
sock = new DatagramSocket(5060);//, Inet4Address.getLocalHost());
while(true){
System.out.println(recvData());
}
} catch (SocketException ex) {
ex.printStackTrace();
} catch(IOException e){
e.printStackTrace();
}
}
private String recvData() throws IOException{
byte[] data = new byte[100];
DatagramPacket datagram = new DatagramPacket(data , data.length);
sock.receive(datagram);
return new String(datagram.getData(),datagram.getOffset(),datagram.getLength(),"utf-8");
}
}
UDP is a datagram-based protocol. It means that you can only send a fixed amount of data without any warranties about the data being delivered. The packet may be lost, the data inside may be corrupted because even the CRC is not mandatory in UDP and even worse - the packets you send may be reordered on the receiving side because of the multiple routers along the packet's way.
To implement something which looks like a Stream using the UDP channel is not trivial, but surely possible. You have to split the data into small portions (the MTU of the system), pack them with some recovery information, assign sequence numbers to each packet and provide some mechanism to reask packets which are lost on the way.
send the length first.
Have your sender pack the bytes first, then send the length and then the string. I suggest you pack it into four bytes.
Have your reader, read 4 bytes, then reconstruct the length, then read the string.
You can then handle the problems of data not arriving and timing out, etc...
Related
Im working on building my own GUI program that talks to my pc from a tablet. I have the server side done in java but my problem is on the client side.
I want to send data out the PrintWriter to the server from a separate method.
I have accomplished sending in the code below (it sends 'a') but i cant figure out how to send it from a separate method. i believe its a basic java scope problem that im not understanding. i would really appreciate the help.
I have tried moving the variables into other scopes.
import java.io.*;
import java.net.*;
public class TestClient {
public static void main(String[] args) {
String hostName = "192.168.0.3";
int portNumber = 6666;
try ( //Connect to server on chosen port.
Socket connectedSocket = new Socket(hostName, portNumber);
//Create a printWriter so send data to server.
PrintWriter dataOut = new PrintWriter(connectedSocket.getOutputStream(), true);
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))
) {
//Send data to server.
dataOut.println("a");
}catch (UnknownHostException e) {
System.err.println("Don't know about host " + hostName);
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to " +
hostName);
System.exit(1);
}
}
public static void sendToServer() {
//I want to control the print writer from this method.
//I have failed i all the ways i have tried.
}
}
You could move the Printer-Code (try try block) into the sendToServer-method and call it via
TestClient client = new TestClient();
client.sendToServer("this is a test");
Of course the sendToServer method needs to accept a parameter then. Even better would probably be to put the main method into a Starter class and decouple it from the Client-Class that you use for sending the data.
I'm now processing a client-server communication in Java, by using Socket and ServerSocket objects.
Once the server has been initialised, it puts on hold with new clients through the accept() method, from ServerSocket class; I immediately provide to put this socket in a client map on the server:
- keys: ClientNode(Socket s, CommunicationChannels channels);
- values: Info();
(CommunicationChannels contains ObjectOutputStream and ObjectInputStream from socket; Info contains some information about client, as username, messages etc..).
Given that, at the very beginning, the socket does not have any other information on the client besides the socket itself, first insertion on the map is map.put(ClientNode, null). I will fill the field "value" afterwards.
Now, on Client class, I am going to initialise a Socket("127.0.0.1", 13001), namely with a loopback address and gate 13001. Once communication channels have been initialised, client connects to the server.
Once the client starts, he takes a remote copy of the server through RMI (stub) libraries and the server makes a register() method available: it would allow to use this method to write requested information (from the clients) on the map.
How can the client go back to the socket with which it has been registered on the server? Frankly speaking, I supposed that accept() method from ServerSocket could take the socket established on the startup client back to the server, namely with the new Socket("127.0.0.1", 13001), but it seems to me that this does not happen.
Here you can find parts of the code, so you can better understand what I'm talking about. I've already taken into account a few things that I will share with you in case of need.
public class Server implements Runnable, RemoteServices {
...
private Map<ClientNode, Info> map = new HashMap<ClientNode, Info>();
...
public void run() {
ServerSocket ss = null;
try {
ss = new ServerSocket();
while (true) {
if (!ss.isBound()) {
ss.bind(new InetSocketAddress(ipServer, port));
}
Socket client = ss.accept();
CommunicationChannels channels = new CommunicationChannels(new ObjectOutputStream(client.getOutputStream()), new ObjectInputStream(client.getInputStream()));
map.put(new ClientNode(client, channels), null);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// RemoteServices implementa Remote e mette a disposizione register()
public void register(Info info) throws RemoteException {
// TODO
}
public class Client implements Runnable {
...
...
#Override
public void run() {
Socket client = null;
try {
client = new Socket(ipServer, port);
out = new ObjectOutputStream(client.getOutputStream());
in = new ObjectInputStream(client.getInputStream());
Registry registry = LocateRegistry.getRegistry("127.0.0.1");
stub = (RemoteServices) registry.lookup("remoteObject");
Info info = new Info();
info.setID(getID());
info.setUsername("Giordano");
stub.register(info);
} catch (IOException e) {
System.err.println("Spiacente: il server ha terminato l'esecuzione.");
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
Remarks:
I have not included the code of some classes because I thought it was superfluous; for example the "info" class is just a series of "getter and setter" of some fields; the CommunicationChannels class represents communication channels of the client, taken directly from the socket, etc ..
The server, after the accept(), does not instantiate any thread to communicate with the client because communication has to come afterwards. However, if there was a way to solve my problem with a thread of communication I would find a way to fix it.
My question starts from the need to make interact 2 clients with a server without using more PCs; therefore all clients will have the IP loopback and therefore I cannot use the IP address as a discriminating between two sockets, otherwise I would have already solved it.
In other words, I know that methods as socket.getInetAddress().getHostAddress() can help me to distinguish between two socket, but if I initialise two clients on the same PC I have to use loopback address and the method always returns "127.0.0.1".
The register() method is obviously incomplete even in the signature; once understood how to compare the server socket and client one through a Serializable discriminating object (like the hashcode()) probably I might put it in the arguments of the method, so you can easily make the comparison.
Finally, main() methods:
public static void main(String[] args) {
Server server = new Server("127.0.0.1", 13001);
RemoteServices stub;
try {
stub = (RemoteServices) UnicastRemoteObject.exportObject(server, 0);
Registry registry = LocateRegistry.getRegistry();
registry.bind("remoteObject", stub);
(new Thread(server)).start();
} catch (RemoteException e) {
System.err.println("Verificare l'apertura dei registri");
} catch (AlreadyBoundException e) {
System.err.println("Server giĆ attivo. Controllare che i registri siano chiusi correttamente.");
}
}
public static void main(String[] args) { Client client = new Client("127.0.0.1", 13001);
new Thread(client).start();
}
I really hope everything is clear and that you can help me.
I am creating a client-server communication application and would like the client to be able to detect and connect to the server automatically, given that they are on the same network.
Previously, my code was working across a Linux and Windows machine. I would broadcast a simple message and it could be read. I could also see the message while watching network traffic with Wireshark.
The approach I am taking is to
Get the broadcast address(es) on the network on the server.
For a given duration, broadcast a message (soon to be the server IP)
On the client side, wait until a message is received.
I am quite new to networking, so any obvious errors may not be immediately obvious to me.
Server broadcast code:
public class Broadcaster {
/* ... */
public void pulse() throws InterruptedException, IOException, SocketException {
Long elapsed = new Date().getTime();
Long timeout = elapsed + this.duration;
DatagramPacket packet = new DatagramPacket(this.message.getBytes(), this.message.length());
HashSet<InetAddress> channels = Broadcaster.getBroadcastChannels();
while(elapsed <= timeout) {
for(InetAddress channel : channels) {
DatagramSocket socket = new DatagramSocket(this.port);
socket.setBroadcast(true);
socket.connect(channel, this.port);
socket.send(packet);
System.out.println("Broadcast sent to " + channel.getHostAddress() + " (" + socket.getPort() + "): " + this.message);
socket.close();
}
Thread.sleep(this.frequency);
elapsed = new Date().getTime();
}
}
private static HashSet<InetAddress> getBroadcastChannels() throws SocketException {
/* Returns 192.168.0.255 */
}
public static void main(String[] args) {
Broadcaster heart = new Broadcaster("Hello from the Raspberry Pi!", 120000, 5000, 8027);
try {
heart.pulse();
} catch(SocketException e) {
/* ...etc... */
} finally {
System.out.println("Broadcasting completed.");
}
}
}
Client code:
public class BroadcastListener {
private int port;
private int length;
public BroadcastListener(int length, int port) {
this.port = port;
this.length = length;
}
public String getNext() throws IOException {
byte buffer[] = new byte[this.length];
DatagramSocket socket = new DatagramSocket(this.port);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
System.out.println("Waiting on " + socket.getLocalSocketAddress());
socket.receive(packet);
socket.close();
return new String(buffer);
}
public static void main(String[] args) {
System.out.println("Listening for network broadcasts...");
BroadcastListener broadcast = new BroadcastListener(128, 8027);
try {
System.out.println("Received broadcast: " + broadcast.getNext());
} catch(IOException e) {
System.out.println("Could not receive broadcasts:");
System.out.println(e.getMessage());
}
}
}
The broadcast/netmask address as seen on both devices ifconfig output is netmask 255.255.255.0 broadcast 192.168.0.255
What confuses me the most is that Wireshark is still seeing the broadcast but when I run the client Java program, it just sits at socket.receive(packet);
Wireshark screenshot on Imgur
Both client & server are on port 8027. It is clear that the broadcaster is working, but the client broadcast listener is not. Does anyone have any idea what could be happening? Thanks!
As mentioned in the comments: Check your firewall :-)
Another thing i recognized was that if I sniff with wireshark, no other processes could receive that datagrams. After realizing that, I wrote a nodejs script, which exclusive=false by default but even that did not help. Maybe there is a kernel flag or something, that UDP datagrams cannot be 'consumed' by one process.
It looks like your server may be brodcasting on a different address than your client. Your client is not being assigned an InetAddress, try using this constructor for your socket in BroadcastListener
DatagramSocket socket = new DatagramSocket(new InetSocketAddress("192.168.0.255", this.port));
If that doesnt work you might try binding both your server and client to 127.0.0.1
You keep creating and destroying DatagramSockets. If the packet arrives at your host at a moment when you don't have a DatagramSocket bound to the port, it will be thrown away.
Create one DatagramSocket and leave it open for this life of this code.
Following is a thread which has a SOCKET listening at port 15445.
Whenever a Datagram packet is sent to it, it forwards back to the sender's address after adding a String(Reply from SERVER) in it. I want this code to run somewhere on the Internet, but I don't know where to start.
Is it possible? Can I run this code on a Tomcat server or do I need to do something different?
import java.io.*;
import java.net.*;
public class HelloWorld extends Thread {
protected DatagramSocket socket = null;
protected BufferedReader in = null;
public HelloWorld() throws IOException {
socket = new DatagramSocket(15445);
}
public void run() {
while (true) {
try {
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
InetAddress address = packet.getAddress();
int port = packet.getPort();
String s = "Reply from SERVER";
byte[] b= s.getBytes();
packet = new DatagramPacket(b, b.length, address, port);
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
new HelloWorld().start();
}
}
No, Tomcat isn't a web server. It's a servlet/JSP engine that happens to have an HTTP server built in.
This looks like POJO with a main. Why can't you just run that as a service? Why do you think you need a web server?
Use Jetty (echo server is one of the tutorials afaik) and expose your port to the internet.
If your code works fine forward the necessary port through your router so it's reachable from the internet. If your looking for a hosted solution, vservers are a cheap way to start. http://www.superuser.com is the place to go for network configuration.
I am working on a server/client communication program and I am stuck at a problem. When I try to send messages from my client side it won't work properly. After initializing the server, I connect the client and that is successful. When I try to send messages from the client, the server won't receive them. After I close the client connection, the server receives all of the messages I attempted to send earlier. The following class is what I am using:
public class ServerSender extends Thread
{
private DataOutputStream out = new DataOutputStream(socket.getOutputStream());
private Scanner kb = new Scanner(System.in);
public void run()
{
while(true)
{
try
{
out.writeUTF(kb.nextLine());
out.flush();
} catch(IOException e) { System.out.println("error"); }
}
}
}
Any help would be much appreciated, thanks.
DataOutputStream doesn't have a buffer to flush, so your diagnosis is incorrect. However you need to be aware that writeUTF() writes a format that only DataInputStream.readUTF() can read. If you're trying to write lines you have the wrong API: try BufferedWriter.write()/.newLine().