I made a UDP server client architecture with multi-thread the problem is when I send from the server to the client another client thread get the packet and so for all .. how can I specify the thread in which UDP should send back the packet ??
Client
public UDPClient(int port) throws SocketException
{
this.socket = new DatagramSocket();
this.arg1 = (int) (Math.random() * 1000) ;
this.arg2 = (int) (Math.random() * 1000) ;
this.port = port ;
}
public void run()
{
try{
String x = arg1 + " + " + arg2;
BUFFER = x.getBytes();
InetAddress ip = InetAddress.getByName("127.0.0.1");
packet = new DatagramPacket(BUFFER , BUFFER.length,ip,port);
printOutput(new String("Client send" + x));
socket.send(packet);
socket.receive(packet);
String output = new String(packet.getData(),0,packet.getLength());
printOutput(new String("receive " + x + "=" + output));
}
catch(IOException e)
{
System.out.println("UDP sending problem " + e.getMessage());
}
}
Server
public void run()
{
while(true)
{
try{
packet = new DatagramPacket(BUFFER,BUFFER.length);
socket.receive(packet);
executor.execute(new UDPServerCore(socket,packet,BUFFER));
}
catch(IOException e)
{
System.out.println("UDP receiving packet problem "
+ e.getMessage());
}
}
}
ServerCore
#Override
public void run(){
String x = new String(packet.getData(),0,packet.getLength());
String y = parseString(x);
BUFFER = y.getBytes();
//packet.setData(y.getBytes());
DatagramPacket res = new DatagramPacket(BUFFER , BUFFER.length
,packet.getAddress(),packet.getPort());
try{
socket.send(res);
}
catch(IOException e)
{
System.out.println("Something went wrong " + e.getMessage());
}
}
synchronized private static String parseString(String x )
{
String arr[] = x.split(" ");
int z = Integer.parseInt(arr[0]);
int y = Integer.parseInt(arr[2]);
y = y + z;
writeServer.append(x+ "=" + y +"\n");
return String.valueOf(y);
}
I solved it as the problem was when i created the port of the thread i should have create all the client threads with specified port first then starting each thread.
Y̶o̶u̶ ̶c̶o̶u̶l̶d̶ ̶t̶r̶y̶ ̶t̶o̶ ̶a̶s̶s̶i̶g̶n̶ ̶a̶n̶ ̶I̶D̶ ̶t̶o̶ ̶t̶h̶e̶ ̶p̶a̶c̶k̶e̶t̶ ̶t̶o̶ ̶t̶r̶a̶c̶k̶ ̶t̶h̶e̶ ̶c̶u̶r̶r̶e̶n̶t̶ ̶s̶e̶s̶s̶i̶o̶n̶ ̶i̶t̶ ̶ b̶e̶l̶o̶n̶g̶s̶ ̶t̶o. UDP as opposed to TCP is a no confirmation and no connection protocol. UDP does not support "streams" of data so you have to find a way to dispatch each packet to the corresponding "session handler".
If you could explain more what you're trying to accomplish I think anyone here could help you more.
edit:
Each udp packet contains source address and port. You can use those to recognize and store data for every client that connects. Hint: you could use a map / dictionary and when receiving a packet retrieve your session object using client address and port as key
Related
There are two files (client file, server file) in this program that are supposed to be able to send and receive messages (utf-8 strings) to each other. Each file has a thread (one thread for client, one thread for server)
The client and the server connect on localhost with a port number (it should be the same port number when typing on the command prompt / mac terminal window)
However, the server is supposed to only send messages to all the other clients after receiving a message from a client. In other words, if a client sends a message to the server, the server cannot send that message back to the same client--it can only send messages to the different clients.
Another way to say it: Once a client is connected, it can send messages to the server. It will also receive from the server all messages sent from the other connected clients (not the messages sent from itself).
At runtime, there is supposed to be only one server (mac terminal / command prompt windows) but there can be multiple/infinite number of clients (mac terminal / command prompt windows)
Screenshot of error (server side):
Screenshot of error (client side):
Code of ChatServer.java:
import java.io.*;
import java.net.*;
import java.util.*;
import static java.nio.charset.StandardCharsets.*;
public class ChatServer
{
private static Socket socket;
public static void main(String args[])
{
Thread ChatServer1 = new Thread ()
{
public void run ()
{
System.out.println("Server thread is now running");
try
{
int port_number1 = 0;
int numberOfClients = 0;
boolean KeepRunning = true;
if(args.length>0)
{
port_number1 = Integer.valueOf(args[0]);
}
System.out.println("Waiting for connections on port " + port_number1);
try
{
ServerSocket serverSocket = new ServerSocket(port_number1);
}
catch (IOException e)
{
e.printStackTrace();
}
System.out.println( "Listening for connections on port: " + ( port_number1 ) );
while(KeepRunning)
{
ServerSocket serverSocket = new ServerSocket(port_number1);
//create a list of clients
ArrayList<String> ListOfClients = new ArrayList<String>();
//connect to client
socket = serverSocket.accept();
//add new client to the list, is this the right way to add a new client? or should it be in a for loop or something?
ListOfClients.add("new client");
numberOfClients += 1;
System.out.println("A client has connected. Waiting for message...");
ListOfClients.add("new client" + numberOfClients);
//reading encoded utf-8 message from client, decoding from utf-8 format
String MessageFromClientEncodedUTF8 = "";
BufferedReader BufReader1 = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String MessageFromClientDecodedFromUTF8 = BufReader1.readLine();
byte[] bytes = MessageFromClientEncodedUTF8.getBytes("UTF-8");
String MessageFromClientDecodedUTF8 = new String(bytes, "UTF-8");
//relaying message to every other client besides the one it was from
for (int i = 0; i < ListOfClients.size(); i++)
{
if(ListOfClients.get(i)!="new client")
{
String newmessage = null;
String returnMessage = newmessage;
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
bw.write(returnMessage + "\n");
System.out.println("Message sent to client: "+returnMessage);
bw.flush();
}
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if (socket != null)
{
socket.close ();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
};
ChatServer1.start();
}
}
Code of ChatClient.java:
import java.io.*;
import java.net.*;
import java.util.*;
import static java.nio.charset.StandardCharsets.*;
public class ChatClient
{
private static Socket Socket;
static int numberOfClients = 0;
public static void main(String args[])
{
//If I wanted to create multiple clients, would this code go here? OR should the new thread creation be outside the while(true) loop?
while (true)
{
String host = "localhost";
int numberOfClients = 0;
Thread ChatClient1 = new Thread ()
{
public void run()
{
try
{
//Client begins, gets port number, listens, connects, prints out messages from other clients
int port = 0;
int port_1number1 = 0;
int numberofmessages = 0;
String[] messagessentbyotherclients = null;
System.out.println("Try block begins..");
System.out.println("Chat client is running");
String port_number1= args[0];
System.out.println("Port number is: " + port_number1);
if(args.length>0)
{
port = Integer.valueOf(port_number1);
}
System.out.println("Listening for connections..");
System.out.println( "Listening on port: " + port_number1 );
Socket.connect(null);
System.out.println("Client has connected to the server");
for(int i = 0; i < numberOfClients; i++)
{
System.out.println(messagessentbyotherclients);
}
//client creates new message from standard input
OutputStream os = Socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
//creating message to send from standard input
String newmessage = "";
try
{
// input the message from standard input encoded in UTF-8 string format
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String line = "";
System.out.println( "Standard input (press enter then control D when finished): " );
while( (line= input.readLine()) != null )
{
newmessage += line + " ";
input=null;
}
}
catch ( Exception e )
{
System.out.println( e.getMessage() );
}
//Sending the message to server
String sendMessage = newmessage;
bw.write(sendMessage + "\n");
bw.flush();
System.out.println("Message sent to server: "+sendMessage);
}
catch (IOException e)
{
e.printStackTrace();
}
}
};
ChatClient1.start();
}
}
}
These two errors have been covered many times and I've heard that the answer is to put the socket in a loop, which it already is in (while loop).
My question is: Is there a way to locate the errors before running it? Whenever I compile the program I don't get any errors in eclipse, but when I run it in the command prompt window / mac terminal, it does tell me that something is wrong. Or perhaps there's a line of code that I'm overlooking?
ServerSocket serverSocket = new ServerSocket(port_number1);
Place it once, before the while loop.
I'm working on some code to interact with a server and send a file in 1000 byte chunks. I want to use setSoTimeout to resend a packet after 5 seconds if I have not received an ACK from the server by then. I have searched for the answer to this but to no avail. Here are some links i checked out and attempted:
What is the functionality of setSoTimeout and how it works?
how to use socket.setSoTimeout()?
setSotimeout on a datagram socket
I am under the impression that when the timer is going you are continuously waiting for the ACK. Is this the case? I am never receiving an ACK from the server, although I was at one point before.
import java.net.*;
import java.io.*;
import java.util.*;
public class FTPClient {
Socket tcpSocket;
DatagramSocket udpSocket;
DataInputStream dataIn;
DataOutputStream dataOut;
BufferedReader br;
String fileName;
int time;
int portNum;
/**
* Constructor to initialize the program
*
* #param serverName server name
* #param server_port server port
* #param file_name name of file to transfer
* #param timeout Time out value (in milli-seconds).
*/
public FTPClient(String server_name, int server_port, String file_name, int timeout) {
System.out.println("Server Name: " + server_name + " Server Port: " + server_port
+ " File Name: " + file_name + " Timeout: " + timeout);
fileName = file_name;
time = timeout;
portNum = server_port;
try {
Socket tcpSocket = new Socket(server_name, server_port);
dataIn = new DataInputStream(tcpSocket.getInputStream());
dataOut = new DataOutputStream(tcpSocket.getOutputStream());
br = new BufferedReader(new InputStreamReader(System.in));
}
catch (Exception ex) {
System.out.println("Exception in FTPClient initialization: " + ex);
}
}
/**
*Send file content as Segments
*
*/
public void send() {
try {
File f = new File(fileName);
if (!f.exists()) {
System.out.println("File does not exist...");
return;
}
System.out.println("Sending filename (" + fileName + ") to server.");
dataOut.writeUTF(fileName);
byte msgFromServer = dataIn.readByte();
if (msgFromServer == 0) {
System.out.println("Server ready to receive file");
}
// Create a UDP socket to send the file to the server
DatagramSocket udpSocket = new DatagramSocket();
InetAddress IPAddress = InetAddress.getByName("localhost");
FileInputStream fileIn = new FileInputStream(f);
int seqNum = 0;
int i = 0;
Boolean received = false;;
byte[] chunks = new byte[1000];
int rc = fileIn.read(chunks);
while(rc != -1)
{
System.out.println("Iteration #: " + i);
System.out.println(rc);
// rc should contain the number of bytes read in this operation.
//if (rc < 1000) {
//System.out.println("Bytes read less than 1000");
//System.out.println("Sequence Number: " + seqNum);
//System.out.println("Packet too small to send");
//}
System.out.println("Bytes read greater than 1000");
System.out.println("Sequence Number: " + seqNum);
while (received == false) {
System.out.println("You are looping and sending again");
transferPacket(seqNum, IPAddress, chunks);
received = getResponse();
}
rc = fileIn.read(chunks);
if (seqNum == 1) {
seqNum = 0;
}
else {
seqNum = 1;
}
i++;
}
}
catch (Exception e) {
System.out.println("Error: " + e);
}
}
public Boolean getResponse() {
try {
DatagramSocket udpSocket = new DatagramSocket();
System.out.println("You are in getResponse()");
byte[] receiveData = new byte[1000];
udpSocket.setSoTimeout(time); // set timer
while (true) {
try {
System.out.println("You are receiving a packet");
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
udpSocket.receive(receivePacket);
Segment unwrap = new Segment(receivePacket);
int num = unwrap.getSeqNum();
System.out.println("Received ACK with Sequence Number: " + num);
return true;
}
catch (SocketTimeoutException t) {
System.out.println("Timeout: return false to send()");
return false;
}
}
}
catch (Exception e) {
System.out.println("You don't wanna be here");
return false;
}
}
public void transferPacket(int seqNum, InetAddress IPAddress, byte[] chunks) {
try {
DatagramSocket udpSocket = new DatagramSocket();
byte[] sendData = new byte[1000];
Segment s = new Segment(seqNum, chunks);
sendData = s.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, portNum);
udpSocket.send(sendPacket);
System.out.println("Sent Packet with sequence number " + seqNum);
}
catch (Exception e) {
System.out.println("Exception in transferPacket: " + e);
}
}
/**
* A simple test driver
*
*/
public static void main(String[] args) {
String server = "localhost";
String file_name = "";
int server_port = 8888;
int timeout = 5000; // milli-seconds (this value should not be changed)
// check for command line arguments
if (args.length == 3) {
// either provide 3 parameters
server = args[0];
server_port = Integer.parseInt(args[1]);
file_name = args[2];
}
else {
System.out.println("Wrong number of arguments, try again.");
System.out.println("Usage: java FTPClient server port file");
System.exit(0);
}
FTPClient ftp = new FTPClient(server, server_port, file_name, timeout);
System.out.printf("Sending file \'%s\' to server...\n", file_name);
try {
ftp.send();
}
catch (Exception e) {
System.out.println("Exception: " + e);
}
System.out.println("File transfer completed.");
}
}
You need to keep using the same UDP socket for the life of the application, not a new one per packet, and use it for both sending and receiving.
At present you are also leaking UDP sockets like a firehose.
I am writing a program for UDP Multithreading server.I have written quote based on the inputs and psuedo codes which i have taken from the Stack overflow.When i execute my program my computer is hanging up.Could anyone please correct the issue with the below code and provide me optimal solution for writing a UDP server which handles Multithreading.
public class MyownServer extends JFrame{
private JTextArea jta = new JTextArea();
public static void main(String[] args) throws Exception {
new MyownServer();
}
public MyownServer(){
add(new JScrollPane(jta), BorderLayout.CENTER);
setVisible(true);
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Server");// It is necessary to show the frame here!
try{
DatagramSocket firstsocket = new DatagramSocket(8100);;
int id = 0;
while (true) {
byte[] buf = new byte[256];
DatagramPacket receivePacket =
new DatagramPacket(buf, buf.length);
ClientServiceThread cliThread = new ClientServiceThread(firstsocket, receivePacket);
cliThread.start();
}
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
public class ClientServiceThread extends Thread {
private JTextArea jta = new JTextArea();
DatagramPacket packet;;
boolean running = true;
DatagramPacket sendPacket;
DatagramSocket socket;;
byte[] buf;
ClientServiceThread(DatagramSocket socket,DatagramPacket packet) {
this.socket=socket;
this.packet=packet;
}
public void run()
{
try {
// Compute and process
byte[] data = new byte[256];
DatagramPacket response = new DatagramPacket(data, data.length);
socket.receive(packet);
double rate = Double.parseDouble(new String(buf).trim());
sendPacket.setAddress(packet.getAddress());
sendPacket.setPort(packet.getPort());
socket.send(sendPacket);
socket.receive(packet);
double years = Double.parseDouble(new String(buf).trim());
socket.send(sendPacket);
socket.receive(packet);
double loan = Double.parseDouble(new String(buf).trim());
double monthlyPayment = loan * (rate / 1200) / (1 - (Math.pow(1 / (1 + (rate / 1200)), years * 12)));
double totalPayment = monthlyPayment * years * 12;
sendPacket.setData(new Double((double)(monthlyPayment * 100) / 100.00).toString().getBytes());
socket.send(sendPacket);
socket.receive(packet);
sendPacket.setData(new Double((double)(totalPayment * 100) / 100.00).toString().getBytes());
socket.send(sendPacket);
socket.close();
jta.append("Interest Rate is " + rate + '\n');
jta.append("Number Of years " + years + '\n');
jta.append("Loan Amount is " + loan + '\n');
jta.append("Monthly payment " + monthlyPayment + '\n');
jta.append("Total Payment " + totalPayment + '\n');
} catch (Exception e) {
e.printStackTrace();
}
}
}
I appreciate your help.Thanks in advance.
This section just sits there creating thread after thread forever.
while (true) {
byte[] buf = new byte[256];
DatagramPacket receivePacket
= new DatagramPacket(buf, buf.length);
ClientServiceThread cliThread = new ClientServiceThread(firstsocket, receivePacket);
cliThread.start();
}
You should use a ThreadPool.
Your server socket should wait to receive connections from the clients inside a infinite loop. You do have a loop but you are missing a receive function call in there.
The receive call shall return the client socket on which then you can communicate with the client henceforth.
Here's a complete example for working with DatagramSockets
https://docs.oracle.com/javase/tutorial/networking/datagrams/clientServer.html
Im having a problem that i can't solve by my own. I think my approach with splitting and adding to a array list and reassembling the message pieces in the end works great if (1) the MSG > BUFFER & message rate is 1msg/second. But the problems appear when i send more than 1 message/second and i have to split a large/small message. Yes the approach may be inefficient in the long run, but this is an assignment so i just want to get it working as i wanted and i'm fine by that.
I am pretty sure the problem is that it of course send every message pieces rate times. And the output is like this in my console:
--------------------------------
| UDP Echo Client
| Configuration:
| server name: localhost
| port: 4950
| buffer: 8
| rate: 5
| message size: 15
--------------------------------
Original: [HelloHe, lloHell, o]
Received: [HelloHe]
MESSAGE IS NOT EQUAL!
Received: [HelloHe, HelloHe]
MESSAGE IS NOT EQUAL!
Received: [HelloHe, HelloHe, HelloHe]
MESSAGE IS NOT EQUAL!
Can anyone please try to help me ? What is the best way to fix this ?
UDP Client:
import java.io.IOException;
import java.net.*;
import java.util.*;
/*
UDP Echo client. Sends a echo message of a size to the server and gets it back.
It checks so that the message wasn't lost or anything has happened to it.
by jv222dp
Rate works perfectly when MSG.length <= MY_BUFFER.
When the BUFFER is smaller then the MSG it works great if rate is 1
*/
public class UDPEchoClient {
private static final String MSG = "HelloHelloHello";
private static int MY_PORT;
private static int RATE;
private static int MY_BUFFER;
private static String HOST_NAME;
private static byte[] buf;
private static int packages;
private static int chars;
private static List<String> originalMsg;
private static List<String> receivedString = new ArrayList<>(packages);
private static DatagramPacket sendPacket;
public static void main(String[] args) {
if (!isCorrect(args)) {
System.exit(1);
} else {
try {
/* Configuration printout */
System.out.println("--------------------------------" +
"\n| UDP Echo Client" +
"\n| Configuration: " +
"\n| server name: " + HOST_NAME +
"\n| port: " + MY_PORT +
"\n| buffer: " + MY_BUFFER +
"\n| rate: " + RATE +
"\n| message size: "+MSG.length()+
"\n--------------------------------");
/* Sets the buffer */
buf = new byte[MY_BUFFER];
/* Create socket */
DatagramSocket socket = new DatagramSocket(null);
/* Create local endpoint using bind() */
SocketAddress localBindPoint = new InetSocketAddress(0);
socket.bind(localBindPoint);
socket.setSoTimeout(2000);
/* Create remote endpoint */
SocketAddress remoteBindPoint = new InetSocketAddress(HOST_NAME,
(MY_PORT));
/* Sends and reads the echo message */
sendEchoPackets(socket, remoteBindPoint);
} catch (SocketException se) {
System.err.println("Host unreachable!" +
"Wrong port or host offline");
}
}
}
public static void sendEchoPackets(DatagramSocket socket, SocketAddress remoteBindPoint) {
System.out.println("Original: "+originalMsg.toString());
/* For each string in the List of message parts */
for (String message : originalMsg) {
/* Create datagram packet for sending message */
sendPacket = new DatagramPacket(
message.getBytes(),
message.length(),
remoteBindPoint);
Timer timer = new Timer();
TimerTask rate = new TimerTask() {
#Override
public void run() {
try {
if (RATE == 0 || RATE == 1) {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
} else {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
};
timer.scheduleAtFixedRate(rate, 0, 1000);
readEchoPacket(socket);
}
}
public static void readEchoPacket(DatagramSocket socket){
try {
/* Create datagram packet for receiving echoed message */
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
socket.receive(receivePacket);
String receivedEcho = new String(
receivePacket.getData(),
receivePacket.getOffset(),
receivePacket.getLength());
receivedString.add(receivedEcho);
/* Compares if the message is the same as the one that was sent */
compareEchoMessage(receivedString);
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void compareEchoMessage(List<String> receivedMsg){
StringBuilder sb = new StringBuilder();
for (String str : receivedMsg) {
sb.append(str);
}
System.out.println("Received: "+receivedMsg.toString());
if (sb.toString().compareTo(MSG) == 0){
System.out.printf("%s bytes sent and received!",sb.length());
}
else{
System.out.println("MESSAGE IS NOT EQUAL!");
}
}
/* Splits the message equally */
private static ArrayList<String> splitMessage(String message, int chunks) {
/* */
ArrayList<String> packages = new ArrayList<>(
(message.length() + chunks) - 1 / chunks);
for (int i = 0; i < message.length(); i += chunks){
packages.add(message.substring(i, Math.min(message.length(),
i + chunks)));
}
return packages;
}
public static boolean isCorrect(String[] args) {
/* Make sure all arguments are present */
if (args.length != 4 && args.length == 0) {
printUsage();
return false;
}
else
try {
HOST_NAME = args[0];
MY_PORT = Integer.parseInt(args[1]);
MY_BUFFER = Integer.parseInt(args[2]);
RATE = Integer.parseInt(args[3]);
/* Ensures RATE is not too high with a tested limit of 3000 */
if (RATE > 3000) {
System.err.println("Rate value is too large!");
return false;
}
/* Make sure the host is valid */
if (!isValidHost(HOST_NAME)) {
System.err.println("Host address is not valid!" +
"\nRequires a valid IP address or just localhost");
return false;
}
/* Make sure the port number is in the valid range */
if (MY_PORT <= 0 || MY_PORT >= 65536) {
System.err.println("Port value must be in (0 -> 65535)!");
return false;
}
/* Make sure the buffer is at least 2, not lower */
if (MY_BUFFER < 2){
System.err.println("Buffer must be higher or equal to 2!");
return false;
}
/* Split the message if bigger than buffer to appropriate packages */
if (MSG.length() > MY_BUFFER) {
packages = (int) Math.ceil((double) MSG.length() / MY_BUFFER);
chars = (MSG.length() / packages);
originalMsg = splitMessage(MSG, chars);
}
/* Else adds whole message to array list */
else {
packages = (int) Math.ceil( (double)MSG.length() / MY_BUFFER);
chars = (MSG.length() / packages);
originalMsg = splitMessage(MSG, chars);
}
}
catch (IndexOutOfBoundsException e) {
printUsage();
System.exit(1);
}
catch (NumberFormatException n) {
System.err.println("Invalid arguments!");
printUsage();
System.exit(1);
}
/* Everything is valid */
return true;
}
private static boolean isValidHost(String host) {
/* Check if the string is valid */
if (host == null || host.length() < 7 || host.length() > 15){
return false;
}
else
/* Host is valid "localhost" */
if (host.equals("localhost")){
return true;
}
/* Check the host string, should be in x.x.x.x format */
StringTokenizer token = new StringTokenizer(host,".");
if (token.countTokens() != 4)
return false;
while (token.hasMoreTokens()) {
/* Get current token and convert to an integer value */
String ip = token.nextToken();
try {
int ipVal = Integer.valueOf(ip).intValue();
if ( ipVal < 0 || ipVal > 255)
return false;
}
catch (NumberFormatException ex) {
return false;
}
}
/* IP Address looks valid */
return true;
}
private static void printUsage() {
System.err.println("Input arguments did not match expected arguments!" +
"\nUsage: \"<host_name> <port> <message_buffer> <message_rate>\"");
}
}
UDP Server:
/*
UDPEchoServer.java
A simple echo server with no error handling
*/
import java.io.IOException;
import java.net.*;
public class UDPEchoServer {
public static final int BUFSIZE = 1024;
public static final int MYPORT = 4950;
public static boolean running = true;
public static void main(String[] args) {
byte[] buf = new byte[BUFSIZE];
try{
/* Create socket */
DatagramSocket socket = new DatagramSocket(null);
/* Create local bind point */
SocketAddress localBindPoint = new InetSocketAddress(MYPORT);
socket.bind(localBindPoint);
System.out.println("---------------------------------"+
"\n| UDP Echo Server"+
"\n| Configuration: "+
"\n| port: "+MYPORT+
"\n---------------------------------");
while (running) {
/* Create datagram packet for receiving message */
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
/* Receiving message */
socket.receive(receivePacket);
/* Create datagram packet for sending message */
DatagramPacket sendPacket =
new DatagramPacket(receivePacket.getData(),
receivePacket.getLength(),
receivePacket.getAddress(),
receivePacket.getPort());
String echo = new String(receivePacket.getData(),
receivePacket.getOffset(), receivePacket.getLength());
System.out.printf("UDP echo request from %s", receivePacket.getAddress().getHostAddress());
System.out.printf(" using port %d\n", receivePacket.getPort());
System.out.println("Received: "+echo);
/* Send message*/
socket.send(sendPacket);
}
}
catch (SocketException s){
System.err.println(s.getMessage());
}
catch (IOException e){
System.err.println(e.getMessage());
}
}
}
Let's see what happens when your rate is 5:
This is the timer body:
if (RATE == 0 || RATE == 1) {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
} else {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
}
So, the if condition is false, because the rate is neither 0 nor 1. We go to the else:
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
For RATE = 5, this is like writing:
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
Of course, cancelling the timer five times doesn't have any effect, but it is sending the same packet 5 times one-by-one. It will then send the next part 5 times, and the third part 5 times, because you are creating three separate timers for the parts.
I think if you want to send at a rate of 5 datagrams per second (is that the meaning of the rate?), you should not create as many timers as there are parts. You should create one timer, give it a list of datagrams to send, and set its schedule period to 1000L / rate (make sure rate is not zero!). The timer should pop the next datagram from the list and send it. If there is no datagram left in the list, it should cancel itself.
One loop to fill the list with datagrams
Assign list to final variable which can be used from anonymous class, or to a field.
Create timer and run it with 1000L / rate schedule time.
Run second loop for reading and comparing the received echo datagrams.
Note the two separate loops!
About re-assembling the datagrams
First, please note that the DatagramPacket that you receive from the server is not the same DatagramPacket that you sent to it even if the contents are the same! They are two different objects, and the equals() method from Object is not overridden, meaning that for any two objects a and b of type DatagramPacket, a.equals(b) is equivalent to a == b.
This means that the only thing you can compare is the datagram content, not the datagram object.
Since UDP does not guarantee that the packets will be sent in any particular order, you have to take care of this yourself. This usually means that you have to include more information in the datagram payload than just the string content. A good place to start is adding a byte that represents the number of the part.
For example, suppose you want to send the message "ABCDEF" in two packets containing "ABC" and "DEF". What you are doing now is sending something like:
┌──┬──┬──┐
│65│66│67│
└──┴──┴──┘
┌──┬──┬──┐
│68│69│70│
└──┴──┴──┘
Now you might get that back as
┌──┬──┬──┐
│68│69│70│
└──┴──┴──┘
┌──┬──┬──┐
│65│66│67│
└──┴──┴──┘
And you have no way to know that, and you'll re-assemble it and it will be DEFABC.
But if you sent another byte that gives the order:
┌─┬──┬──┬──┐
│0│65│66│67│
└─┴──┴──┴──┘
┌─┬──┬──┬──┐
│1│68│69│70│
└─┴──┴──┴──┘
you'd get the first byte, convert it to integer, and convert the rest of the bytes to string. Then you can put it in the list using the index, and they will come out in the proper order, no matter if you got the 1 packet first or second.
In a real world situation, you'll also send the size (number of packets) and an identifying number (so that if you get some rogue datagrams that belong to an old communication that only just arrived, they won't get mixed into your re-assembled payload.
How to receive the data from server example "cutting" in parcels of 1024 bytes, cause when the answer comes from server in packets like 2 parts I don't know how solve that.
Ex. When the first packet arrives the size informed by the server is 1988 and received is 1444, that it's ok, but when the second packet arrives the size informed is something like 808333347 and received 540, the sum of 1444 + 540 = 1984 that is right. I don't know where this number 808333347 is coming from.
I'm googling for this solution and all then teachs using udp and I need this for a tcp/ip connection.
The class:
public class Connection implements Runnable {
private static Connection instance;
private SocketChannel channel = null;
private int port = 0;
private int service = 0;
private final int SOCKET_TIMEOUT = 15 * 1000;
private final int SOCKET_BYTES = 16 * 1024;
private final Charset CHARSET = Charset.forName("ISO-8859-1");
private String host = null;
private String message = "";
public Connection(String host, String port){
this.host = host;
this.port = Integer.parseInt(port);
}
public static Connection createConnection(String host, String port) {
if(instance == null){
instance = new Conexao(host, port);
}
return instance;
}
public void connect(){
try{
instance.channel = SocketChannel.open();
instance.channel.socket().setSoTimeout(SOCKET_TIMEOUT);
instance.channel.socket().setTcpNoDelay(false);
instance.channel.socket().setKeepAlive(true);
instance.channel.connect(new InetSocketAddress(host, port));
instance.channel.configureBlocking(false);
} catch (IOException ioe){
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
}
#Override public void run() {
if(null != instance.channel){
if(instance.channel.isConnected()){
Log.d(TAG, "CHANNEL CONNECTED = TRUE");
} else {
Log.d(TAG, "CHANNEL CONNECTED = FALSE");
}
} else {
instance.connect();
Log.d(TAG, "CHANNEL CONNECTED");
}
sendMessage();
while(true){
receiveMessage();
}
}
public void sendMessage() {
int size = message.length();
ByteBuffer buffer = ByteBuffer.allocate(4 + 4 + size);
buffer.putInt(service).putInt(size).put(message.getBytes());
buffer.flip();
for(int i = 0; i < size; i++){
try {
instance.channel.write(buffer);
} catch (IOException ioe) {
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
}
}
public void receiveMessage(){
ByteBuffer buffer = ByteBuffer.allocateDirect(SOCKET_BYTES);
int bytesReaded = 0;
String received = "";
buffer.clear();
try {
do {
bytesReaded = instance.channel.read(buffer);
} while (bytesReaded == 0);
} catch (IOException ioe) {
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
buffer.flip();
int size = buffer.getInt();
received += CHARSET.decode(buffer);
Log.d(TAG,"SERVIÇE: " + size + "/" + received.length() + " MSG: " + received);
}
public int getService() {
return service;
}
public void setService(int service) {
this.service = service;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
I changed the function like this:
public void receiveMessage(){
ByteBuffer buffer = ByteBuffer.allocateDirect(SOCKET_BYTES);
int bytesReaded = 0;
String received = "";
buffer.clear();
try {
bytesReaded = instance.channel.read(buffer);
} catch (IOException ioe) {
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
buffer.flip();
if(bytesReaded >= 4){
if(size == 0 && size < 5000) size = buffer.getInt();
received += CHARSET.decode(buffer);
answer += received;
if(size == answer.length()){
Log.d(TAG,"SERVICE: " + size + "/" + answer.length() + " " + answer);
}
}
}
But it's very ugly now.
You can't control how data arrives on a TCP connection, from either end. It can arrive a byte at a time or in any other quanta up to the size of the data. You have to loop at the receiving end until you have everything you need. You need to use the same read buffer for the size of the socket so you can accumulate data for this loop, and to preserve any data that might belong to a subsequent message.
The 'size informed' cannot be as huge as you state. That only results from a bug. Probably you are getting out of sync.
The problem is that with TCP you have no control how the data is sent (fragmented). You do not know how much data is read with channel.read(...).
You call receiveMessage() in a loop, there you fill read data into a buffer.
You can not be sure that
int size = buffer.getInt();
is the size of the message you receive(only in the first call, if you receive at least 4 bytes.). You have to remember the first 4 received bytes, the size, (since you use getInt()), then you have to channel.read(...) until you have received size bytes, after that -> process the next message.
Also re-use your buffer. Since you use NIO (and non-blocking) i also suggest you to use select(), instead of busy reading.