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.
Related
I have a UDP client and server that uses DatagramChannel to join multicast group. The client is expected to be able to send strings as well as packets that contains data that is encoded by simple binary encoding.
On the other hand, in the server I need to decode it accordingly. I now am able to decode without receiving garbage values, however, currently when I send string literals as a message, it is interpreted and decoded as a MoldUdpHeader message.
For encoding in the client side:
/**
* simple helper method to send a string message to the multicast address
* #param message
* #throws IOException
*/
public void sendMessage(String message) throws IOException {
byteBuffer.clear();
byteBuffer.put(message.getBytes());
byteBuffer.flip();
client.send(byteBuffer, groupAddress);
}
/**
* simple helper method to send an encodable message to the multicast address
* #param encodableMessage
* #throws IOException
*/
public void sendMessage(IsEncodable encodableMessage) throws IOException {
int offset = 0;
encodableMessage.encode(byteBuffer, offset);
client.send(byteBuffer, groupAddress);
}
The code I use to receive data from a channel:
private boolean receiveData(DatagramChannel channel) throws IOException {
byteBuffer.clear();
SocketAddress remoteAdd = channel.receive(byteBuffer);
byteBuffer.flip();
String msg = decodeByteBuffer(byteBuffer);
System.out.println("Client at " + remoteAdd + " sent: " + msg);
return (msg.equals("end")) ? true : false;
}
This calls the method to decode byte buffer:
private String decodeByteBuffer(ByteBuffer byteBuffer){
String msg = "";
int offset = 0;
moldUdpHeader = (MoldUdpHeader) moldUdpHeader.decode(byteBuffer, offset);
msg = moldUdpHeader.toString();
System.out.println("String decoding: "+decodeStringLiteral(byteBuffer));
// appender.writeDocument(w->w.write().text("write().text() Document: "+msg));
return msg;
}
Decoding strings:
private String decodeStringLiteral(ByteBuffer byteBuffer){
int limits = byteBuffer.limit();
byte[] bytes = new byte[limits];
byteBuffer.get(bytes, 0, limits);
return new String(bytes);
}
The MoldUdpHeader is a custom class I made to store the results from the decoder stub. It basically contains the MessageHeaderEncoder and decoder from sbe-tool as well as the relevant encoder/decoder for the MoldUdpHeader message schema.
public class MoldUdpHeader implements IsEncodable, IsDecodable {
public static final MessageHeaderEncoder MESSAGE_HEADER_ENCODER;
public static final MessageHeaderDecoder MESSAGE_HEADER_DECODER;
public static final MoldUdpHeaderEncoder UDP_HEADER_ENCODER;
public static final MoldUdpHeaderDecoder UDP_HEADER_DECODER;
private long seqNum;
private long tvNsec;
private int senderId;
private int msgCnt;
static {
MESSAGE_HEADER_ENCODER = new MessageHeaderEncoder();
MESSAGE_HEADER_DECODER = new MessageHeaderDecoder();
UDP_HEADER_ENCODER = new MoldUdpHeaderEncoder();
UDP_HEADER_DECODER = new MoldUdpHeaderDecoder();
}
public MoldUdpHeader() {}
public MoldUdpHeader(long seqNum, long tvNsec, int senderId, int msgCnt) {
this.seqNum = seqNum;
this.tvNsec = tvNsec;
this.senderId = senderId;
this.msgCnt = msgCnt;
}
#Override
public IsDecodable decode(ByteBuffer byteBuffer, int offset) {
UnsafeBuffer buffer = new UnsafeBuffer(byteBuffer);
MoldUdpHeaderDecoder decoder = MOLD_UDP_HEADER_DECODER.wrap(buffer, offset
,MoldUdpHeaderDecoder.BLOCK_LENGTH, 0);
this.setSeqNum(decoder.seqNum());
this.setTvNsec(decoder.tvNsec());
this.setSenderId(decoder.senderId());
this.setMsgCnt(decoder.msgCnt());
// System.out.println(this);
return this;
}
#Override
public void encode(ByteBuffer byteBuffer, int offset) {
UnsafeBuffer buffer = new UnsafeBuffer(byteBuffer);
MOLD_UDP_HEADER_ENCODER.wrap(buffer, offset);
MOLD_UDP_HEADER_ENCODER.seqNum(this.seqNum).tvNsec(this.tvNsec).senderId(this.senderId).msgCnt(this.msgCnt);
}
//getters and setters
}
How can I ensure that the string is not malformed, or how can I choose the proper way to decode prior to anything? There is no error thrown for bad decoding, or failure to decode.
sample code for codec:
MoldUdpHeaderEncoder UDP_HEADER_ENCODER = new MoldUdpHeaderEncoder(); //sbe tool generated stub
MoldUdpHeaderDecoder UDP_HEADER_DECODER = new MoldUdpHeaderDecoder(); //sbe tool generated stub
ByteBuffer buffer = ByteBuffer.allocate(1500000000);
for (int i=0; i<=100; i++){
MoldUdpHeader msg = new MoldUdpHeader(seq++, LocalTime.now().toSecondOfDay()* 1_000_000_000L,0xDEADBEEF, i );
System.out.println(msg);
UnsafeBuffer bb = new UnsafeBuffer(buffer);
UDP_HEADER_ENCODER.wrap(bb, 0);
UDP_HEADER_ENCODER.seqNum(msg.getSeqNum()).tvNsec(msg.getTvNsec()).senderId(msg.getSenderId()).msgCnt(msg.getMsgCnt());
MoldUdpHeaderDecoder decoder = UDP_HEADER_DECODER.wrap(bb, 0
,MoldUdpHeaderDecoder.BLOCK_LENGTH, 0);
System.out.println("decoded: "+decoder.seqNum()+", "+decoder.tvNsec()+", "+decoder.senderId()+", "+decoder.msgCnt());
if (i==100){ //example of getting a random string message
bb.wrap("end".getBytes());
decoder = UDP_HEADER_DECODER.wrap(bb, 0
,MoldUdpHeaderDecoder.BLOCK_LENGTH, 0);
System.out.println("decoded: "+decoder.seqNum()+", "+decoder.tvNsec()+", "+decoder.senderId()+", "+decoder.msgCnt());
}
}
Output:
I have implemented Android App - Server side application. The Android app communicate with the server to get authtenticated by the smart card. When I click a button in the App a TCP connection is built and messages are being exchanged until the protocol ends so far so good. Currently I am facing problem when I click the button in the app again. The same process is beig passed through
but the retrieved data from the smart card are different.
In the SmartCard class --> forwardMessage():
--> First button clicking in the Android App --> first clientSocket:
I am getting this byte [0, -92, 2, 12, 2, 0, 2] array as in the screenshot and when calling channel.transmit(new CommandAPDU(array)); I am getting the right response [-112,0]
--> Second button clicking in the Android App --> second clientSocket (without run the server application neu)
I am getting this byte [0, -92, 2, 12, 2, 0, 2] array as in the screenshot and when calling channel.transmit(new CommandAPDU(array)); I am getting the right response [106,-126]
This result [106,-126] should be like the one of the first clientSocket also [-112,0]. As a result of this retrieved data [106,-126] from the smart card the protocol is not being executed to the end.
I have tried to call the disconnect() of the Card class inside the disconnect() of the SmartCard but I am
I appreciate any help!
the first clientsocket data
the second clientsocket data
Android App --> Server --> Card Reader
Server class
public class Server {
private final static int RECEIVE_BUFFER_LENGTH = 512;
public final static int MESSAGE_MAXIMUM_LENGTH = 256;
private final static int MESSAGE_HEADER_LENGTH = 8;
private static InputStream bufferedInputStream = null;
private static OutputStream outputStream = null;
private static SmartCard smartCard = null;
public static void main(String args[]) throws Exception {
ServerSocket serverSocket = new ServerSocket(27015);
System.out.println("SS construction of server socket");
System.out.println("SS listenting for incoming connection on port 27015");
while (true) {
Socket clientSocket = serverSocket.accept();
Server.bufferedInputStream = new BufferedInputStream(clientSocket.getInputStream());
Server.outputStream = clientSocket.getOutputStream();
Server.smartCard = new SmartCard();
Server.handleConnection();
Server.bufferedInputStream.close();
Server.outputStream.close();
Server.smartCard = null;
clientSocket.close();
System.out.println("Smart card instance was deleted ");
System.out.println("--------------------- Finished ---------------------------");
}
}
private static void handleConnection() throws IOException {
ByteBuffer receiveBuffer = ByteBuffer.allocate(Server.RECEIVE_BUFFER_LENGTH);
int readBytes = 0;
while (true) {
readBytes = Server.bufferedInputStream.read(receiveBuffer.array(), receiveBuffer.position(),
receiveBuffer.remaining());
System.out.println("readBytes: " + readBytes);
if (readBytes < 0) {
break;
}
//Here I am reading the received bytes and communicating with the Smart card.
}
}
}
}
SmartCard
public class SmartCard {
public Card card;
private String protocol;
public void connect(String preferredProtocol) {
Card cardTemp = null;
this.protocol = preferredProtocol;
try {
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
CardTerminal terminal = terminals.get(0);
System.out.println("Reader name: " + terminal.getName());
if (preferredProtocol.equals(ProtocolType.SC_T0.getProtocolName())) {
cardTemp = terminal.connect(preferredProtocol);
} else if (preferredProtocol.equals(ProtocolType.SC_T1.getProtocolName())) {
cardTemp = terminal.connect(preferredProtocol);
}
System.out.println("SC connect --> SCARD Protocol " + preferredProtocol);
this.card = cardTemp;
} catch (CardException e) {
e.printStackTrace();
}
}
private boolean isConnect() {
if (this.card != null) {
return true;
} else {
return false;
}
}
public void disconnect() throws CardException {
if (this.isConnect()) {
this.card = null;
//this.card.disconnect(false);
System.out.println("SC disconnect()");
}
}
private void reconnect(String preferredProtocol) {
if (!this.isConnect()) {
this.connect(preferredProtocol);
}
}
public byte[] requestATR() {
ATR atr = this.card.getATR();
if (atr.getBytes().length > 0xFF) {
throw new IllegalArgumentException(
"Package too big, not supported with protocol -> Answer To Test byte array is too big!");
}
return atr.getBytes();
}
public byte[] forwardMessage(byte[] array) throws CardException {
try {
if (!this.isConnect()) {
this.reconnect(this.protocol);
System.out.println("SC reconnect()");
}
Cg2AapiServer.printData(array, array.length, SmartCard.OPERATOR, Cg2AapiServer.RECEIVE);
CardChannel channel = this.card.getBasicChannel();
System.out.println("data from the client socket: " + Arrays.toString(array));
ResponseAPDU responseAPDU = channel.transmit(new CommandAPDU(array));
byte[] byteArray = responseAPDU.getBytes();
System.out.println("retrieved data from the smart card: " + Arrays.toString(byteArray));
if (responseAPDU.getBytes().length > 0xFF) {
throw new IllegalArgumentException("Package too big, not supported with protocol.");
}
Cg2AapiServer.printData(responseAPDU.getBytes(), responseAPDU.getBytes().length, SmartCard.OPERATOR,
Cg2AapiServer.TRANSMIT);
return responseAPDU.getBytes();
} catch (CardException e) {
e.printStackTrace();
}
return null;
}
}
To get it work I had to add the this.card.disconnect(false); method to the disconnect() method in the smart Card like this:
public void disconnect() throws CardException {
if (this.isConnect()) {
// this.card = null;
this.card.disconnect(false);
System.out.println("SC disconnect()");
}
}
I used FTP and FTPClient in package 'org.apache.commons.net.ftp' to download files from FTP server.
Here is my total example code
public class FtpInput {
private static final Logger LOG = Logger.getLogger(FtpInput.class);
private static final int TIMEOUT = 120000;
private static final String SIZE_COMMAND_REPLY_CODE = "213 ";
/**
* FTPClient
*/
private FTPClient ftpClient;
/**
* FTP size
*/
private long completeFileSize = 0;
protected String ip = "";
protected int port = 21;
protected String user = "";
protected String passwd = "";
protected String path = "";
protected String fileName = "";
/**
* count input bytes
*/
private CountingInputStream is;
/**
* the bytes already processed
*/
private long processedBytesNum;
private byte[] inputBuffer = new byte[1024];
/**
* connect to ftp server and fetch inputStream
*/
public void connect() {
this.ftpClient = new FTPClient();
ftpClient.setRemoteVerificationEnabled(false);
try {
ftpClient.connect(ip, port);
if (!ftpClient.login(user, passwd)) {
throw new IOException("ftp login failed!");
}
if (StringUtils.isNotBlank(path)) {
if (!ftpClient.changeWorkingDirectory(path)) {
ftpClient.mkd(path);
if (!ftpClient.changeWorkingDirectory(path)) {
throw new IOException("ftp change working dir failed! path:" + path);
}
}
}
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setSoTimeout(TIMEOUT);
ftpClient.setConnectTimeout(TIMEOUT);
ftpClient.setDataTimeout(TIMEOUT);
ftpClient.enterLocalPassiveMode();
// keep control channel keep-alive when download large file
ftpClient.setControlKeepAliveTimeout(120);
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException("ftp login failed!", e);
}
// get complete ftp size
completeFileSize = getFtpFileSize();
LOG.info(String.format("ftp file size: %d", completeFileSize));
try {
InputStream ftpis = this.ftpClient.retrieveFileStream(this.fileName);
if (ftpis == null) {
LOG.error("cannot fetch source file.");
}
this.is = new CountingInputStream(ftpis);
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
/**
* readBytes
*
* #return
*/
public byte[] readBytes() {
byte[] bytes = readBytesFromStream(is, inputBuffer);
// the bytes processed
processedBytesNum = is.getCount();
return bytes;
}
/**
* readBytesFromStream
*
* #param stream
* #param inputBuffer
* #return
*/
protected byte[] readBytesFromStream(InputStream stream, byte[] inputBuffer) {
Preconditions.checkNotNull(stream != null, "InputStream has not been inited yet.");
Preconditions.checkArgument(inputBuffer != null && inputBuffer.length > 0);
int readBytes;
try {
readBytes = stream.read(inputBuffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (readBytes == inputBuffer.length) {
// inputBuffer is filled full.
return inputBuffer;
} else if (readBytes > 0 && readBytes < inputBuffer.length) {
// inputBuffer is not filled full.
byte[] tmpBytes = new byte[readBytes];
System.arraycopy(inputBuffer, 0, tmpBytes, 0, readBytes);
return tmpBytes;
} else if (readBytes == -1) {
// Read end.
return null;
} else {
// may other situation happens?
throw new RuntimeException(String.format("readBytesFromStream: readBytes=%s inputBuffer.length=%s",
readBytes, inputBuffer.length));
}
}
/**
* fetch the byte size of remote file size
*/
private long getFtpFileSize() {
try {
ftpClient.sendCommand("SIZE", this.fileName);
String reply = ftpClient.getReplyString().trim();
LOG.info(String.format("ftp file %s size reply : %s", fileName, reply));
Preconditions.checkArgument(reply.startsWith(SIZE_COMMAND_REPLY_CODE),
"ftp file size reply: %s is not success", reply);
String sizeSubStr = reply.substring(SIZE_COMMAND_REPLY_CODE.length());
long actualFtpSize = Long.parseLong(sizeSubStr);
return actualFtpSize;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
public void close() {
try {
if (is != null) {
LOG.info(String.format("already read %d bytes from ftp file %s", is.getCount(), fileName));
is.close();
}
if (ftpClient != null) {
// Must call completePendingCommand() to finish command.
boolean isSuccessTransfer = ftpClient.completePendingCommand();
if (!isSuccessTransfer) {
LOG.error("error happened when complete transfer of ftp");
}
ftpClient.logout();
ftpClient.disconnect();
}
} catch (Throwable e) {
e.printStackTrace();
LOG.error(String.format("Close ftp input failed:%s,%s", e.getMessage(), e.getCause()));
} finally {
is = null;
ftpClient = null;
}
}
public void validInputComplete() {
Preconditions.checkArgument(processedBytesNum == completeFileSize, "ftp file transfer is not complete");
}
/**
* main
*
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String ip = "***.***.***.****";
int port = 21;
String user = "***";
String passwd = "***";
String path = "/home/work";
String fileName = "b.txt";
FtpInput input = new FtpInput();
try {
input.fileName = fileName;
input.path = path;
input.ip = ip;
input.port = port;
input.user = user;
input.passwd = passwd;
// connect to FTP server
input.connect();
while (true) {
// read bytes
byte[] bytes = input.readBytes();
if (bytes == null) {
break;
}
LOG.info("read " + bytes.length + " bytes at :" + new Date(System.currentTimeMillis()));
// Attention: this is used for simulating the process of writing data into hive table
// it maybe consume more than 1 minute;
Thread.sleep(3000);
}
input.validInputComplete();
} catch (Exception e) {
e.printStackTrace();
} finally {
input.close();
}
}
}
here is the exception message:
java.net.SocketTimeoutException: Read timed out
or
java.net.SocketException: Connection reset
at stream.readBytes in method readBytesFromStream
At first, i think it probably caused by writing into hive table slowly, and then the FTP Server closed the connection.
But actually, the speed of writing into hive table is fast enough.
Now, i need your help, how can i fix this problem.
From your comments, it looks like it can take hours before you finish downloading the file.
You cannot reasonably expect an FTP server to wait for you for hours to finish the transfer. Particularly if you are not transferring anything most of the time. You waste server resources and most servers will protect themselves against such abuse.
Your design is flawed.
You should redesign your application to first fully download the file; and import the file only after the download finishes.
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 making a multiplayer game in java, and I'm really have some issues here. For some reason, my Datagram socket will stop receiving packets set from the client even though according to the console, it recieved them perfectly fine before. I don't know much about networking or programming with sockets, so this has me stumped.
Here's my code:
Server:
package server;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import shared.BaseEntity;
import shared.MethodNotOverridenException;
import shared.Packet;
import shared.Player;
public class S_GameLoop implements Runnable{
//Constants
private static final int TICKS_PER_SECOND = 25;
private static final int TICKS_TO_SKIP = 1000 / TICKS_PER_SECOND;
private static final int MAX_FRAMESKIP = 5;
private static final int SPAWN_COORD_X = 50;
private static final int SPAWN_COORD_Y = 50;
private Vector<S_Client> ClientList;
private Map<Integer, BaseEntity> EntityMap;
private AtomicInteger IDGenerator;
private boolean gameIsRunning = true;
private DatagramSocket socket;
byte[] recieveData = new byte[1024];
byte[] prevRecieveData = new byte[1024];
private int port;
private int loops;
public S_GameLoop(int port) {
ClientList = new Vector<S_Client>();
this.port = port;
IDGenerator = new AtomicInteger();
EntityMap = new HashMap<Integer, BaseEntity>();
}
private void Init() throws SocketException {
System.out.println("INIT");
socket = new DatagramSocket(port);
System.out.println("[Server] Create listen server on " + socket.getLocalAddress().getHostAddress() + " on port " + socket.getLocalPort());
socket.setSoTimeout(0);
}
private void Running() {
System.out.println("S_GameLoop staring");
long nextGameTick = System.currentTimeMillis();
DatagramPacket recievePacket = new DatagramPacket(recieveData, recieveData.length);
//GameLoop goes here
while(gameIsRunning) {
loops = 0;
//Receive the data
try {
socket.receive(recievePacket);
} catch (IOException e1) {
e1.printStackTrace();
}
while(System.currentTimeMillis() > nextGameTick && loops < MAX_FRAMESKIP) {
try {
Update(recievePacket);
} catch (MethodNotOverridenException | IOException e) {
System.err.println(e);
}
nextGameTick += TICKS_TO_SKIP;
loops++;
}
nextGameTick += TICKS_TO_SKIP;
loops++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void Update(DatagramPacket recievePacket) throws IOException, MethodNotOverridenException {
if(prevRecieveData != recieveData) {
parseData(formatData(recievePacket.getData()), recievePacket);
prevRecieveData = recieveData;
} else {
return;
}
}
public static Packet formatData(byte[] data) {
try {
if(data[1] == 0);
String tag = new String(Arrays.copyOfRange(data, 1, 8));
System.out.println("[Server] Recieved packet " + data[0] + " " + new String(tag));
return new Packet(data[0],tag.getBytes(), Arrays.copyOfRange(data, 8, data.length));
} catch (ArrayIndexOutOfBoundsException e) {
return new Packet((byte)0, new byte[0], new byte[0]);
}
}
private void parseData(Packet p, DatagramPacket recievePacket) throws IOException, MethodNotOverridenException {
if(p.getTag() == new byte[0]) {
System.out.println("[Server] Recieved NULL packet");
return;
}
switch (p.getTagAsString()) {
case "LGN_RQS": System.out.println("[Server] Login Request Recieved");
//Login was accepted
//Create a Client ref, and add it to the vector
S_Client newClient = new S_Client(recievePacket.getAddress(), recievePacket.getPort());
ClientList.add(newClient);
//Create a player and add it to Entity list
Player newPlayer = new Player(IDGenerator.getAndIncrement(), ClientList.indexOf(newClient));
EntityMap.put(newPlayer.getEntID(), newPlayer);
System.out.println("[Server] Created new Player with EID " + newPlayer.getEntID() + " and CID " + newPlayer.getCID());
//Send reply to Client that is logging in
sendData(new Packet((byte)2, "LGN_ACP".getBytes(), ("CID;" + ClientList.indexOf(newClient) + ";EID;" + newPlayer.getEntID()).getBytes()).getBytes(), newClient.getIp(), newClient.getPort());
//New Entity was created
//sendData(newPlayer.onCreate(this));
break;
case "HND_SHK": System.out.println("[Server] Handshake Recieved");
String fdata = new String(p.getData());
String[] data = fdata.split(";");
//Get client by client ID
S_Client c = ClientList.get(Integer.parseInt(data[1]));
c.setUsername(data[3]);
System.out.println("[Server] Set Client " + data[1] + "'s username to " + data[3]);
//Now spawn the player
sendData(new Packet((byte)9, "PLR_SPW".getBytes(), ("CID;" + data[1] + ";X;" + SPAWN_COORD_X + ";Y;" + SPAWN_COORD_Y).getBytes()).getBytes());
break;
}
}
public String[] byteArrayToStringArray(byte[] b) {
String fdata = new String(b);
return fdata.split(";");
}
public void createEntity(BaseEntity be) throws MethodNotOverridenException, IOException {
int ID = IDGenerator.incrementAndGet();
be.setEntID(ID);
EntityMap.put(ID, be);
sendData(be.onCreate(this));
}
public void sendData(byte[] sendData, InetAddress IP, int port) throws IOException {
System.out.println("[Server] Send packet " + sendData[0] + " " + new String(Arrays.copyOfRange(sendData, 1, 8)));
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IP, port);
socket.send(sendPacket);
}
public void sendData(byte[] sendData) throws IOException {
for (S_Client entry : ClientList) {
sendData(sendData, entry.getIp(), entry.getPort());
}
}
#Override
public void run() {
try {
Init();
Running();
} catch (SocketException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
}
Client:
package client;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.Vector;
import shared.BaseEntity;
import shared.Packet;
import shared.Player;
public class C_GameLoop {
//How fast should the game update?
private static final int TICKS_PER_SECOND = 25;
//How many ticks should be skipped in order to run it at that speed?
//If it runs faster, then the Render() will be called more often
private static final int TICKS_TO_SKIP = 1000 / TICKS_PER_SECOND;
//How many times should you skip rendering, if you have to?
private static final int MAX_FRAMESKIP = 5;
RenderCanvas rc;
InetAddress ServerIP;
private DatagramSocket socket;
private int port;
private boolean connectedToServer = false;
private boolean waitForData = false;
private String username = "Dummy";
byte[] sendPacket;
Vector<BaseEntity> RenderList;
Player player;
int fps = 0;
public C_GameLoop(RenderCanvas rc){
this.rc = rc;
RenderList = new Vector<BaseEntity>();
}
public boolean Connect(InetAddress ServerIP, int port) throws IOException {
this.port = port;
this.ServerIP = ServerIP;
socket = new DatagramSocket();
socket.setSoTimeout(4000);
DatagramPacket recieveData = new DatagramPacket(new byte[1024], new byte[1024].length);
System.out.println("[Client] Connecting to: " + ServerIP.getHostAddress() + ":" + port);
//Send a login request
sendData(new Packet((byte)1, "LGN_RQS".getBytes()).getBytes());
int retries = 0;
//Start the connect loop
while(!connectedToServer) {
try {
socket.receive(recieveData);
waitForData = false;
parseData(formatData(recieveData.getData()));
//recieveData.setData(new Packet((byte)0, new byte[0], new byte[0]).getBytes());
} catch (SocketTimeoutException e) {
if(waitForData = true && retries <= 4) {
System.out.println("[Client] Failed to recieve response from server, retrying");
parseData(formatData(recieveData.getData()));
retries++;
} else {
System.out.println("[Client] Failed to Connect to the server!");
return false;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
public void sendData(byte[] data) throws IOException {
System.out.println("[Client] Sent packet " + data[0] + " " + new String(Arrays.copyOfRange(data, 1, 8)));
DatagramPacket sendPacket = new DatagramPacket(data, data.length, ServerIP, port);
socket.send(sendPacket);
waitForData = true;
}
private void parseData(Packet p) throws IOException {
switch (p.getTagAsString()) {
case "LGN_ACP": System.out.println("[Client] Login Accepted");
//Get the data needed to create a new player from the packet
String fdata = new String(p.getData());
String[] data = fdata.split(";");
Player player = new Player(Integer.parseInt(data[1]), Integer.parseInt(data[3].trim()));
//Add it to the render list
this.player = player;
rc.getCamera().addDrawableEntity(player);
System.out.println("[Client] Player created with CID " + data[1] + " and EID " + data[3]);
//Send the handshake
System.out.println("[Client] Finshing Handshake...");
sendData(new Packet((byte)4, "HND_SHK".getBytes(), ("CID;" + player.getCID() + ";Username;" + username).getBytes()).getBytes());
break;
case "PLR_SPW": System.out.println("[Client] Spawn Recieved");
//Get the coords
String[] spawn_data = byteArrayToStringArray(p.getData());
this.player.setX(Integer.parseInt(spawn_data[3]));
this.player.setY(Integer.parseInt(spawn_data[5].trim()));
sendData(new Packet((byte)0, "KEP_ALV".getBytes()).getBytes());
break;
}
}
public String[] byteArrayToStringArray(byte[] b) {
String fdata = new String(b);
return fdata.split(";");
}
/*
* Formats data into a packet
*/
public static Packet formatData(byte[] data) {
try {
if(data[1] == 0);
String tag = new String(Arrays.copyOfRange(data, 1, 8));
System.out.println("[Client] Recieved packet " + data[0] + " " + new String(tag));
return new Packet(data[0],tag.getBytes(), Arrays.copyOfRange(data, 8, data.length));
} catch (ArrayIndexOutOfBoundsException e) {
return new Packet((byte)0, new byte[0], new byte[0]);
}
}
}
Console output when running code:
INIT
[Server] Create listen server on 0.0.0.0 on port 4334
S_GameLoop staring
[Client] Connecting to: 127.0.0.1:4334
[Client] Sent packet 1 LGN_RQS
[Server] Recieved packet 1 LGN_RQS
[Server] Login Request Recieved
[Server] Created new Player with EID 0 and CID 0
[Server] Send packet 2 LGN_ACP
[Client] Recieved packet 2 LGN_ACP
[Client] Login Accepted
[Client] Player created with CID 0 and EID 0
[Client] Finshing Handshake...
[Client] Sent packet 4 HND_SHK
[Client] Failed to recieve response from server, retrying
(Same lines as previous 6 just repeat 4 times)
[Client] Failed to Connect to the server!
Any Ideas?
It looks like your Server class is designed to be used as the Runnable of some worker thread.
I suspect that the problem is that the run() method is dying due to an uncaught exception, and that the worker thread is simply terminating.
I recommend that you configure a default uncaught exception handler to (at least) log the stack trace for the fatal exception. Refer to the Thread javadocs for details.