How to receive the data from server example "cutting" in parcels of 1024 bytes, cause when the answer comes from server in packets like 2 parts I don't know how solve that.
Ex. When the first packet arrives the size informed by the server is 1988 and received is 1444, that it's ok, but when the second packet arrives the size informed is something like 808333347 and received 540, the sum of 1444 + 540 = 1984 that is right. I don't know where this number 808333347 is coming from.
I'm googling for this solution and all then teachs using udp and I need this for a tcp/ip connection.
The class:
public class Connection implements Runnable {
private static Connection instance;
private SocketChannel channel = null;
private int port = 0;
private int service = 0;
private final int SOCKET_TIMEOUT = 15 * 1000;
private final int SOCKET_BYTES = 16 * 1024;
private final Charset CHARSET = Charset.forName("ISO-8859-1");
private String host = null;
private String message = "";
public Connection(String host, String port){
this.host = host;
this.port = Integer.parseInt(port);
}
public static Connection createConnection(String host, String port) {
if(instance == null){
instance = new Conexao(host, port);
}
return instance;
}
public void connect(){
try{
instance.channel = SocketChannel.open();
instance.channel.socket().setSoTimeout(SOCKET_TIMEOUT);
instance.channel.socket().setTcpNoDelay(false);
instance.channel.socket().setKeepAlive(true);
instance.channel.connect(new InetSocketAddress(host, port));
instance.channel.configureBlocking(false);
} catch (IOException ioe){
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
}
#Override public void run() {
if(null != instance.channel){
if(instance.channel.isConnected()){
Log.d(TAG, "CHANNEL CONNECTED = TRUE");
} else {
Log.d(TAG, "CHANNEL CONNECTED = FALSE");
}
} else {
instance.connect();
Log.d(TAG, "CHANNEL CONNECTED");
}
sendMessage();
while(true){
receiveMessage();
}
}
public void sendMessage() {
int size = message.length();
ByteBuffer buffer = ByteBuffer.allocate(4 + 4 + size);
buffer.putInt(service).putInt(size).put(message.getBytes());
buffer.flip();
for(int i = 0; i < size; i++){
try {
instance.channel.write(buffer);
} catch (IOException ioe) {
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
}
}
public void receiveMessage(){
ByteBuffer buffer = ByteBuffer.allocateDirect(SOCKET_BYTES);
int bytesReaded = 0;
String received = "";
buffer.clear();
try {
do {
bytesReaded = instance.channel.read(buffer);
} while (bytesReaded == 0);
} catch (IOException ioe) {
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
buffer.flip();
int size = buffer.getInt();
received += CHARSET.decode(buffer);
Log.d(TAG,"SERVIÇE: " + size + "/" + received.length() + " MSG: " + received);
}
public int getService() {
return service;
}
public void setService(int service) {
this.service = service;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
I changed the function like this:
public void receiveMessage(){
ByteBuffer buffer = ByteBuffer.allocateDirect(SOCKET_BYTES);
int bytesReaded = 0;
String received = "";
buffer.clear();
try {
bytesReaded = instance.channel.read(buffer);
} catch (IOException ioe) {
Log.d(TAG, ioe.getMessage() + " " + ioe.toString());
}
buffer.flip();
if(bytesReaded >= 4){
if(size == 0 && size < 5000) size = buffer.getInt();
received += CHARSET.decode(buffer);
answer += received;
if(size == answer.length()){
Log.d(TAG,"SERVICE: " + size + "/" + answer.length() + " " + answer);
}
}
}
But it's very ugly now.
You can't control how data arrives on a TCP connection, from either end. It can arrive a byte at a time or in any other quanta up to the size of the data. You have to loop at the receiving end until you have everything you need. You need to use the same read buffer for the size of the socket so you can accumulate data for this loop, and to preserve any data that might belong to a subsequent message.
The 'size informed' cannot be as huge as you state. That only results from a bug. Probably you are getting out of sync.
The problem is that with TCP you have no control how the data is sent (fragmented). You do not know how much data is read with channel.read(...).
You call receiveMessage() in a loop, there you fill read data into a buffer.
You can not be sure that
int size = buffer.getInt();
is the size of the message you receive(only in the first call, if you receive at least 4 bytes.). You have to remember the first 4 received bytes, the size, (since you use getInt()), then you have to channel.read(...) until you have received size bytes, after that -> process the next message.
Also re-use your buffer. Since you use NIO (and non-blocking) i also suggest you to use select(), instead of busy reading.
Related
I'm working on some code to interact with a server and send a file in 1000 byte chunks. I want to use setSoTimeout to resend a packet after 5 seconds if I have not received an ACK from the server by then. I have searched for the answer to this but to no avail. Here are some links i checked out and attempted:
What is the functionality of setSoTimeout and how it works?
how to use socket.setSoTimeout()?
setSotimeout on a datagram socket
I am under the impression that when the timer is going you are continuously waiting for the ACK. Is this the case? I am never receiving an ACK from the server, although I was at one point before.
import java.net.*;
import java.io.*;
import java.util.*;
public class FTPClient {
Socket tcpSocket;
DatagramSocket udpSocket;
DataInputStream dataIn;
DataOutputStream dataOut;
BufferedReader br;
String fileName;
int time;
int portNum;
/**
* Constructor to initialize the program
*
* #param serverName server name
* #param server_port server port
* #param file_name name of file to transfer
* #param timeout Time out value (in milli-seconds).
*/
public FTPClient(String server_name, int server_port, String file_name, int timeout) {
System.out.println("Server Name: " + server_name + " Server Port: " + server_port
+ " File Name: " + file_name + " Timeout: " + timeout);
fileName = file_name;
time = timeout;
portNum = server_port;
try {
Socket tcpSocket = new Socket(server_name, server_port);
dataIn = new DataInputStream(tcpSocket.getInputStream());
dataOut = new DataOutputStream(tcpSocket.getOutputStream());
br = new BufferedReader(new InputStreamReader(System.in));
}
catch (Exception ex) {
System.out.println("Exception in FTPClient initialization: " + ex);
}
}
/**
*Send file content as Segments
*
*/
public void send() {
try {
File f = new File(fileName);
if (!f.exists()) {
System.out.println("File does not exist...");
return;
}
System.out.println("Sending filename (" + fileName + ") to server.");
dataOut.writeUTF(fileName);
byte msgFromServer = dataIn.readByte();
if (msgFromServer == 0) {
System.out.println("Server ready to receive file");
}
// Create a UDP socket to send the file to the server
DatagramSocket udpSocket = new DatagramSocket();
InetAddress IPAddress = InetAddress.getByName("localhost");
FileInputStream fileIn = new FileInputStream(f);
int seqNum = 0;
int i = 0;
Boolean received = false;;
byte[] chunks = new byte[1000];
int rc = fileIn.read(chunks);
while(rc != -1)
{
System.out.println("Iteration #: " + i);
System.out.println(rc);
// rc should contain the number of bytes read in this operation.
//if (rc < 1000) {
//System.out.println("Bytes read less than 1000");
//System.out.println("Sequence Number: " + seqNum);
//System.out.println("Packet too small to send");
//}
System.out.println("Bytes read greater than 1000");
System.out.println("Sequence Number: " + seqNum);
while (received == false) {
System.out.println("You are looping and sending again");
transferPacket(seqNum, IPAddress, chunks);
received = getResponse();
}
rc = fileIn.read(chunks);
if (seqNum == 1) {
seqNum = 0;
}
else {
seqNum = 1;
}
i++;
}
}
catch (Exception e) {
System.out.println("Error: " + e);
}
}
public Boolean getResponse() {
try {
DatagramSocket udpSocket = new DatagramSocket();
System.out.println("You are in getResponse()");
byte[] receiveData = new byte[1000];
udpSocket.setSoTimeout(time); // set timer
while (true) {
try {
System.out.println("You are receiving a packet");
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
udpSocket.receive(receivePacket);
Segment unwrap = new Segment(receivePacket);
int num = unwrap.getSeqNum();
System.out.println("Received ACK with Sequence Number: " + num);
return true;
}
catch (SocketTimeoutException t) {
System.out.println("Timeout: return false to send()");
return false;
}
}
}
catch (Exception e) {
System.out.println("You don't wanna be here");
return false;
}
}
public void transferPacket(int seqNum, InetAddress IPAddress, byte[] chunks) {
try {
DatagramSocket udpSocket = new DatagramSocket();
byte[] sendData = new byte[1000];
Segment s = new Segment(seqNum, chunks);
sendData = s.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, portNum);
udpSocket.send(sendPacket);
System.out.println("Sent Packet with sequence number " + seqNum);
}
catch (Exception e) {
System.out.println("Exception in transferPacket: " + e);
}
}
/**
* A simple test driver
*
*/
public static void main(String[] args) {
String server = "localhost";
String file_name = "";
int server_port = 8888;
int timeout = 5000; // milli-seconds (this value should not be changed)
// check for command line arguments
if (args.length == 3) {
// either provide 3 parameters
server = args[0];
server_port = Integer.parseInt(args[1]);
file_name = args[2];
}
else {
System.out.println("Wrong number of arguments, try again.");
System.out.println("Usage: java FTPClient server port file");
System.exit(0);
}
FTPClient ftp = new FTPClient(server, server_port, file_name, timeout);
System.out.printf("Sending file \'%s\' to server...\n", file_name);
try {
ftp.send();
}
catch (Exception e) {
System.out.println("Exception: " + e);
}
System.out.println("File transfer completed.");
}
}
You need to keep using the same UDP socket for the life of the application, not a new one per packet, and use it for both sending and receiving.
At present you are also leaking UDP sockets like a firehose.
Im having a problem that i can't solve by my own. I think my approach with splitting and adding to a array list and reassembling the message pieces in the end works great if (1) the MSG > BUFFER & message rate is 1msg/second. But the problems appear when i send more than 1 message/second and i have to split a large/small message. Yes the approach may be inefficient in the long run, but this is an assignment so i just want to get it working as i wanted and i'm fine by that.
I am pretty sure the problem is that it of course send every message pieces rate times. And the output is like this in my console:
--------------------------------
| UDP Echo Client
| Configuration:
| server name: localhost
| port: 4950
| buffer: 8
| rate: 5
| message size: 15
--------------------------------
Original: [HelloHe, lloHell, o]
Received: [HelloHe]
MESSAGE IS NOT EQUAL!
Received: [HelloHe, HelloHe]
MESSAGE IS NOT EQUAL!
Received: [HelloHe, HelloHe, HelloHe]
MESSAGE IS NOT EQUAL!
Can anyone please try to help me ? What is the best way to fix this ?
UDP Client:
import java.io.IOException;
import java.net.*;
import java.util.*;
/*
UDP Echo client. Sends a echo message of a size to the server and gets it back.
It checks so that the message wasn't lost or anything has happened to it.
by jv222dp
Rate works perfectly when MSG.length <= MY_BUFFER.
When the BUFFER is smaller then the MSG it works great if rate is 1
*/
public class UDPEchoClient {
private static final String MSG = "HelloHelloHello";
private static int MY_PORT;
private static int RATE;
private static int MY_BUFFER;
private static String HOST_NAME;
private static byte[] buf;
private static int packages;
private static int chars;
private static List<String> originalMsg;
private static List<String> receivedString = new ArrayList<>(packages);
private static DatagramPacket sendPacket;
public static void main(String[] args) {
if (!isCorrect(args)) {
System.exit(1);
} else {
try {
/* Configuration printout */
System.out.println("--------------------------------" +
"\n| UDP Echo Client" +
"\n| Configuration: " +
"\n| server name: " + HOST_NAME +
"\n| port: " + MY_PORT +
"\n| buffer: " + MY_BUFFER +
"\n| rate: " + RATE +
"\n| message size: "+MSG.length()+
"\n--------------------------------");
/* Sets the buffer */
buf = new byte[MY_BUFFER];
/* Create socket */
DatagramSocket socket = new DatagramSocket(null);
/* Create local endpoint using bind() */
SocketAddress localBindPoint = new InetSocketAddress(0);
socket.bind(localBindPoint);
socket.setSoTimeout(2000);
/* Create remote endpoint */
SocketAddress remoteBindPoint = new InetSocketAddress(HOST_NAME,
(MY_PORT));
/* Sends and reads the echo message */
sendEchoPackets(socket, remoteBindPoint);
} catch (SocketException se) {
System.err.println("Host unreachable!" +
"Wrong port or host offline");
}
}
}
public static void sendEchoPackets(DatagramSocket socket, SocketAddress remoteBindPoint) {
System.out.println("Original: "+originalMsg.toString());
/* For each string in the List of message parts */
for (String message : originalMsg) {
/* Create datagram packet for sending message */
sendPacket = new DatagramPacket(
message.getBytes(),
message.length(),
remoteBindPoint);
Timer timer = new Timer();
TimerTask rate = new TimerTask() {
#Override
public void run() {
try {
if (RATE == 0 || RATE == 1) {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
} else {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
};
timer.scheduleAtFixedRate(rate, 0, 1000);
readEchoPacket(socket);
}
}
public static void readEchoPacket(DatagramSocket socket){
try {
/* Create datagram packet for receiving echoed message */
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
socket.receive(receivePacket);
String receivedEcho = new String(
receivePacket.getData(),
receivePacket.getOffset(),
receivePacket.getLength());
receivedString.add(receivedEcho);
/* Compares if the message is the same as the one that was sent */
compareEchoMessage(receivedString);
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void compareEchoMessage(List<String> receivedMsg){
StringBuilder sb = new StringBuilder();
for (String str : receivedMsg) {
sb.append(str);
}
System.out.println("Received: "+receivedMsg.toString());
if (sb.toString().compareTo(MSG) == 0){
System.out.printf("%s bytes sent and received!",sb.length());
}
else{
System.out.println("MESSAGE IS NOT EQUAL!");
}
}
/* Splits the message equally */
private static ArrayList<String> splitMessage(String message, int chunks) {
/* */
ArrayList<String> packages = new ArrayList<>(
(message.length() + chunks) - 1 / chunks);
for (int i = 0; i < message.length(); i += chunks){
packages.add(message.substring(i, Math.min(message.length(),
i + chunks)));
}
return packages;
}
public static boolean isCorrect(String[] args) {
/* Make sure all arguments are present */
if (args.length != 4 && args.length == 0) {
printUsage();
return false;
}
else
try {
HOST_NAME = args[0];
MY_PORT = Integer.parseInt(args[1]);
MY_BUFFER = Integer.parseInt(args[2]);
RATE = Integer.parseInt(args[3]);
/* Ensures RATE is not too high with a tested limit of 3000 */
if (RATE > 3000) {
System.err.println("Rate value is too large!");
return false;
}
/* Make sure the host is valid */
if (!isValidHost(HOST_NAME)) {
System.err.println("Host address is not valid!" +
"\nRequires a valid IP address or just localhost");
return false;
}
/* Make sure the port number is in the valid range */
if (MY_PORT <= 0 || MY_PORT >= 65536) {
System.err.println("Port value must be in (0 -> 65535)!");
return false;
}
/* Make sure the buffer is at least 2, not lower */
if (MY_BUFFER < 2){
System.err.println("Buffer must be higher or equal to 2!");
return false;
}
/* Split the message if bigger than buffer to appropriate packages */
if (MSG.length() > MY_BUFFER) {
packages = (int) Math.ceil((double) MSG.length() / MY_BUFFER);
chars = (MSG.length() / packages);
originalMsg = splitMessage(MSG, chars);
}
/* Else adds whole message to array list */
else {
packages = (int) Math.ceil( (double)MSG.length() / MY_BUFFER);
chars = (MSG.length() / packages);
originalMsg = splitMessage(MSG, chars);
}
}
catch (IndexOutOfBoundsException e) {
printUsage();
System.exit(1);
}
catch (NumberFormatException n) {
System.err.println("Invalid arguments!");
printUsage();
System.exit(1);
}
/* Everything is valid */
return true;
}
private static boolean isValidHost(String host) {
/* Check if the string is valid */
if (host == null || host.length() < 7 || host.length() > 15){
return false;
}
else
/* Host is valid "localhost" */
if (host.equals("localhost")){
return true;
}
/* Check the host string, should be in x.x.x.x format */
StringTokenizer token = new StringTokenizer(host,".");
if (token.countTokens() != 4)
return false;
while (token.hasMoreTokens()) {
/* Get current token and convert to an integer value */
String ip = token.nextToken();
try {
int ipVal = Integer.valueOf(ip).intValue();
if ( ipVal < 0 || ipVal > 255)
return false;
}
catch (NumberFormatException ex) {
return false;
}
}
/* IP Address looks valid */
return true;
}
private static void printUsage() {
System.err.println("Input arguments did not match expected arguments!" +
"\nUsage: \"<host_name> <port> <message_buffer> <message_rate>\"");
}
}
UDP Server:
/*
UDPEchoServer.java
A simple echo server with no error handling
*/
import java.io.IOException;
import java.net.*;
public class UDPEchoServer {
public static final int BUFSIZE = 1024;
public static final int MYPORT = 4950;
public static boolean running = true;
public static void main(String[] args) {
byte[] buf = new byte[BUFSIZE];
try{
/* Create socket */
DatagramSocket socket = new DatagramSocket(null);
/* Create local bind point */
SocketAddress localBindPoint = new InetSocketAddress(MYPORT);
socket.bind(localBindPoint);
System.out.println("---------------------------------"+
"\n| UDP Echo Server"+
"\n| Configuration: "+
"\n| port: "+MYPORT+
"\n---------------------------------");
while (running) {
/* Create datagram packet for receiving message */
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
/* Receiving message */
socket.receive(receivePacket);
/* Create datagram packet for sending message */
DatagramPacket sendPacket =
new DatagramPacket(receivePacket.getData(),
receivePacket.getLength(),
receivePacket.getAddress(),
receivePacket.getPort());
String echo = new String(receivePacket.getData(),
receivePacket.getOffset(), receivePacket.getLength());
System.out.printf("UDP echo request from %s", receivePacket.getAddress().getHostAddress());
System.out.printf(" using port %d\n", receivePacket.getPort());
System.out.println("Received: "+echo);
/* Send message*/
socket.send(sendPacket);
}
}
catch (SocketException s){
System.err.println(s.getMessage());
}
catch (IOException e){
System.err.println(e.getMessage());
}
}
}
Let's see what happens when your rate is 5:
This is the timer body:
if (RATE == 0 || RATE == 1) {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
} else {
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
}
So, the if condition is false, because the rate is neither 0 nor 1. We go to the else:
for (int i = 0; i < RATE; i++) {
socket.send(sendPacket);
timer.cancel();
}
For RATE = 5, this is like writing:
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
socket.send(sendPacket);
timer.cancel();
Of course, cancelling the timer five times doesn't have any effect, but it is sending the same packet 5 times one-by-one. It will then send the next part 5 times, and the third part 5 times, because you are creating three separate timers for the parts.
I think if you want to send at a rate of 5 datagrams per second (is that the meaning of the rate?), you should not create as many timers as there are parts. You should create one timer, give it a list of datagrams to send, and set its schedule period to 1000L / rate (make sure rate is not zero!). The timer should pop the next datagram from the list and send it. If there is no datagram left in the list, it should cancel itself.
One loop to fill the list with datagrams
Assign list to final variable which can be used from anonymous class, or to a field.
Create timer and run it with 1000L / rate schedule time.
Run second loop for reading and comparing the received echo datagrams.
Note the two separate loops!
About re-assembling the datagrams
First, please note that the DatagramPacket that you receive from the server is not the same DatagramPacket that you sent to it even if the contents are the same! They are two different objects, and the equals() method from Object is not overridden, meaning that for any two objects a and b of type DatagramPacket, a.equals(b) is equivalent to a == b.
This means that the only thing you can compare is the datagram content, not the datagram object.
Since UDP does not guarantee that the packets will be sent in any particular order, you have to take care of this yourself. This usually means that you have to include more information in the datagram payload than just the string content. A good place to start is adding a byte that represents the number of the part.
For example, suppose you want to send the message "ABCDEF" in two packets containing "ABC" and "DEF". What you are doing now is sending something like:
┌──┬──┬──┐
│65│66│67│
└──┴──┴──┘
┌──┬──┬──┐
│68│69│70│
└──┴──┴──┘
Now you might get that back as
┌──┬──┬──┐
│68│69│70│
└──┴──┴──┘
┌──┬──┬──┐
│65│66│67│
└──┴──┴──┘
And you have no way to know that, and you'll re-assemble it and it will be DEFABC.
But if you sent another byte that gives the order:
┌─┬──┬──┬──┐
│0│65│66│67│
└─┴──┴──┴──┘
┌─┬──┬──┬──┐
│1│68│69│70│
└─┴──┴──┴──┘
you'd get the first byte, convert it to integer, and convert the rest of the bytes to string. Then you can put it in the list using the index, and they will come out in the proper order, no matter if you got the 1 packet first or second.
In a real world situation, you'll also send the size (number of packets) and an identifying number (so that if you get some rogue datagrams that belong to an old communication that only just arrived, they won't get mixed into your re-assembled payload.
After browsing some other threads regarding my problem I think I've understood that I need to re-design my application. But just for clarification: I have a single TCP/IP connection between a client and a server. On the client side there are a number of threads running concurrently. Randomly one or more of these threads use the TCP/IP connection to communicate with the server. I've found out that, e. g. While a long running file transfer is active, using the connection with another thread concurrently might lead to errors. Though I've preceeded each message with a specific header including the data length it appears to me that the IP stack sometimes delivers a mix of more than one messages to my program, which means that though one message has net yet been delivered completely, part of another message is delivered to my read method. Is this a correct observation which matches the intended TCP/IP behaviour? Thanks in advance - Mario
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
For anybody who's interested: following is the source code of my test program. You may play with various values for the BUFFER_SIZE and the number of THREADS used to bombard the server socket with concurrent TCP/IP sends using the same socket. I've left out some error handling and removed a more sophisticated termination including the closing of the sockets. Test with a BUFFER_SIZE greater than 64KB always leads to errors on my machine.
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
public class TCPTest
{
private final static String INPUT_FILE = "c:/temp/tcptest.in";
private final static int BUFFER_SIZE = 64 * 1024 - 8; //65536;
private final static int MESSAGE_SIZE = 512 * 64 * 1024;
private final static int THREADS = 3;
private final static int SIZE_OF_INT = 4;
private final static int LENGTH_SIZE = SIZE_OF_INT;
private final static int ID_SIZE = SIZE_OF_INT;
private final static int HEADER_SIZE = LENGTH_SIZE + ID_SIZE;
private final static String NEW_LINE = System.getProperty("line.separator");
private ServerSocket m_serverSocket = null;
private Socket m_clientSocket = null;
private int m_iThreadCounter;
public static void main(String[] args)
{
new TCPTest();
} // main
public TCPTest()
{
final String id = "ReaderThread[*]";
// start a new thread creating a server socket waiting for connections
new Thread(new Runnable()
{
public void run()
{
try
{
// create server socket and accept client requests
m_serverSocket = new ServerSocket(12345);
m_clientSocket = m_serverSocket.accept();
// client request => prepare and read data
long startTime = System.currentTimeMillis();
byte[] buffer = new byte[BUFFER_SIZE];
ByteBuffer header = ByteBuffer.allocate(HEADER_SIZE);
int iTotalBytesRead = 0;
boolean fTerminate = false;
int iBytesRead;
// get hold of socket's input stream
InputStream clientInputStream = m_clientSocket.getInputStream();
// loop
while (false == fTerminate)
{
// loop to read next header
for (int i = 0; i < HEADER_SIZE; i++)
clientInputStream.read(header.array(), i, 1);
header.rewind();
// get information of interest
int iLength = header.getInt();
int iId = header.getInt();
int iLengthSoFar = 0;
int iBytesLeft = iLength;
int iBytesToRead;
// any length given?
if ((0 < iLength) && (BUFFER_SIZE >= iLength))
{
// that's the case => read complete message
while (iLengthSoFar < iLength)
{
// calculate number of bytes left
iBytesLeft = iLength - iLengthSoFar;
// calculate maximum number of bytes to read
if (iBytesLeft > BUFFER_SIZE)
iBytesToRead = BUFFER_SIZE;
else
iBytesToRead = iBytesLeft;
// read next portion of bytes
if ((iBytesRead = clientInputStream.read(buffer, 0, iBytesToRead)) != -1)
{
// maintain statistics
iTotalBytesRead += iBytesRead;
iLengthSoFar += iBytesRead;
} // if
else
{
// finish => print message
System.out.println("==> "+id+": ERROR length=<-1> received " +
"for id=<"+iId+">");
fTerminate = true;
break;
} // else
} // while
} // if
else
{
System.out.println("==> "+id+": ERROR data length <= 0 for id=<"+iId+">");
dump(header, 0, HEADER_SIZE / SIZE_OF_INT, "Error header");
} // else
} // while
System.out.println("==> "+id+": "+ iTotalBytesRead + " bytes read in "
+ (System.currentTimeMillis() - startTime) + " ms.");
} // try
catch (IOException e)
{
e.printStackTrace();
} // catch
} // run
}).start();
// create the socket writer threads
try
{
// ensure server is brought up and request a connection
Thread.sleep(1000);
System.out.println("==> "+id+": just awoke");
Socket socket = new Socket("localhost", 12345);
OutputStream socketOutputStream = socket.getOutputStream();
System.out.println("==> "+id+": socket obtained");
// create some writer threads
for (int i = 0; i < THREADS; i++)
// create a new socket writer and start the thread
(new SocketWriter(socket,
(i+1),
BUFFER_SIZE,
new String("WriterThread["+(i+1)+"]"),
socketOutputStream)).start();
} // try
catch (Exception e)
{
e.printStackTrace();
} // catch
} // TCPTestEx
private final static void dump(ByteBuffer bb, int iOffset, int iInts, String header)
{
System.out.println(header);
bb.rewind();
for (int i = 0; i < iInts; i++)
System.out.print(" " + Integer.toHexString(bb.getInt()).toUpperCase());
System.out.print(NEW_LINE);
} // dump
private class SocketWriter extends Thread
{
Socket m_socket;
int m_iId;
int m_iBufferSize;
String m_id;
OutputStream m_os;
protected SocketWriter(Socket socket, int iId, int iBufferSize, String id, OutputStream os)
{
m_socket = socket;
m_iId = iId;
m_iBufferSize = iBufferSize;
m_id = id;
m_os = os;
// increment thread counter
synchronized (m_serverSocket)
{
m_iThreadCounter++;
} // synchronized
} // SocketWriter
public final void run()
{
try
{
long startTime = System.currentTimeMillis();
ByteBuffer buffer = ByteBuffer.allocate(m_iBufferSize + HEADER_SIZE);
int iTotalBytesRead = 0;
int iNextMessageSize = 512 * m_iBufferSize;
int iBytesRead;
// open input stream for file to read and send
FileInputStream fileInputStream = new FileInputStream(INPUT_FILE);
System.out.println("==> "+m_id+": file input stream obtained");
// loop to read complete file
while (-1 != (iBytesRead = fileInputStream.read(buffer.array(), HEADER_SIZE, m_iBufferSize)))
{
// add length and id to buffer and write over TCP
buffer.putInt(0, iBytesRead);
buffer.putInt(LENGTH_SIZE, m_iId);
m_os.write(buffer.array(), 0, HEADER_SIZE + iBytesRead);
// maintain statistics and print message if so desired
iTotalBytesRead += iBytesRead;
if (iNextMessageSize <= iTotalBytesRead)
{
System.out.println("==> "+m_id+": <"+iTotalBytesRead+"> bytes processed");
iNextMessageSize += MESSAGE_SIZE;
} // if
} // while
// close my file input stream
fileInputStream.close();
System.out.println("==> "+m_id+": file input stream closed");
System.out.println("==> "+m_id+": <"+ iTotalBytesRead + "> bytes written in "
+ (System.currentTimeMillis() - startTime) + " ms.");
// decrement thread counter
synchronized (m_serverSocket)
{
m_iThreadCounter--;
// last thread?
if (0 >= m_iThreadCounter)
// that's the case => terminate
System.exit(0);
} // synchronized
} // try
catch (Exception e)
{
e.printStackTrace();
} // catch
} // run
} // SocketWriter
} // TCPTest
Yer. TCP is a byte oriented stream protocol. That means that the application receives an (undelimited) stream of bytes. The concept of "message" should be provided by the application (or use a message oriented protocol instead).
I am trying to playback audio and keep it continuous and free from skips or blank spots. I have to first receive as bytes in chunks and convert this to mp3 to be streamed by the servletOutputStream. I only start playing once enough bytes have been collected by the consumer in an attempt to maintain a constant flow of audio. As you can see I have hard coded this buffer but would like it to work for any size of audio bytes. I was wondering if anyone had come across a similar problem and had any advice?
Thanks in advance. Any help would be greatly appreciated.
public class Consumer extends Thread {
private MonitorClass consBuf;
private InputStream mp3InputStream = null;
private OutputStream OutputStream = null;
public Consumer (MonitorClass buf, OutputStream servlet)
{
consBuf = buf;
OutputStream = servlet;
}
public void run()
{
byte[] data;
byte[] tempbuf;
int byteSize = 60720; //This should be dynamic
int byteIncrement = byteSize;
int dataPlayed = 0;
int start = 0;
int buffer = 0;
boolean delay = true;
AudioFormat generatedTTSAudioFormat = getGeneratedAudioFormat();
try
{
while(true)
{
try
{
data = consBuf.get(); //gets data from producer using a shared monitor class
if(data.length >= byteSize) //Buffer size hit, start playing
{
if(delay) //help with buffering
{
System.out.println("Pre-delay...");
consBuf.preDelay();
delay = false;
}
tempbuf = new byte[byteIncrement];
arraySwap(data, tempbuf, start, byteSize);
System.out.println("Section to play: " + start + ", " + byteSize);
mp3InputStream = FishUtils.convertToMP3( new ByteArrayInputStream(tempbuf), generatedTTSAudioFormat);
copyStream(mp3InputStream, OutputStream);
System.out.println("Data played: " + byteSize);
System.out.println("Data collected: " + consBuf.getDownloadedBytes() );
dataPlayed = byteSize;
start = byteSize;
byteSize += byteIncrement;
}
if( consBuf.getIsComplete() )
{
if (consBuf.checkAllPlayed(dataPlayed) > 0)
{
System.out.println("Producer finished, play remaining section...");
//mp3InputStream = convertToMP3(new ByteArrayInputStream(tempbuf), generatedTTSAudioFormat);
//copyStream(mp3InputStream, OutputStream);
}
System.out.println("Complete!");
break;
}
}
catch (Exception e)
{
System.out.println(e);
return;
}
}
}
finally
{
if (null != mp3InputStream)
{
try
{
mp3InputStream.skip(Long.MAX_VALUE);
}
catch (Exception e)
{}
}
closeStream(mp3InputStream);
closeStream(OutputStream);
}
}
}
I am aware of the fact that the following code may seem vulgar, but I am new to these things and just tried everything in order to get it to work..
Problem: Even though I am using (possible in a wrong way) a CyclicBarrier, one - and seems to always be the same - thread stops too soon and prints out his vector, leaving 1 out of 11 of those "Incoming connection" messages absent. There is probably something terribly wrong with the last iteration of my loop, but I can't seem to find what exactly.. Now the program just loops waiting to process the last connection.
public class VectorClockClient implements Runnable {
/*
* Attributes
*/
/*
* The client number is store to provide fast
* array access when, for example, a thread's own
* clock simply needs to be incremented.
*/
private int clientNumber;
private File configFile, inputFile;
int[] vectorClock;
/*
* Constructor
* #param
* - File config
* - int line
* - File input
* - int clients
*/
public VectorClockClient(File config, int line, File input, int clients) {
/*
* Make sure that File handles aren't null and that
* the line number is valid.
*/
if (config != null && line >= 0 && input != null) {
configFile = config;
inputFile = input;
clientNumber = line;
/*
* Set the array size to the number of lines found in the
* config file and initialize with zero values.
*/
vectorClock = new int[clients];
for (int i = 0; i < vectorClock.length; i++) {
vectorClock[i] = 0;
}
}
}
private int parsePort() {
int returnable = 0;
try {
FileInputStream fstream = new FileInputStream(configFile.getName());
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine = "";
for (int i = 0; i < clientNumber + 1; i++) {
strLine = br.readLine();
}
String[] tokens = strLine.split(" ");
returnable = Integer.parseInt(tokens[1]);
}
catch (Exception e) {
e.printStackTrace();
}
System.out.println("[" + clientNumber + "] returned with " + returnable + ".");
return returnable;
}
private int parsePort(int client) {
int returnable = 0;
try {
FileInputStream fstream = new FileInputStream(configFile.getName());
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine = "";
for (int i = 0; i < client; i++) {
strLine = br.readLine();
}
String[] tokens = strLine.split(" ");
returnable = Integer.parseInt(tokens[1]);
}
catch (Exception e) {
e.printStackTrace();
}
return returnable;
}
private int parseAction(String s) {
int returnable = -1;
try {
FileInputStream fstream = new FileInputStream(configFile.getName());
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String[] tokens = s.split(" ");
if (!(Integer.parseInt(tokens[0]) == this.clientNumber + 1)) {
return -1;
}
else {
if (tokens[1].equals("L")) {
vectorClock[clientNumber] += Integer.parseInt(tokens[2]);
}
else {
returnable = Integer.parseInt(tokens[2]);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return returnable;
}
/*
* Do the actual work.
*/
public void run() {
try {
InitClients.barrier.await();
}
catch (Exception e) {
System.out.println(e);
}
int port = parsePort();
String hostname = "localhost";
String strLine;
ServerSocketChannel ssc;
SocketChannel sc;
FileInputStream fstream;
DataInputStream in;
BufferedReader br;
boolean eof = false;
try {
ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(hostname, port));
ssc.configureBlocking(false);
fstream = new FileInputStream("input_vector.txt");
in = new DataInputStream(fstream);
br = new BufferedReader(new InputStreamReader(in));
try {
InitClients.barrier.await();
}
catch (Exception e) {
System.out.println(e);
}
while (true && (eof == false)) {
sc = ssc.accept();
if (sc == null) {
if ((strLine = br.readLine()) != null) {
int result = parseAction(strLine);
if (result >= 0) {
//System.out.println("[" + (clientNumber + 1)
//+ "] Send a message to " + result + ".");
try {
SocketChannel client = SocketChannel.open();
client.configureBlocking(true);
client.connect(
new InetSocketAddress("localhost",
parsePort(result)));
//ByteBuffer buf = ByteBuffer.allocateDirect(32);
//buf.put((byte)0xFF);
//buf.flip();
//vectorClock[clientNumber] += 1;
//int numBytesWritten = client.write(buf);
String obj = Integer.toString(clientNumber+1);
ObjectOutputStream oos = new
ObjectOutputStream(
client.socket().getOutputStream());
oos.writeObject(obj);
oos.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
else {
eof = true;
}
}
else {
ObjectInputStream ois = new
ObjectInputStream(sc.socket().getInputStream());
String clientNumberString = (String)ois.readObject();
System.out.println("At {Client[" + (clientNumber + 1)
+ "]}Incoming connection from: "
+ sc.socket().getRemoteSocketAddress()
+ " from {Client[" + clientNumberString + "]}");
sc.close();
}
try {
InitClients.barrier.await();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
printVector();
}
private void printVector() {
System.out.print("{Client[" + (clientNumber + 1) + "]}{");
for (int i = 0; i < vectorClock.length; i++) {
System.out.print(vectorClock[i] + "\t");
}
System.out.println("}");
}
}
To clarify, here are the formats of the files used. Config contains hostnames and ports used by clients that are threads and input file's rows mean either "this client sends a message to that client" or "this client increments his logical clock by some constant value".
1 M 2 (M means sending a message)
2 M 3
3 M 4
2 L 7 (L means incrementing clock)
2 M 1
...
127.0.0.1 9000
127.0.0.1 9001
127.0.0.1 9002
127.0.0.1 9003
...
I would look at the logic related to when you are expecting an incoming socket connection. From your question it looks like you expect a certain number of incoming socket connections (potentially an incoming connection after every outgoing message?). Since you are using non-blocking I/O on the incoming socket it is always possible that your while statement loops before an incoming socket could be established. As a result, a thread would be able to continue and read the next line from the file without receiving a connection. Since your end state is reached once the end of the file is reached, it is possible that you may miss an incoming socket connection.
I would add some simple print outs that displays when you read from the file, when you send a message and when you receive an incoming connection. That should quickly tell you whether or not a particular thread is missing an expected incoming connection. If it turns out that the problem is due to the non-blocking I/O, then you may need to disable non-blocking I/O when you expect an incoming socket or implement a control that keeps track of how many incoming sockets you expect and continues until that goal is met.
Hope this helps.