Question: Are the ports wrong (to make an udp punch)?
I got a Java server that save ip and ports from connecting clients and servers. The ip and ports is sent to the client and server so they can start a udp hole punch. I use 2 computer connected to the same NAT. But it refuse to work.
The java server log say:
Send server global ip and port 61721 to client
Send client global ip and port 63105 to server
The server and client start to send udp packets but after some time they timeout.
I used Wireshark to check the hole punch and the client log looks like this:
Source Destination Info
192.168.1.78 206.217.173.176 UDP source port: 50701 Destination Port: 61721
192.168.1.78 206.217.173.176 UDP source port: 50701 Destination Port: 61721
... + 50 more
Server log:
Source Destination Info
192.168.1.73 206.217.173.176 UDP source port: 6510 Destination Port: 63105
192.168.1.73 206.217.173.176 UDP source port: 6510 Destination Port: 63105
... + 50 more
No packets from the client is found on the server log. And no server packets is found on the client log.
This is the Java server code:
Full code can be found on: https://github.com/Parakoopa/GMnet-GATE-PUNCH/tree/master/src/main/java/org/parakoopa/gmnetgate/punch
Server.java
package org.parakoopa.gmnetgate.punch;
import com.google.gson.annotations.Expose;
import java.net.Socket;
public class Server {
/**
* Contains the IP of this server
*/
#Expose private String ip = "";
/**
* Contains the Ports of the Server.
* A port is assigned to an ip, so only one ip per server is possible.
*/
private Integer port = 0;
/**
* Contains the TCP Sockets of the Server.
* For sending the connection requests to the servers.
*/
private Socket tcp_socket = null;
/**
* The 8 data strings.
*/
#Expose private String data1 = "";
#Expose private String data2 = "";
#Expose private String data3 = "";
#Expose private String data4 = "";
#Expose private String data5 = "";
#Expose private String data6 = "";
#Expose private String data7 = "";
#Expose private String data8 = "";
/**
* Time the server was created
*/
#Expose private long createdTime;
public Server(String ip) {
this.createdTime = System.currentTimeMillis() / 1000L;
this.ip = ip;
}
/**
* Contains the Ports of the Server.
* A port is assigned to an ip, so only one ip per server is possible.
*/
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
/**
* Contains the TCP Sockets of the Server.
* For sending the connection requests to the servers.
*/
public Socket getTCPsocket() {
return tcp_socket;
}
public void setTCPsocket(Socket tcp_socket) {
this.tcp_socket = tcp_socket;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public String getData2() {
return data2;
}
public void setData2(String data2) {
this.data2 = data2;
}
public String getData3() {
return data3;
}
public void setData3(String data3) {
this.data3 = data3;
}
public String getData4() {
return data4;
}
public void setData4(String data4) {
this.data4 = data4;
}
public String getData5() {
return data5;
}
public void setData5(String data5) {
this.data5 = data5;
}
public String getData6() {
return data6;
}
public void setData6(String data6) {
this.data6 = data6;
}
public String getData7() {
return data7;
}
public void setData7(String data7) {
this.data7 = data7;
}
public String getData8() {
return data8;
}
public void setData8(String data8) {
this.data8 = data8;
}
public long getCreatedTime() {
return createdTime;
}
}
TCPConnection.java:
package org.parakoopa.gmnetgate.punch;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Handles incoming TCP connections.
* Stores server sockets and on client request send server ports to client and
* client ports to server via the stored socket.
* #author Parakoopa
*/
public class TCPConnection implements Runnable {
private Mediator main;
private Socket client;
private ServerSocket server;
/** Game Maker Studio seperates strings in buffers with this char (buffer_string). */
private char gm_string_seperator = 0;
/** True, if the "reg" command was used on this connection **/
private boolean isServer = false;
/**
* Set's up a new connection listener that handles all packets of one connection.
* #param main Mediator class instance that this server was created with.
* #param client Socket that the client is connected to.
* #param server Our TCP server socket the client is connected to. (not actually used)
*/
public TCPConnection(Mediator main, Socket client, ServerSocket server) {
this.server = server;
this.client = client;
this.main = main;
}
/**
* Starts listening for incoming packets and responds to it.
*/
#Override
public void run() {
String debug_string = this.client.getInetAddress().getHostAddress()+":"+this.client.getPort()+" | TCP | ";
Mediator.log(debug_string+" Connected!",true);
try {
//TcpNoDelay configures the socket to transfer messages immediately, otherwise GM:S won't pick them up
this.client.setTcpNoDelay(true);
//Input and Output streams. We write bytes out and take Strings in.
OutputStream out = client.getOutputStream();
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream()));
String inputLine;
Server serverObj;
//Process all packets. This while loop will stop when the peer disconnected.
while ((inputLine = in.readLine()) != null) {
//This will kill Threads (or commands) if the client don't send
//all of the data expected. Needed otherwise this could lead
//to many hanging threads.
client.setSoTimeout(1000);
//Cleans string, it might contain some garbage characters.
inputLine = inputLine.replaceAll("\\p{C}", "");
switch (inputLine) {
case "reg2":
Mediator.log(debug_string+" Server wants to register!",true);
//A server wants to register/reregister. We put the socket in the socket map so we can use it later.
//Check version compatibility
String version = in.readLine().replaceAll("\\p{C}", "");
Mediator.log(debug_string+" Version: "+version,true);
if (!(Mediator.versionCompare(version,Mediator.getUdphpMin()) >= 0)) {
//For now just silently end the connection.
//Proper error messages will follow in the next release
Mediator.log(debug_string+" Server not accepted. Version too old.",true);
client.close();
return;
}
Mediator.log(debug_string+" Server registered!",false);
serverObj = this.main.getServer(this.client.getInetAddress().getHostAddress());
this.isServer = true;
serverObj.setTCPsocket(this.client);
//Write the 8 data strings
serverObj.setData1(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 1: "+serverObj.getData1(),true);
serverObj.setData2(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 2: "+serverObj.getData2(),true);
serverObj.setData3(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 3: "+serverObj.getData3(),true);
serverObj.setData4(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 4: "+serverObj.getData4(),true);
serverObj.setData5(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 5: "+serverObj.getData5(),true);
serverObj.setData6(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 6: "+serverObj.getData6(),true);
serverObj.setData7(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 7: "+serverObj.getData7(),true);
serverObj.setData8(in.readLine().replaceAll("\\p{C}", ""));
Mediator.log(debug_string+" Data 8: "+serverObj.getData8(),true);
break;
case "connect":
//A client wants to connect. Now the interesting part begins
//Wait for next line that contains the requested IP adress.
String requested_server = in.readLine().replaceAll("\\p{C}", "");
String debug_string2 = debug_string + " Client <-> "+requested_server+" ->";
Mediator.log(debug_string2+" Connecting...",false);
if (this.main.getServerMap().containsKey(requested_server)) {
//SERVER FOUND
serverObj = this.main.getServer(requested_server);
//get server connection socket from the map (stored above)
Socket gameserver = serverObj.getTCPsocket();
if (!gameserver.isClosed()) {
String connect_to_server = requested_server;
//Get server port
int connect_to_port = serverObj.getPort();
//Send server port to client
Mediator.log(debug_string2+" Found server",true);
Mediator.log(debug_string2+" Send server port "+connect_to_port+" to client",true);
out.write((byte) 255);
out.write((connect_to_server+this.gm_string_seperator).getBytes());
out.write((String.valueOf(connect_to_port)+this.gm_string_seperator).getBytes());
//Send buffer to client
out.flush();
//Get client port
Client clientObj = this.main.getClient(this.client.getInetAddress().getHostAddress());
int connect_to_port_server = clientObj.getPort();
Mediator.log(debug_string2+" Send client port "+connect_to_port_server+" to server",true);
//Get an output stream for the server socket. We will contact the server with this.
OutputStream out_server = gameserver.getOutputStream();
out_server.write((byte) 255);
out_server.write((this.client.getInetAddress().getHostAddress()+this.gm_string_seperator).getBytes());
out_server.write(String.valueOf(connect_to_port_server+this.gm_string_seperator).getBytes());
//Send buffer to server
out_server.flush();
//We are done! Client and Server now connect to each other and the hole is punched!
Mediator.log(debug_string2+" CONNECTED!",false);
} else {
//SERVER FOUND BUT SOCKET IS DEAD
Mediator.log(debug_string+" CONNECTION FAILED - Server not reachable",false);
out.write((byte) 254);
out.flush();
}
} else {
//SERVER NOT FOUND
Mediator.log(debug_string+" CONECTION FAILED - Server not found",false);
out.write((byte) 254);
out.flush();
}
this.main.destroyClient(this.client.getInetAddress().getHostAddress());
break;
case "lobby2":
if (Mediator.isLobby() || Mediator.isTesting()) {
Mediator.log(debug_string+" Sending lobby based on requested filters",true);
HashMap<String, Server> servers = new HashMap<String, Server>(main.getServerMap());
String filter_data1 = in.readLine().replaceAll("\\p{C}", "");
String filter_data2 = in.readLine().replaceAll("\\p{C}", "");
String filter_data3 = in.readLine().replaceAll("\\p{C}", "");
String filter_data4 = in.readLine().replaceAll("\\p{C}", "");
String filter_data5 = in.readLine().replaceAll("\\p{C}", "");
String filter_data6 = in.readLine().replaceAll("\\p{C}", "");
String filter_data7 = in.readLine().replaceAll("\\p{C}", "");
String filter_data8 = in.readLine().replaceAll("\\p{C}", "");
final String filter_sortby = in.readLine().replaceAll("\\p{C}", "");
final String filter_sortby_dir = in.readLine().replaceAll("\\p{C}", "");
String filter_limit = in.readLine().replaceAll("\\p{C}", "");
//Skip servers with <INV> gamename (this might happen if a server was created using UDP connection but never initialized via TCP)
//TODO: Remove these invalid servers after some time.
Iterator<Map.Entry<String, Server>> iterInv = servers.entrySet().iterator();
while (iterInv.hasNext()) {
Map.Entry<String, Server> entry = iterInv.next();
if (entry.getValue().getData1().equals("<INV>")) {
iterInv.remove();
}
}
if (!"".equals(filter_data1)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData1().equals(filter_data1)) {
iter.remove();
}
}
}
if (!"".equals(filter_data2)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData2().equals(filter_data2)) {
iter.remove();
}
}
}
if (!"".equals(filter_data3)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData3().equals(filter_data3)) {
servers.remove(entry.getKey());
}
}
}
if (!"".equals(filter_data4)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData4().equals(filter_data4)) {
iter.remove();
}
}
}
if (!"".equals(filter_data5)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData5().equals(filter_data5)) {
iter.remove();
}
}
}
if (!"".equals(filter_data6)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData6().equals(filter_data6)) {
iter.remove();
}
}
}
if (!"".equals(filter_data7)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData7().equals(filter_data7)) {
iter.remove();
}
}
}
if (!"".equals(filter_data8)) {
Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Server> entry = iter.next();
if (!entry.getValue().getData8().equals(filter_data8)) {
iter.remove();
}
}
}
Server[] arr = servers.values().toArray(new Server[servers.values().size()]);
Arrays.sort(arr, new Comparator<Server>() {
#Override
public int compare(Server o1, Server o2) {
int mp = 1;
int rt = 0;
if ("ASC".equals(filter_sortby_dir)) {
mp = -1;
}
switch (filter_sortby) {
default:
case "date":
rt = new Long(o1.getCreatedTime()).compareTo(o2.getCreatedTime()) * mp;
break;
case "data1":
rt = o1.getData1().compareTo(o2.getData1()) * mp;
break;
case "data2":
rt = o1.getData2().compareTo(o2.getData2()) * mp;
break;
case "data3":
rt = o1.getData3().compareTo(o2.getData3()) * mp;
break;
case "data4":
rt = o1.getData4().compareTo(o2.getData4()) * mp;
break;
case "data5":
rt = o1.getData5().compareTo(o2.getData5()) * mp;
break;
case "data6":
rt = o1.getData6().compareTo(o2.getData6()) * mp;
break;
case "data7":
rt = o1.getData7().compareTo(o2.getData7()) * mp;
break;
case "data8":
rt = o1.getData8().compareTo(o2.getData8()) * mp;
break;
}
return rt;
}
});
if (!"".equals(filter_limit) && Integer.valueOf(filter_limit) <= arr.length) {
arr = Arrays.copyOfRange(arr, 0, Integer.valueOf(filter_limit));
}
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(arr);
out.write((byte) 249);
out.write(json.getBytes());
out.write(10);
//Send buffer to server
out.flush();
}
break;
case "istesting":
out.write((byte) 248);
if (Mediator.isTesting()) {
Mediator.log(debug_string+" Sending if testing is enabled",true);
out.write((byte) 1);
} else {
out.write((byte) 0);
}
//Send buffer
out.flush();
break;
case "testinginfos":
out.write((byte) 247);
if (Mediator.isTesting()) {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
out.write(Mediator.getName().getBytes());
out.write(10);
out.write(Mediator.getVersion().getBytes());
out.write(10);
out.write(Mediator.getUdphpMin().getBytes());
out.write(10);
Mediator.log(debug_string+" Sending testing information",true);
} else {
out.write((byte) 0);
}
//Send buffer
out.flush();
break;
case "version":
out.write((byte) 246);
out.write(Mediator.getVersion().getBytes());
out.write(10);
Mediator.log(debug_string+" Sending version information",true);
//Send buffer
out.flush();
break;
default:
//Ignore unknown commands (client disconnection will cause an unknown command)
break;
}
//Disable timout again and wait for next command
client.setSoTimeout(0);
}
client.close();
Mediator.log(debug_string+" Disconnected!",true);
//Cleanup, when they loose TCP connection, this data can't be used anymore, so it's safe to remove
if (this.isServer) {
this.main.destroyServer(this.client.getInetAddress().getHostAddress());
Mediator.log(debug_string+" Server deleted!",false);
}
} catch (Exception ex) {
Mediator.log(debug_string+" Disconnected (e: "+ex.getClass().getName()+")",true);
//Cleanup, when they loose TCP connection, this data can't be used anymore, so it's safe to remove
if (this.isServer) {
this.main.destroyServer(this.client.getInetAddress().getHostAddress());
Mediator.log(debug_string+" Server deleted!",false);
}
}
}
}
The ports where not wrong. The NAT had been updated by my provider behind my back. They made it more "secure". UDP Punch is not possible anymore with my NAT because it treat punch as an attack and block it.
Related
I have a stream of video. And every frame I need to send a small json file with data from that frame, speed it´s crucial. Whats the best way to do this?
My Server is something like this. Waits for a json file and then has to send that json file to a python application.
public class ServerClass {
public static void main(String[] args) {
Marcoserver mimarco=new Marcoserver();
}
}
class Marcoserver implements Runnable {
public Marcoserver(){
Thread miHilo = new Thread(this);
miHilo.start();
}
public void run() {
try {
ServerSocket server = new ServerSocket(7777);
while (true) {
Socket miSocket = server.accept();
BufferedReader entrada = new BufferedReader(new InputStreamReader(miSocket.getInputStream(), "UTF8"));
String mensaje = entrada.readLine();
JSONObject obj;
obj = new JSONObject(mensaje);
System.out.println(obj);
ConectorSocket cntor = new ConectorSocket("localhost", 6363);
cntor.Conectar();
cntor.Send(mensaje);
cntor.Close();
miSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ConectorSocket{
private String host;
private int port;
Socket sockSend;
public ConnectClass(String hst, int prt ) {
this.host = hst;
this.port = prt;
}
public void Conectar() {
this.sockSend = new Socket(this.host, this.port);
}
public void Send(String mensaje) {
DataOutputStream flujo_salida = new DataOutputStream(sockSend.getOutputStream());
flujo_salida.writeBytes(mensaje);
flujo_salida.close();
}
public boolean Close() {
this.sockSend.close();
}
}
This is the python app simplified:
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind(('localhost', 6666))
serversocket.listen()
while True:
connection, address = serversocket.accept()
buf = connection.recv(2048)
if len(buf) > 0:
print(buf.decode())
My problem is that the python app prints incomplete information such as:
{"keyword"
{"keyword": [[14, 1, -1]]}
{"keyword": [[14,
instead of:
{"keyword":[]}
{"keyword":[[14,1,-1]]}
{"keyword":[[14,2,-1]]}
There's nothing wrong with your Java code, but your Python code is not good:
while True:
connection, address = serversocket.accept()
buf = connection.recv(2048)
if len(buf) > 0:
print(buf.decode())
That will print just the first received TCP packet for each connection.
You need to continue calling recv until it returns 0:
while True:
connection, address = serversocket.accept()
msg = []
while True:
buf = connection.recv(65536)
if (len(buf) == 0):
break
msg.append(buf)
print(''.join(msg))
connection.close()
You also need to close each connection.
I write my own Java FTP server. Until recently I used PUttY to debug my control telnet connection and everything seemed fine - I had successful two-way communication. Now I try to debug my server with FileZilla, but it does not seem to read my text, nor to send some to server, so it just hangs and wait for something.
Control connection class
public class ControlConnection extends Thread {
private enum OperationMode {
ACTIVE, PASSIVE
}
private final Map<String, Supplier<String>> COMMANDS;
private String[] userTokens;
private User user;
private String userLogin;
private boolean authenticated;
private boolean dataConnected;
private boolean userExists;
private final Socket socket;
private DataInputStream inputStream;
private DataOutputStream outputStream;
private DataConnection ftpSession;
private OperationMode operationMode;
private String errorMessage;
public ControlConnection(Socket socket) {
super(ControlConnection.class.toString());
this.socket = socket;
// constants initialization
authenticated = false;
dataConnected = false;
// commands initialization
COMMANDS = new HashMap<>();
// commands init
}
#Override
public void run() {
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
sendGreetings();
IOProcessing.writeBytes(outputStream, pasvCommand());;
boolean running = true;
while (running) {
sendGreetings();
String input = IOProcessing.readBytes(inputStream);
if (!(input.equals("")))
System.out.println(input);
if (!checkInput(input))
continue;
userTokens = input.split(" ");
String command = userTokens[0].toUpperCase();
String answer = COMMANDS.get(command).get();
outputStream.writeBytes(answer);
}
}
catch (IOException e) {
System.err.println(e);
System.exit(-1);
}
}
private boolean commonCheck() {
// some checks
return true;
}
private String getErrorMessage() {
return errorMessage;
}
public void sendGreetings() {
String greetings = String.format("220 Control connection established: %s", getConnectionInfo());
IOProcessing.writeBytes(outputStream, greetings);
}
public String getConnectionInfo() {
String info = String.format("%s: %d %s",
socket.getInetAddress().toString(), socket.getPort(), user != null ? user.getUsername(): "");
return info;
}
// input/output proccessing functions
public boolean checkInput(String input) {
// checks
return true;
}
// commands functions
private String pasvCommand() {
if (operationMode == OperationMode.PASSIVE) {
errorMessage = "Already in passive mode.%n";
return errorMessage;
}
String answer;
new ListenToSocket().start();
answer = String.format("227 Entering Passive Mode (%s, %d)",
"127.0.0.1", DataConnection.PORT);
operationMode = OperationMode.PASSIVE;
return answer;
}
private class ListenToSocket extends Thread {
public ListenToSocket() {
}
#Override
public void run() {
try {
ServerSocket ftpSocket =
new ServerSocket(DataConnection.PORT);
ftpSession =
DataConnection.getDataConnection(ftpSocket.accept());
if (ftpSession != null) {
ftpSession.start();
dataConnected = true;
String greetings = "Data connection established: " + ftpSession.getConnectionInfo();
IOProcessing.writeBytes(outputStream, greetings);
} else {
dataConnected = false;
}
} catch (IOException e) {
System.out.print(e);
}
}
}
also, server does not get user credentials, entered in FileZilla - input from server is always empty
IOProcessing class
public class IOProcessing {
private static final Charset UTF8_CHARSET;
static {
UTF8_CHARSET = Charset.forName("UTF-8");
}
public static String readBytes(DataInputStream inputStream) {
String result = "";
try {
int len = inputStream.available();
if (len == 0) {
return result;
}
byte[] byteInput = new byte[len];
inputStream.readFully(byteInput, 0, len);
result = new String(byteInput, "UTF-8").trim();
} catch (IOException e) {
System.err.println(e);
}
return result;
}
output FileZlla log
Status: Resolving address of localhost
Status: Connecting to [::1]:21...
Status: Connection established, waiting for welcome message.
You didn't show us the writeBytes. So I can only guess that you are not sending \r\n after the messages sent to the client. Particularly after the welcome message. So FileZilla keeps waiting forever for it, as any FTP client would do.
A little bit of context: the client is sending to the server a SOSPFPacket object (via TCP) that has various attributes, such as a Vector<LSA> lsaArray. The LSA itself has a LinkedList<LinkDescription> links attribute. In my test case, there are two messages being sent. In both messages, there is only one LSA in the vector. In the first message, the LSA has one LinkDescription, in the second, it has two. When I send a message, I increment the messageId.
The server receives both messages with proper ids, but in the second message, the links only contain one link instead of two. I'm clueless...
Here are the object implementations:
import java.io.*;
import java.util.Vector;
public class SOSPFPacket implements Serializable {
public final static short HELLO = 0;
public final static short LSU = 1;
public final static short OVER_BURDENED = 2;
public static int id = Integer.MIN_VALUE;
public String srcProcessIP;
public short srcProcessPort;
public String srcIP;
public String dstIP;
public short sospfType; //0 - HELLO, 1 - LinkState Update, 2 - Over Burdened
public String routerID;
public int messageId = id++;
public String neighborID; //neighbor's simulated IP address
public Vector<LSA> lsaArray = new Vector<>();
public String lsaInitiator = null;
}
import java.io.Serializable;
import java.util.LinkedList;
public class LSA implements Serializable {
public String linkStateID;
public int lsaSeqNumber = Integer.MIN_VALUE;
public LinkedList<LinkDescription> links = new LinkedList<LinkDescription>();
#Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(linkStateID + ":").append(lsaSeqNumber + "\n");
for (LinkDescription ld : links) {
sb.append(ld);
}
sb.append("\n");
return sb.toString();
}
}
import java.io.Serializable;
public class LinkDescription implements Serializable {
public String linkID;
public int portNum;
public int tosMetrics;
public LinkDescription() {}
public LinkDescription(String linkID, int portNum, int tosMetrics) {
this.linkID = linkID;
this.portNum = portNum;
this.tosMetrics = tosMetrics;
}
public String toString() {
return linkID + "," + portNum + "," + tosMetrics;
}
}
To send the message, I do it via a Client.java thread implementing Runnable. Here are the relevant methods:
public void run() {
try {
_outputStream = new ObjectOutputStream(_clientSocket.getOutputStream());
sendMessage(SOSPFPacket.HELLO);
_inputStream = new ObjectInputStream(_clientSocket.getInputStream());
SOSPFPacket message = Util.receiveMessage(_inputStream);
if (message.sospfType == SOSPFPacket.OVER_BURDENED) {
System.out.println("Removing link with router " + message.srcIP + "...");
_router.removeLink(_remoteRouterIP);
return;
}
_remoteRouterDescription.setStatus(RouterStatus.TWO_WAY);
_router.addLinkDescriptionToDatabase(_remoteRouterDescription, _link.getWeight());
sendMessage(SOSPFPacket.HELLO);
message = Util.receiveMessage(_inputStream);
if (message.sospfType == SOSPFPacket.LSU) {
_router.synchronize(message.lsaArray);
}
_router.propagateSynchronization(message.lsaInitiator, message.srcIP);
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendMessage(short messageType) {
try {
SOSPFPacket message = Util.makeMessage(_rd, _remoteRouterDescription, messageType, _router);
_outputStream.writeObject(message);
_outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public class Util {
public static SOSPFPacket makeMessage(RouterDescription local, RouterDescription external, short messageType, Router rd) {
SOSPFPacket message = new SOSPFPacket();
message.srcProcessIP = local.getProcessIPAddress();
message.srcProcessPort = local.getProcessPortNumber();
message.srcIP = local.getSimulatedIPAddress();
message.dstIP = external.getSimulatedIPAddress();
message.sospfType = messageType;
message.routerID = local.getSimulatedIPAddress();
message.neighborID = external.getSimulatedIPAddress();
rd.getLsd().getStore().forEach((k, v) -> message.lsaArray.addElement(v));
message.lsaInitiator = messageType == SOSPFPacket.LSU ? message.srcIP : null;
return message;
}
public static SOSPFPacket receiveMessage(ObjectInputStream inputStream) {
SOSPFPacket receivedMessage = null;
try {
receivedMessage = (SOSPFPacket) inputStream.readObject();
String messageType;
switch (receivedMessage.sospfType) {
case SOSPFPacket.HELLO:
messageType = "HELLO";
break;
case SOSPFPacket.LSU:
messageType = "LINKSTATEUPDATE";
break;
case SOSPFPacket.OVER_BURDENED:
messageType = "OVER_BURDENED";
break;
default:
messageType = "UNKNOWN_STATE";
break;
}
System.out.println("received " + messageType + " from " + receivedMessage.srcIP + ";");
} catch (ClassNotFoundException e) {
System.out.println("No message received.");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return receivedMessage;
}
}
And the server instantiates a private ClientServiceThread when it receives a new connection, which is in charge of receiving the message.
private class ClientServiceThread implements Runnable {
Socket _clientSocket;
Thread _runner;
ClientServiceThread(Socket s) {
_clientSocket = s;
_runner = new Thread(this);
}
public Thread getRunner() { return _runner; }
public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
inputStream = new ObjectInputStream(_clientSocket.getInputStream());
outputStream = new ObjectOutputStream(_clientSocket.getOutputStream());
while (true) {
try {
SOSPFPacket receivedMessage = Util.receiveMessage(inputStream);
//some logic not relevant since the receivedMessage is already not correct
}
}
}
}
}
Again, all SOSPFPacket fields are correctly received, except for the Vector<LSA> lsaArray...
Edit: I also tried sending a third sendMessage(SOSPFPacket.HELLO) after _router.propagateSynchronization(message.lsaInitiator, message.srcIP);. This time, the message being sent contains two LSA, the first one having two LinkDescription, the second one having one. Both LSA are received by the server, but still, only the first LinkDescription is received in the first LSA. The message id is correct in all three messages.
If I run everything a second time (i.e. I create a new Client and a new ClientService Thread for the already running routers), only then does the server finally receive two LinkDescription in the first LSA.
Java sends references to objects that have already been serialized, to preserve the integrity of object graphs.
You should call ObjectOutputStream.reset() after each writeObject().
Or use ObjectOutputStream.writeUnshared(), but note that it still shares referenced objects, i.e. if you try to send a list with both added and changed element objects, it will send the new list and new element objects, but not the element objects which have been changed.
Finally figured it out. Somehow it seems like the problem was the following line of code in Util.makeMessage: rd.getLsd().getStore().forEach((k, v) -> message.lsaArray.addElement(v));. I replaced it with rd.getLsd().getStore().forEach((k, v) -> message.lsaArray.add(new LSA(v))); with the following LSA constructor:
public LSA(LSA lsa) {
linkStateID = lsa.linkStateID;
lsaSeqNumber = lsa.lsaSeqNumber;
links = new LinkedList<>();
for (LinkDescription ld : lsa.links) {
LinkDescription linkD = new LinkDescription();
linkD.linkID = ld.linkID;
linkD.portNum = ld.portNum;
linkD.tosMetrics = ld.tosMetrics;
links.add(linkD);
}
}
In other words, I needed to deep copy the object contained in my message.
I'm trying to send and object over udp by first serializing it and then deserializing it on the other end. I thought this would be trivial since I have sent other data over udp before and serialized stuff to the files etc.
I have debugged thing some time now and I keep getting EOFException on the receiving end. Packets arrive properly but somehow deserialization fails. I'm not sure if the mistake is in sender or receiver. I suppose the problem might be about the receiver not knowing the size of the packet.
Here is my sender class:
package com.machinedata.sensordata;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import android.content.Context;
import android.util.Log;
import com.machinedata.io.DataSerializer;
import com.machinedata.io.ManagerUdpPacket;
/**
* This class sends udp-packets. It is used to send driver's information to the manager tablet.
* #author tuomas
*
*/
public class UdpSender
{
private final int MANAGER_PORT = 1234;
private String ip = "192.168.11.50"; //tablet's IP
private DatagramSocket sock = null;
private InetAddress host;
private String mType;
private DataSerializer dataser;
public UdpSender(Context context)
{
try
{
sock = new DatagramSocket();
host = InetAddress.getByName(ip); //tabletin ip
}
catch(Exception e)
{
System.err.println("Exception alustettaessa senderia" + e);
}
dataser = new DataSerializer(context);
}
/**
* With this function we can send packets about our machine to the manager to
* see in the fleet-view.
*/
public void sendToManager(ManagerUdpPacket managerUdp)
{
//serialize
Log.v("sendudp", "Send a packet: " + managerUdp.getDriver());
//serialize
byte[] data = dataser.serializeManagerPacket(managerUdp);
//send
try
{
DatagramPacket dp = new DatagramPacket(data , data.length , host , MANAGER_PORT);
sock.send(dp);
}
catch(IOException e)
{
System.err.println("IOException senderissa " + e);
}
}
public void close()
{
sock.close();
}
}
Here is the serialization function:
/**
* Serializes packet to be sent over udp to the manager tablet.
*/
public byte[] serializeManagerPacket(ManagerUdpPacket mp)
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(mp);
oos.close();
// get the byte array of the object
byte[] obj= baos.toByteArray();
baos.close();
return obj;
}
catch(Exception e) {
e.printStackTrace();
}
return null;
}
Packet receiver class
public class UdpReceiver {
private DatagramSocket clientSocket;
private byte[] receiveData;
private final int timeout = 1;
/**
* Create a receiver.
* #param port Port to receive from.
* #param signCount Number of signals in a packet
*/
public UdpReceiver(int port)
{
//receiveData = serializeManagerPacket(new ManagerUdpPacket("asd", new MachineData(1, 2, "asd", "modelName"), 1,2,3,4,5.0,null));
try{
clientSocket=new DatagramSocket(port);
clientSocket.setReceiveBufferSize(2048);
clientSocket.setSoTimeout(timeout);
}catch(SocketException e){
Log.e("ERR", "SocketException in UdpReceiver()");
}
}
public void close()
{
clientSocket.close();
}
/**
* Receive a data packet and split it into array.
* #param data Array to put data in, must be correct size
* #return True on successful read, false otherwise
*/
public ManagerUdpPacket receive()
{
//receive a packet
DatagramPacket recvPacket = new DatagramPacket(receiveData, receiveData.length);
try{
clientSocket.receive(recvPacket);
}catch(IOException e){
Log.e("ERR", "IOException in UdpReceiver.receive");
return null;
}
ManagerUdpPacket obj = deserializeManagerPacket(receiveData);
if (obj != null)
Log.v("udpPacket", "UDP saatu: " + obj.getDriver());
return obj;
}
/**
* Deserialize the udp-packet back to readable data.
* #param data
* #return
*/
public ManagerUdpPacket deserializeManagerPacket(byte[] data)
{
try
{
ObjectInputStream iStream = new ObjectInputStream(new ByteArrayInputStream(data));
ManagerUdpPacket obj = (ManagerUdpPacket) iStream.readObject();
iStream.close();
return obj;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}
Thread which listens packets in receiving end:
dataStreamTask = new TimerTask()
{
public void run()
{
if (currentlyStreaming)
{
ManagerUdpPacket mp = udpReceiver.receive();
if(mp != null)
{
Log.v("log", "Paketti saatu! " + mp.getDriver());
}
//stop thread until next query
try {
synchronized(this){
this.wait(queryInterval);
}
} catch (InterruptedException e) {
Log.e("ERR", "InterruptedException in TimerTask.run");
}
}
}
And finally the class I'm sending over the UDP:
public class ManagerUdpPacket implements Serializable
{
private static final long serialVersionUID = 9169314425496496555L;
private Location gpsLocation;
private double totalFuelConsumption;
private long operationTime;
//workload distribution
private long idleTime = 0;
private long normalTime = 0;
private long fullTime = 0;
private int currentTaskId;
private String driverName;
String machineModelName = "";
String machineName = "";
int machineIconId = -1;
int machinePort = -1;
public ManagerUdpPacket(String driver, MachineData machine, int currentTaskId, long idleTime, long fullTime, long operationTime, double fuelConsumption, Location location)
{
driverName = driver;
this.currentTaskId = currentTaskId;
this.idleTime = idleTime;
this.fullTime = fullTime;
this.operationTime = operationTime;
this.totalFuelConsumption = fuelConsumption;
this.gpsLocation = location;
machineModelName = machine.getModelName();
machineName = machine.getName();
machineIconId = machine.getIconId();
machinePort = machine.getPort();
}
public String getDriver()
{
return driverName;
}
public int getCurrentTaskId()
{
return currentTaskId;
}
public long getIdleTime()
{
return idleTime;
}
public long getFullTime()
{
return fullTime;
}
public long getOperationTime()
{
return operationTime;
}
public double getTotalFuelConsumption()
{
return totalFuelConsumption;
}
public double getLocation()
{
return gpsLocation.getLatitude();
}
public String getMachineModelName()
{
return machineModelName;
}
public String getMachineName()
{
return machineName;
}
public int getMachineIconId()
{
return machineIconId;
}
public int getMachinePort()
{
return machinePort;
}
}
I tried to get the packet size from the size of the serialized packet or inserting arbitrary 2048 based on some examples on internet. Couldn't get it work though.
As far as i know the receive function returns the length of the bytes it received. But your buffer will be full:
Example:
int buffersize = 1024;
You send 8bytes over udp.
So your byte[] will be full with your 8 bytes but the rest of the 1024 will be 0.
save the size you get by the .receive() call and just save all values of your buffer to another byte[] and you should get your object.
For your example:
public ManagerUdpPacket receive()
{
int receivedBytes = 0;
//receive a packet
DatagramPacket recvPacket = new DatagramPacket(receiveData, receiveData.length);
try{
receivedBytes = clientSocket.receive(recvPacket);
}catch(IOException e){
Log.e("ERR", "IOException in UdpReceiver.receive");
return null;
}
byte[] myObject = new byte[receivedBytes];
for(int i = 0; i < receivedBytes; i++)
{
myObject[i] = receiveData[i];
}
ManagerUdpPacket obj = deserializeManagerPacket(myObject);
if (obj != null)
Log.v("udpPacket", "UDP saatu: " + obj.getDriver());
return obj;
}
When receiving data on UDP, always use java.net.DatagramSocket.getReceiveBufferSize();. This is the actual size of the platform or SP_RCVBUF for the socket. Since UDP is a datagram based protocol unlike TCP, which is streaming protocol, receiving buffers become critical for data sanity. Usually, receiving and sending buffers are equal in size, but you are not bothered while sending when using DatagramSocket.send(DatagramPacket), alternately, you can also use DatagramSocket.setSendBufferSize(DatagramSocket.getSendBufferSize()) for using the SO_SNDBUF option for this socket. Keep in mind, in UDP, if you use a SO_SNDBUF size greater than platform's, the packet can be discarded.
This is one of the most common application scenario that can be found all over the net. and I'm not asking any questions about the java codes that I did because I was successful in running it on my laptop where both the client and server part of the .java file resides. Rather I have had problem getting it to work in between two computers. I tried establishing physical connection using cross-over cable to connect two computers, and did a test to see if file transfers successfully and it did, however, keeping one Server part of the .java file in one computer and client part in the other, I tried to run the server first and then the client but it got a "access denied" error.
For reference here's my two .java files:
/* ChatClient.java */
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ChatClient {
private static int port = 5000; /* port to connect to */
private static String host = "localhost"; /* host to connect to (server's IP)*/
private static BufferedReader stdIn;
private static String nick;
/**
* Read in a nickname from stdin and attempt to authenticate with the
* server by sending a NICK command to #out. If the response from #in
* is not equal to "OK" go bacl and read a nickname again
*/
private static String getNick(BufferedReader in,
PrintWriter out) throws IOException {
System.out.print("Enter your nick: ");
String msg = stdIn.readLine();
out.println("NICK " + msg);
String serverResponse = in.readLine();
if ("SERVER: OK".equals(serverResponse)) return msg;
System.out.println(serverResponse);
return getNick(in, out);
}
public static void main (String[] args) throws IOException {
Socket server = null;
try {
server = new Socket(host, port);
} catch (UnknownHostException e) {
System.err.println(e);
System.exit(1);
}
stdIn = new BufferedReader(new InputStreamReader(System.in));
/* obtain an output stream to the server... */
PrintWriter out = new PrintWriter(server.getOutputStream(), true);
/* ... and an input stream */
BufferedReader in = new BufferedReader(new InputStreamReader(
server.getInputStream()));
nick = getNick(in, out);
/* create a thread to asyncronously read messages from the server */
ServerConn sc = new ServerConn(server);
Thread t = new Thread(sc);
t.start();
String msg;
/* loop reading messages from stdin and sending them to the server */
while ((msg = stdIn.readLine()) != null) {
out.println(msg);
}
}
}
class ServerConn implements Runnable {
private BufferedReader in = null;
public ServerConn(Socket server) throws IOException {
/* obtain an input stream from the server */
in = new BufferedReader(new InputStreamReader(
server.getInputStream()));
}
public void run() {
String msg;
try {
/* loop reading messages from the server and show them
* on stdout */
while ((msg = in.readLine()) != null) {
System.out.println(msg);
}
} catch (IOException e) {
System.err.println(e);
}
}
}
and here's the ChatServer.java:
/* ChatServer.java */
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Hashtable;
public class ChatServer {
private static int port = 5000; /* port to listen on */
public static void main (String[] args) throws IOException
{
ServerSocket server = null;
try {
server = new ServerSocket(port); /* start listening on the port */
} catch (IOException e) {
System.err.println("Could not listen on port: " + port);
System.err.println(e);
System.exit(1);
}
Socket client = null;
while(true) {
try {
client = server.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.err.println(e);
System.exit(1);
}
/* start a new thread to handle this client */
Thread t = new Thread(new ClientConn(client));
t.start();
}
}
}
class ChatServerProtocol {
private String nick;
private ClientConn conn;
/* a hash table from user nicks to the corresponding connections */
private static Hashtable<String, ClientConn> nicks =
new Hashtable<String, ClientConn>();
private static final String msg_OK = "OK";
private static final String msg_NICK_IN_USE = "NICK IN USE";
private static final String msg_SPECIFY_NICK = "SPECIFY NICK";
private static final String msg_INVALID = "INVALID COMMAND";
private static final String msg_SEND_FAILED = "FAILED TO SEND";
/**
* Adds a nick to the hash table
* returns false if the nick is already in the table, true otherwise
*/
private static boolean add_nick(String nick, ClientConn c) {
if (nicks.containsKey(nick)) {
return false;
} else {
nicks.put(nick, c);
return true;
}
}
public ChatServerProtocol(ClientConn c) {
nick = null;
conn = c;
}
private void log(String msg) {
System.err.println(msg);
}
public boolean isAuthenticated() {
return ! (nick == null);
}
/**
* Implements the authentication protocol.
* This consists of checking that the message starts with the NICK command
* and that the nick following it is not already in use.
* returns:
* msg_OK if authenticated
* msg_NICK_IN_USE if the specified nick is already in use
* msg_SPECIFY_NICK if the message does not start with the NICK command
*/
private String authenticate(String msg) {
if(msg.startsWith("NICK")) {
String tryNick = msg.substring(5);
if(add_nick(tryNick, this.conn)) {
log("Nick " + tryNick + " joined.");
this.nick = tryNick;
return msg_OK;
} else {
return msg_NICK_IN_USE;
}
} else {
return msg_SPECIFY_NICK;
}
}
/**
* Send a message to another user.
* #recepient contains the recepient's nick
* #msg contains the message to send
* return true if the nick is registered in the hash, false otherwise
*/
private boolean sendMsg(String recipient, String msg) {
if (nicks.containsKey(recipient)) {
ClientConn c = nicks.get(recipient);
c.sendMsg(nick + ": " + msg);
return true;
} else {
return false;
}
}
/**
* Process a message coming from the client
*/
public String process(String msg) {
if (!isAuthenticated())
return authenticate(msg);
String[] msg_parts = msg.split(" ", 3);
String msg_type = msg_parts[0];
if(msg_type.equals("MSG")) {
if(msg_parts.length < 3) return msg_INVALID;
if(sendMsg(msg_parts[1], msg_parts[2])) return msg_OK;
else return msg_SEND_FAILED;
} else {
return msg_INVALID;
}
}
}
class ClientConn implements Runnable {
private Socket client;
private BufferedReader in = null;
private PrintWriter out = null;
ClientConn(Socket client) {
this.client = client;
try {
/* obtain an input stream to this client ... */
in = new BufferedReader(new InputStreamReader(
client.getInputStream()));
/* ... and an output stream to the same client */
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.err.println(e);
return;
}
}
public void run() {
String msg, response;
ChatServerProtocol protocol = new ChatServerProtocol(this);
try {
/* loop reading lines from the client which are processed
* according to our protocol and the resulting response is
* sent back to the client */
while ((msg = in.readLine()) != null) {
response = protocol.process(msg);
out.println("SERVER: " + response);
}
} catch (IOException e) {
System.err.println(e);
}
}
public void sendMsg(String msg) {
out.println(msg);
}
}
Now, what should I do in order to run this two files from two computers given that I have the physical connection(TCP/IP) setup already??
Thanks in advance... :)
Sounds like it's quite possibly a firewall problem. Have you tried opening a hole in your firewall for port 1001?
Have you also looked at your java.policy and make sure that it is configured to allow local codebase to open sockets?
as mentioned in comment, you should not use port < 1025 for you applications, since they are always used in deamon processes. However you should test your program like this
1) if you get connection refused then you should check the exception properly, whether client program takes time before generating exception ( that mean request is going to server and then it's giving connection refused), in that case you should try java.policy put following in a file named java.policy
grant {
permission java.net.SocketPermission ":1024-65535",
"connect,accept";
permission java.net.SocketPermission ":80", "connect";
permission java.io.FilePermission "", "read,write,delete";
permission java.security.SecurityPermission "";
};
while compiling use this flag -Djava.security.policy=java.policy
more-over you should also try -Djava.rmi.server.hostname=IP, where IP is clien-ip for client.java and server-ip for server.java
2) if you are immediately getting exception at client side then your request is not going outside your pc, so client has some problem.
check the exception properly and post them over here.
3) though i've not got access denied error, but it seems to have port problem that might be solved using policy or port>1024.
post what are you getting now.