I have a project require using UDP protocol to transfer file but still guaranty the feature of TCP protocol. That means we have the speed of UDP and the file not lost.
I've already have:
Server:
public class UDPServer {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
// TODO code application logic here
int port = 6788;
DatagramSocket sk = new DatagramSocket(port);
byte[] buf = new byte[1000];
while(true){
DatagramPacket request = new DatagramPacket(buf, buf.length);
sk.receive(request);
String msg = (new String(request.getData()));
DatagramPacket reply = new DatagramPacket(msg.getBytes(), msg.getBytes().length, request.getAddress(), request.getPort());
sk.send(reply);
}
}
}
Client:
public class UDPClient {
public static void main(String[] args) throws SocketException, UnknownHostException, IOException {
DatagramSocket sk = new DatagramSocket();
String msg = "message send";
InetAddress addr = InetAddress.getByName("localhost");
int port = 6788;
DatagramPacket request = new DatagramPacket(msg.getBytes(), msg.getBytes().length, addr, port);
sk.send(request);
byte[] buf = new byte[1000];
DatagramPacket reply = new DatagramPacket(buf, buf.length);
sk.receive(reply);
System.out.println("packet da nhan duoi client" + new String(reply.getData()));
sk.close();
}
}
and can you help me any suggest to guaranty file not lost ?. tks
You have quite big task, if you really want to implement fast and reliable file transfer with UDP.
With very small files (that fits in a single IP packet), it could be easy to avoid some overhead of TCP (like TCP connection opening and closing handshakes).
But If you are going to transfer bigger files, then you must implement many difficult features, like: flow control, selective acknowledges, re-transmissions and so on.
So I suggest to use TCP, instead of implementing own similar protocol.
You just CAN'T. It's not designed to be reliable.
If you manage to do it... it just means that you've implemented TCP yourself!, it will be much slower though.
Related
I have written a client and server that are currently in an endless loop, that allow one message at a time to be sent and received. I need it to be able to continuously send/receive messages rather than be limited to one at a time.
Any idea on how I may go about this?
I am new to threads and not entirely sure how they work but i was thinking maybe a thread?
Client:
import java.io.*;
import java.net.*;
public class ClientChat {
DatagramSocket Socket;
public ClientChat(){
}
public void createAndListenSocket() throws SocketException, IOException{
while(true){
Socket = new DatagramSocket();
InetAddress IPAddress = InetAddress.getLocalHost();
System.out.println("Type your message:");
BufferedReader message = new BufferedReader(new InputStreamReader(System.in));
byte[] incomingData = new byte[256];
String sentence = message.readLine();//"Testing from client";
byte[] data = sentence.getBytes();
DatagramPacket sendPacket = new DatagramPacket(data, data.length, IPAddress, 9876);
Socket.send(sendPacket);
System.out.println("Client: " + sentence);
DatagramPacket incomingPacket = new DatagramPacket(incomingData, incomingData.length);
Socket.receive(incomingPacket);
String reply = new String(incomingPacket.getData());
System.out.println("Server: " + reply);
//Socket.close();
}
}
public static void main(String[] args) throws IOException{
ClientChat client = new ClientChat();
client.createAndListenSocket();
}
}
Server:
import java.io.*;
import java.net.*;
public class ServerClient {
public void run() throws Exception{
DatagramSocket Server = new DatagramSocket(9876);
while(true){
byte[] buf = new byte[1024];
DatagramPacket incomingPacket = new DatagramPacket(buf, buf.length);
Server.receive(incomingPacket);
String message = new String(incomingPacket.getData());
System.out.println("Client: " + message);
System.out.print("Server: ");
BufferedReader response = new BufferedReader(new InputStreamReader(System.in));
String reply = response.readLine();
InetAddress IPAddress = incomingPacket.getAddress();
int port = incomingPacket.getPort();
byte[] data = reply.getBytes();
DatagramPacket replyPacket = new DatagramPacket(data, data.length, IPAddress, port);
Server.send(replyPacket);
}
}
public static void main(String[] args) throws Exception {
ServerClient Server = new ServerClient();
Server.run();
}
}
You are correct. You should use two threads - one for sending and one for receiving. You will also need two buffers to store the data while it's waiting to be send or processed after receiving. Most likely, you'll want FIFO buffers (a simple two-sided synchronized queue). Then, your main thread can only deal with reading/writing data to the queue, and the sending/receiving threads will take care of the rest in the background.
The sending thread should try to read from sending queue in a loop. The read operation on queue should block (pause the thread) until data is available.
The receiving thread should run in a loop to read a datagram from network and store it into a "received" queue. Reading datagram from network will block, while storing into a queue will return immediately unless the queue is full.
When the program is finished, you have to somehow "break" the threads from their loop. For example, you can set "shouldExit" boolean to true in your main thread, and check it in your send/receive threads. If you don't care, you can also set threads to daemon mode or just call System.exit().
Size your queues appropriately. If too small, messages will get lost and/or threads will be blocked a lot of time. If too large, you will waste memory. For example, estimate (or observe) the average and maximum number of messages in a queue, and add a safety margin.
I have written UDP server client program. The problem that i am facing is that when i am running server program, it is not waiting for client to connect. Whole code is executing after running till the end. And when i am running client side in between of execution of server side, client side is receiving data from its point of execution. Here is my server code-
public static void main(String args[]) throws Exception
{
DatagramSocket serverSocket = new DatagramSocket(4321);
byte[] sendData;
String sentence = null;
FileInputStream file = new FileInputStream(new File("E:\\Deepak.txt"));
InetAddress IPAddress=InetAddress.getByName("localhost");
BufferedReader in = new BufferedReader(new InputStreamReader(file));
do{
while((sentence = in.readLine()) != null)
{
Thread.sleep(3000);
System.out.println(sentence);
sendData = sentence.getBytes();
DatagramPacket sendPacket =new DatagramPacket(sendData, sendData.length,IPAddress,9876);
serverSocket.send(sendPacket);
}
}while(true);
}
Here is my client side code-
public static void main(String args[]) throws SocketException, UnknownHostException, IOException
{
DatagramSocket clientSocket = new DatagramSocket();
byte[] receiveData = new byte[1024];
String sentence ;
while(true)
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
receivePacket.getLength();
System.out.println(receivePacket.getLength());
clientSocket.receive(receivePacket);
sentence = new String( receivePacket.getData());
}
}
UDP is connectionless, so there is no such thing as a connection in it.
You can only send a packet (in a fire and forget way, send in java will send the data to the port specified in the Datagram) or receive packet on the port (receive in java will block until there is a packet received).
So you would need to implement your own connection on top of UDP if you want to have a server that only sends data if client "connects" to it.
So summing up:
send won't wait for anything, it will just throw the datagram on the wire
receive will wait for a datagram to arrive
Having the above information you would need to write your own protocol to keep the connection.
I am trying to create a p2p connection. This is a just test app that i have check but seems like it does not work over internet.
This is the java code that I am using on my pc to send a datagram to my friend:
'
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.*;
import javax.net.ssl.SSLServerSocket;
public class j2{
public static void main(String[] args) throws Exception {
InetAddress IPAddress = InetAddress.getByName("my friend's public IP");
DatagramSocket clientSocket = new DatagramSocket(3456);
System.out.println("Sending data");
String datamsg = "hello ";
byte[] sendData = datamsg.getBytes("UTF-8");
byte [] receiveData = new byte[10];
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 7890);
int i = 500;
//incase if some packets are lost
while(i-->1)
{
clientSocket.send(sendPacket);
}
System.out.println("Data sent");
System.out.println(clientSocket.isClosed());
clientSocket.close();
}
}
'
//My friend uses this app to receive a data gram:
// port 7890 is used to send data gram and create a hole. The same is used to receice data.
'
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.*;
import javax.net.ssl.SSLServerSocket;
public class j1{
public static void main(String[] args) throws Exception {
InetAddress IPAddress = InetAddress.getByName("any ip"); //does not matter as it is used to open a hole
DatagramSocket clientSocket = new DatagramSocket(7890);
System.out.println("Sending data");
String datamsg = "hello ";
byte[] sendData = datamsg.getBytes("UTF-8");
byte [] receiveData = new byte[10];
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 5000);
int i = 500;
while(i-->1)
{
clientSocket.send(sendPacket);
}
System.out.println("Data sent");
System.out.println(clientSocket.isClosed());
DatagramPacket receivePacket = new DatagramPacket(sendData, sendData.length);
clientSocket.receive(receivePacket);
System.out.println("Packet received");
String msg = new String(receivePacket.getData());
clientSocket.close();
}
}'
// I am not using a stun server as i already know my friends public ip address. We both have disabled our firewall as well.
Your approach is not the most reliable way to do NAT hole punching. At best it will "sometimes work".
Here's some suggestions:
Don't hardcode port numbers. Let your UDP socket code pick a random port number (i.e. port=0) and use a STUN server (or equivalent) to ascertain your public IP address and public port mapping for this local socket.
Use a reliable service to exchange the IP/port. Since you are just trying to get a single packet to go through, start with using a phone to verbally exchange this information could suffice.
You don't need to send 500 packets all at once. The firewall code on
the remote NAT might see this as a DOS attack and block everything.
Try sending like 1 a second.
You should be simultaneously listening and sending periodic packets at the same time
when trying to do the connectivity check that your code is doing.
(e.g. two seperate threads or periodic polling).
Don't close the socket until after connectivity is confirmed by both endpoints. As you have it now, your first program closes the socket immediately after it sends the packet burst.
Read my full answer here: https://stackoverflow.com/a/8524609/104458
I'm starting to write my first Java networking program, and long story short I'm having difficulty making sure that I'm taking the right approach. Our professor has given us a server program to test against this UDP client, but I'm getting some errors I can't seem to squash. Specifically, I get IO exceptions, either "Connection Refused" or "No route to host" exceptions.
public class Lab2Client {
/**
* #param args[1] == server name, args[2] == server port, args[3] == myport
*/
public static void main(String[] args) {
//Serverport is set to 10085, our client is 10086
try {
Socket echoSocket = new Socket(args[0],Integer.parseInt(args[2]));
System.out.println("Server connection Completed\n");
DataOutputStream output = new DataOutputStream(echoSocket.getOutputStream());
byte[] toSend = new byte[5];
toSend[0] = 12; toSend[1] = 34;//Code Number
toSend[2] = 15;//GroupId
toSend[3] = 86;toSend[4] = 100;//Port number in Little Endian Order
output.write(toSend);
System.out.println("Sent Request. Waiting for reply...\n");
DataInputStream input = new DataInputStream(echoSocket.getInputStream());
byte[] toRecieve = new byte[]{0,0,0,0,0,0,0,0};
input.read(toRecieve);
checkMessage(toRecieve);
}
catch (UnknownHostException e) {
System.err.println("Servername Incorrect!");
System.exit(1);
}
catch (IOException e){
System.err.println("IO Exception. Exiting...");
System.err.println(e);
System.exit(1);
}
}
I also have some questions about my implementation regarding receiving messages in Java. I'll be getting a datagram that contains either:
a) 3 formatting bytes (unimportant to the question) along with an IP and port number
or
b) 3 formatting bytes and a port.
Is using a DataInputStream the correct way to do this? I know using an array with 9 elements is lazy instead of dynamically allocating one that's either 5 or 9, but right now I'm just trying to get this working. That being said, is there a different approach anyone would suggest for this?
You need not to wrap the stream returned by Socket.getOuputStream() with DataOutputStream - it is already the DataOutputStream
In this line:
Socket echoSocket = new Socket(args[0],Integer.parseInt(args[2]));
I suppose it should be args[1], not args[0].
Here you have to convert the integer value to its byte representation:
toSend[3] = 10086 & 0xFF;toSend[4] = 10086>>8; //Port number in Little Endian Order
Answer to your question: case b as you are not sending the IP
thought I'd leave this up for posterity. The problem is simple, and I'm a fool for not noticing it sooner.
The correct programs I was testing this against used the UDP protocol, and this program is written in TCP. The corrected code is:
public class Lab2Client {
/**
* #param args[0] == server name, args[1] == server port, args[2] == myport
*/
public static void main(String[] args) {
//Serverport is 10085, our client is 10086
try {
DatagramSocket clientSocket = new DatagramSocket();
InetAddress IPAddress = InetAddress.getByName(args[0]);
int portToSend = Integer.parseInt(args[2]);
System.out.println("Clent Socket Created");
byte[] toSend = new byte[5];
toSend[0] = 0x12; toSend[1] = 0x34;//Code Number
toSend[2] = 15;//GroupId, f in hex
toSend[3] = 0x27;toSend[4] = 0x66;
System.out.println("Byte Array Constructed");
DatagramPacket sendPacket = new DatagramPacket(toSend, toSend.length, IPAddress, Integer.parseInt(args[1]));
clientSocket.send(sendPacket);
System.out.println("Sent Request. Waiting for reply...\n");
DataInputStream input = new DataInputStream(echoSocket.getInputStream());
toRecieve can either be an error message, a return of what we sent,
or a byte stream full of IP info and port numbers.
the "heavy" byte stream is either 4 for IPv4 of 16 for IPv6, 2 bytes for port,
and the magic number (2 bytes) for a total of 9-20 bytes*/
byte[] toRecieve = new byte[9];
DatagramPacket receivePacket = new DatagramPacket(toRecieve, toRecieve.length);
clientSocket.receive(receivePacket);
checkMessage(toRecieve);
} //and so on and so forth...
Thanks to #Serge for the help, though nobody could have answered my question correctly with how I asked it. The byte shifting you suggested was important too.
I'm implementing the example where a server listens for any active clients in the network.
I'm using Datagram sockets for the server to do the multicast and clients to respon to the server.
public void run() {
try {
byte[] recvBuf = new byte[15000];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
DatagramSocket dSock = new DatagramSocket(4445);
dSock.receive(packet);
int byteCount = packet.getLength();
ByteArrayInputStream byteStream = new ByteArrayInputStream(recvBuf);
ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(byteStream));
}
}
and on the client's side:
public void run() {
{
ObjectOutputStream os = null;
try {
InetAddress address = InetAddress.getByName("Server's IP");//Note!
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(15000);
os = new ObjectOutputStream(new BufferedOutputStream(byteStream));
os.flush();
os.flush();
byte[] sendBuf = byteStream.toByteArray();
DatagramPacket packet = new DatagramPacket(sendBuf, sendBuf.length, address, 4445);
int byteCount = packet.getLength();
}
}
}
In the above Eg, the Client has to know the server's IP apriori(hardcode). How can I modify the code on the server's side so that the server sends it's IP to the client and client responds to it?
I was able to do this using sockets but is it possible using datagram sockets?
Thanks!
You could use DatgramPacket.getAddress() and reply to the sender
Returns the IP address of the machine to which this datagram is being sent or from which the datagram was received.
Try getting hostAddress using InetAddress.getHostAddress, read the IP part and pass it to a variable.
InetAddress address = InetAddress.getByName("[variable]");
or
InetAddress address = InetAddress.getByAddress("[variable]");
I hope any of these would lead to a better way.