In my application I'm create a socket to do communication between a client and a server. There are two types of messages are passing which they differ in-terms of the length of the data. One message type has only one byte, while the other one is of variable length. I was trying to use a single TCP connection to handle both situations, but so far failed. Could someone please tell me what would be the ideal approach for this. Is using two connections with different port numbers would be the best approach?
Note that It is impossible to use socket.io in my project due to external constraints.
here's the reading code i'm using:
Socket socket = new Socket( dstAddress, dstPort );
InputStream inputStream = socket.getInputStream();
try( ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream( 1024 ) )
{
byte[] buffer = new byte[1024];
int bytesRead;
while( ( bytesRead = inputStream.read( buffer ) ) != -1 )
{
byteArrayOutputStream.write( buffer, 0, bytesRead );
}
}
catch( Exception e )
{
e.printStackTrace();
}
Thanks
you need to design your own communication protocol, like http for example, but of course much simpler. and implement decoder/encoder on both sides
Related
I want to connect Android Device to external device via Socket. Socket Connect to external device successfully.
Now if any data require from external device then send request of byte packet data to socket below order. if external device receive data correct then send byte data in response.
Parameters : methodname(1 byte), payloadlength(2 byte), payload(2 byte).
Now My Code is...
Socket socket = new Socket("local exteranl device ip", 5000);
if(socket.isConnected()) {
int methodname = 5;
int payload = 2151;
int payloadLength = 2;
ByteBuffer buffer = ByteBuffer.allocate(3 + payloadLength); // 3 = for method name + length
buffer.order(ByteOrder.BIG_ENDIAN); // Just to be explicit
buffer.put((byte) methodname);
buffer.putShort((short) payloadLength);
buffer.putShort((short) payload);
buffer.rewind();
byte[] result = new byte[buffer.capacity()]; // Could also use result = buffer.array();
buffer.get(result);
DataOutputStream classOUTstream = new DataOutputStream(socket.getOutputStream());
// socket is already connected
classOUTstream.write(result);
classOUTstream.flush();
InputStream stream = socket.getInputStream();
byte[] data = new byte[100];
int count = stream.read(data);
}
Above Code is Android, i knowing only basic concept of java. i am getting -1 result in count.
can any one please suggest me or tell me my mistake?
You're doing this the hard way. Get rid of the ByteBuffer altogether and use all the methods of DataOutputStream. They are all big-endian. I can't see any mistake but clearly you must be sending something the peer didn't understand so he is closing the connection instead of sending a reply.
Note: Socket.isConnected() cannot possibly be false at the point you're testing it.
Hi I have created a server socket for reading byte array from socket using getInputStream, But getInputStream.read is not exiting after endof data reaches. Below is my code.
class imageReciver extends Thread {
private ServerSocket serverSocket;
InputStream in;
public imageReciver(int port) throws IOException
{
serverSocket = new ServerSocket(port);
}
public void run()
{
Socket server = null;
server = serverSocket.accept();
in = server.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buffer[] = new byte[1024];
while(true){
int s = 0;
s = in.read(buffer); //Not exiting from here
if(s<0) break;
baos.write(buffer, 0, s);
}
server.close();
return;
}
}
From the client if I sent 2048 bytes, the line in.read(buffer) should return -1 after reading two times, but it waiting there to read for the third time. How can I solve this ?
Thanks in advance....
Your server will need to close the connection, basically. If you're trying to send multiple "messages" over the same connection, you'll need some way to indicate the size/end of a message - e.g. length-prefixing or using a message delimiter. Remember that you're using a stream protocol - the abstraction is just that this is a stream of data; it's up to you to break it up as you see fit.
See the "network packets" in Marc Gravell's IO blog post for more information.
EDIT: Now that we know that you have an expected length, you probably want something like this:
int remainingBytes = expectedBytes;
while (remainingBytes > 0) {
int bytesRead = in.read(buffer, 0, Math.min(buffer.length, remainingBytes));
if (bytesRead < 0) {
throw new IOException("Unexpected end of data");
}
baos.write(buffer, 0, bytesRead);
remainingBytes -= bytesRead;
}
Note that this will also avoid overreading, i.e. if the server starts sending the next bit of data, we won't read into that.
If I send 2048 bytes, the line 'in.read(buffer)' should return -1 after reading two times.
You are mistaken on at least two counts here. If you send 2048 bytes, the line 'in.read(buffer)' should execute an indeterminate number of times, to read a total of 2048 bytes, and then block. It should only return -1 when the peer has closed the connection.
Im trying to send a packet back to the user informing them of all the people currently on the server, when they send a message to the server which has the word "who" in it.
Here is my code:
else if( response.contains( "who" ) )
{
System.out.println( "Size of names collection: "+names.size() );
buf = null;
buf = names.toString().getBytes();
int thisPort = packet.getPort();
packet = new DatagramPacket( buf, buf.length,packet.getAddress(),thisPort );
socket.send(packet);
}
The output of the print statement above is 2 indicating that there are two people on, for example andrew and james. Now when I package it up and send it I would expect it to output this:
[Andrew, James]
But instead the client gets:
[Andrew,
And thats it. Whats the problem? BTW I have to use UDP for this and can't switch to TCP
UPDATE
Here is the code in the client class that receives the packets:
while( true )
{
try
{
// Set the buf to 256 to receive data back from same address and port
buf = null;
buf = new byte[256];
packet = new DatagramPacket(buf, buf.length, address, 4445);
socket.receive(packet);
String response = new String( packet.getData() );
// Receive the packet back
System.out.println( response );
}
catch( IOException e )
{
}
}
Your datagram is being truncated to 256 byes because that's the size of the buffer you declared for the receiving DatagramPacket. If your datagrams can be longer, make the buffer bigger.
Best practice is to make it one bigger than the largest datagram you are expecting to receive. Then if you receive one that size you have an application protocol error.
You should check on both client and server the length of the DatagramPacket after the send/receive operation respectively (with the getLength method) to make sure it's the same, that would be the first hint. What Collection are you using for names?
Your question is incomplete. However..
UDP loses packets. That's why it's not reliable to use UDP for File Transfer purposes. The Adobe RTMFP uses UDP to transfer audio and video data in which many packets are lost., But audio/video content streaming is really faster when compared to TCP. I don't know if this answers your question, I just want to say that UDP does lose packets.
I have this TCP server running Because UDP server was able to accept large packets but ISP blocks my UDP packets even i have Public IP and Static services. But now decided to change it to TCP but i have a large stack now to replace with UDP to TCP.
Here is the server which is running but it does not at once receive a large packet, how can i increase it to unlimited or maximum size or etc?
1) Is there any way with this?
public void run()
{
while(true)
{
byte[] buf=new byte[5024]; <<< not helping!!
int bytes_read = 0;
try {
bytes_read = sockInput.read(buf, 0, buf.length);
String data = null;
data = new String(buf, 0, bytes_read);
System.out.println("[TCP]: incomeing data: " + bytes_read + " bytes, data=" +data);
}
}
2) Is there any way with this?
public TCPHandler(Socket sock) throws IOException
{
sock.setReceiveBufferSize(10*1024 +1024); //<<<<< not helping !!!
sock.setSendBufferSize(10*1024+1024);
this.sock = sock;
sockInput = sock.getInputStream();
sockOutput = sock.getOutputStream();
this.myThread = new Thread(this);
}
None is allowing me to handle large so that i can switch from UDP to TCP. Any ideas!!
TCP and UDP are different. TCP is not a packet-based protocol, it is a stream based protocol. Your data will arrive in order, and un-corrupted, but it will not necessarily all arrive at once.
What you should do is prefix each set 'packet' with a length. When you receive data, first read the length value, and then continue reading chunks from the socket until you have the amount of data required.
You shouldn't care how TCP splits your messages into packets and how many times you must read to get a full message. Just read until the end of the message has been reached, and then return all the bytes of the message.
Knowing when the message has been read completely depends on your protocol. You could choose to send only one message per connection, or you could send the length of the message before the message, or use some marker byte.
Note that you shouldn't use new String(byte[]) without specifying an encoding (and use the same when calling String.getBytes()), especially if you transfer from one machine to another, since both machines could have a different default encoding.
The trouble you're having is how InputStreams work. When you call read(buf,offset,length) it doesn't always read length bytes everytime. It simply reads what is available() on the InputStream, and returns the number of bytes it read. So you need to continue to call read until you've actually read length bytes.
int len = 0;
int expectedLength = sockInput.readInt();
byte[] buf = new byte[ expectedLength ];
while( len != buf.length ) {
len += sockInput.read( buf, len, buf.length );
}
// now you have a buffer with expectedLength data in it.
You can also look at using DataInputStream.readFully() by wrapping your
DataInputStream ds = new DataInputStream( sock.getInputStream() );
ds.readFully( buf, 0, buf.length );
http://docs.oracle.com/javase/6/docs/api/java/io/DataInputStream.html
But if you're really just after a String you should do this:
Reader reader = new InputStreamReader( sockInput.getInputStream(), "UTF-8" );
Same rules for read( char[], offset, length ) as read( byte[], offset, length )
A TCP is a stream of bytes you can get a portion of, one or more messages is a single read.
With UDP packets of more than 532 bytes, the packet can be broken up, so you have the same potential problem.
A simple way to encode message is to send the length first.
final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
final DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
public void writeMessage(String text) {
out.writeUTF(text);
}
public String readMessage() {
return in.readUTF();
}
read/write UTF sends the length first and followed by the content
I am writing a java TCP client that talks to a C server.
I have to alternate sends and receives between the two.
Here is my code.
The server sends the length of the binary msg(len) to client(java)
Client sends an "ok" string
Server sends the binary and client allocates a byte array of 'len' bytes to recieve it.
It again sends back an "ok".
step 1. works. I get "len" value. However the Client gets "send blocked" and the server waits to receive data.
Can anybody take a look.
In the try block I have defined:
Socket echoSocket = new Socket("192.168.178.20",2400);
OutputStream os = echoSocket.getOutputStream();
InputStream ins = echoSocket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
String fromPU = null;
if( (fromPU = br.readLine()) != null){
System.out.println("Pu returns as="+fromPU);
len = Integer.parseInt(fromPU.trim());
System.out.println("value of len from PU="+len);
byte[] str = "Ok\n".getBytes();
os.write(str, 0, str.length);
os.flush();
byte[] buffer = new byte[len];
int bytes;
StringBuilder curMsg = new StringBuilder();
bytes =ins.read(buffer);
System.out.println("bytes="+bytes);
curMsg.append(new String(buffer, 0, bytes));
System.out.println("ciphertext="+curMsg);
os.write(str, 0, str.length);
os.flush();
}
UPDATED:
Here is my code. At the moment, there is no recv or send blocking on either sides. However, both with Buffered Reader and DataInput Stream reader, I am unable to send the ok msg. At the server end, I get a large number of bytes instead of the 2 bytes for ok.
Socket echoSocket = new Socket("192.168.178.20",2400);
OutputStream os = echoSocket.getOutputStream();
InputStream ins = echoSocket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
DataInputStream dis = new DataInputStream(ins);
DataOutputStream dos = new DataOutputStream(os);
if( (fromPU = dis.readLine()) != null){
//if( (fromPU = br.readLine()) != null){
System.out.println("PU Server returns length as="+fromPU);
len = Integer.parseInt(fromPU.trim());
byte[] str = "Ok".getBytes();
System.out.println("str.length="+str.length);
dos.writeInt(str.length);
if (str.length > 0) {
dos.write(str, 0, str.length);
System.out.println("sent ok");
}
byte[] buffer = new byte[len];
int bytes;
StringBuilder curMsg = new StringBuilder();
bytes =ins.read(buffer);
System.out.println("bytes="+bytes);
curMsg.append(new String(buffer, 0, bytes));
System.out.println("binarytext="+curMsg);
dos.writeInt(str.length);
if (str.length > 0) {
dos.write(str, 0, str.length);
System.out.println("sent ok");
}
Using a BufferedReader around a stream and then trying to read binary data from the stream is a bad idea. I wouldn't be surprised if the server has actually sent all the data in one go, and the BufferedReader has read the binary data as well as the line that it's returned.
Are you in control of the protocol? If so, I suggest you change it to send the length of data as binary (e.g. a fixed 4 bytes) so that you don't need to work out how to switch between text and binary (which is basically a pain).
If you can't do that, you'll probably need to just read a byte at a time to start with until you see the byte representing \n, then convert what you've read into text, parse it, and then read the rest as a chunk. That's slightly inefficient (reading a byte at a time instead of reading a buffer at a time) but I'd imagine the amount of data being read at that point is pretty small.
Several thoughts:
len = Integer.parseInt(fromPU.trim());
You should check the given size against a maximum that makes some sense. Your server is unlikely to send a two gigabyte message to the client. (Maybe it will, but there might be a better design. :) You don't typically want to allocate however much memory a remote client asks you to allocate. That's a recipe for easy remote denial of service attacks.
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
/* ... */
bytes =ins.read(buffer);
Maybe your BufferedReader has sucked in too much data? (Does the server wait for the Ok before continuing?) Are you sure that you're allowed to read from the underlying InputStreamReader object after attaching a BufferedReader object?
Note that TCP is free to deliver your data in ten byte chunks over the next two weeks :) -- because encapsulation, differing hardware, and so forth makes it very difficult to tell the size of packets that will eventually be used between two peers, most applications that are looking for a specific amount of data will instead populate their buffers using code somewhat like this (stolen from Advanced Programming in the Unix Environment, an excellent book; pity the code is in C and your code is in Java, but the principle is the same):
ssize_t /* Read "n" bytes from a descriptor */
readn(int fd, void *ptr, size_t n)
{
size_t nleft;
ssize_t nread;
nleft = n;
while (nleft > 0) {
if ((nread = read(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount read so far */
} else if (nread == 0) {
break; /* EOF */
}
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
The point to take away is that filling your buffer might take one, ten, or one hundred calls to read(), and your code must be resilient against slight changes in network capabilities.