Reading all string from a server socket which has not been closed - java

I have a problem with socket programming in Java.
There is a server which has been written in python like this which I shouldn't not change.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send('from server\nnewline\0')
data = s.recv(BUFFER_SIZE)
s.close()
Now I want to write a code in Java which read the string from a server. Something like this:
public static String readStr(Socket client) throws IOException {
InputStreamReader inStream = new InputStreamReader(
client.getInputStream());
BufferedReader inBuff = new BufferedReader(inStream);
String answer = new String();
String str = inBuff.readLine();
while (str!=null) {
answer = answer.concat(str + "\n");
str = inBuff.readLine();
}
answer = answer.substring(0, answer.length() - 1);
System.out.println("answer:\n "+answer);
return answer;
}
But it seems that it blocks at line str = inBuff.readLine(); at the last line of the message. I tried the read() method but it was blocked too.

When designing a protocol over tcp, the best way is to include some kind of framer. This is done in the current protocol by the usage of a NUL byte.
When reading the data from the socket, you should first divide it into blocks/frames by some operations, before parsing the individual blocks.
A crude way to divide the packets into blocks is reading until you find a NUL byte, then returning that block as a byte array. (This is not the most efficient implementation)
public byte[] readPacket(InputStream in) throws IOException {
ByteArrayOutputStream tempStr = new ByteArrayOutputStream();
int read;
read=in.read();
while(read > 0){
tempStr.write(read);
read=in.read();
}
if(read == -1)
throw new EOFException();
return tempStr.toByteArray();
}
Because you now have proper frames for your data, you can now easily read the data:
byte[] frame = readPacket(in);
String[] lines = new String(frame, StandardCharsets.UTF8).split("\n");
// Do something with the lines

This is probably because the last line sent by the server does not end and readLine() method only returns when it reaches end of the line. Since you change the server's code. I recommend you use another method for reading from the stream. You may also use InputStreamReader class.

Apart from already mentioned inconsistent message/line ending - once with \n, second with \0, at the server there is no detection of end of the message. So the server will loop as long as the socket is not closed (or shut down for writing) at the client side. And as you have this line before closing the socket:
data = s.recv(BUFFER_SIZE)
in other words the client is waiting for some response from the server. But the server is stuck forever reading the message from the client in a loop.
So you need to either close the socket prior to that recv call or send some message (like empty line) and detect in on the server and eventually exit the loop.

Related

Why can't I run this tcp client socket code without \n?

I was trying this code. it works fine but if I remove \n in String str it doesn't work I mean It was able to compile without \n but it didn't give me output.
public class Test {
// Use try-with-resources to close a socket.
public static void main(String args[]) throws Exception {
int c;
// Create a socket connected to internic.net, port 43. Manage this
// socket with a try-with-resources block.
try (Socket s = new Socket("whois.internic.net", 43)) {
// Obtain input and output streams.
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
// Construct a request string.
String str = (args.length == 0 ? "MHProfessional.com" : args[0]) + "\n"; // <- herer
// Convert to bytes.
byte buf[] = str.getBytes();
// Send request.
out.write(buf);
// Read and display response.
while ((c = in.read()) != -1) {
System.out.print((char) c);
}
}
// The socket is now closed.
}
}
The server you're talking to reads data up until an end-of-line (\n) character -- that's just the way it works, but it's far from unusual. It's possible that other end-of-line sequences will be accepted as well.
The server has to have some way to know the client has finished sending data. It will probably know if the client closes its connection, but by then it's too late to respond to the client.

Use writeUTF and readUTF for http requests in Java

This is a a Java method that tries to crawl a designated web page. I am using writeUTF and readUTF for socket communications to a server.
static void get_html(String host, String page, int port) throws IOException {
Socket sock = new Socket(host, port);
String msg = MessageFormat.format("GET {0} HTTP/1.1\r\nHost: {1}\r\n\r\n", page, host);
DataOutputStream outToServer = new DataOutputStream(sock.getOutputStream());
DataInputStream inFromServer = new DataInputStream(sock.getInputStream());
InputStream stream = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8));
BufferedReader buf = new BufferedReader(new InputStreamReader(stream));
String outMsg;
while ((outMsg = buf.readLine()) != null) {
System.out.println("Sending message: " + outMsg);
outToServer.writeUTF(outMsg);
String inMsg;
try {
inMsg = inFromServer.readUTF();
} catch (EOFException eof) {
break;
}
System.out.println(inMsg);
}
sock.close();
}
The reason I am writing it this way was to mimic the c code, where you have a while loop of send() making all deliveries from a buffer, and another while loop of recv() from a buffer untill it hits 'null'. When execute my code, it just hangs there, I suspect that is due to a call of readUTF before I finished sending all my messages. If this is the case, is there any way to fix it?
You can't do this. HTTP is defined as text lines. writeUTF() does not write text, it writes a special format starting with a 16-bit binary length word. Similarly the HTTP server won't reply with that format into your readUTF() call. See the Javadoc.
You have to use binary streams and the write() method, with \r\n as the line terminator. Depending on the output format you may or may not be able to use readLine(). Best not, then you don't have to write two pieces of code: use binary streams again.
In fact you should throw it all away and use HttpURLConnection. Implementing HTTP is not as simple as may hastily be supposed.

readUTF hangs at server side

I'm implementing Client/Server File send and receive.
Implementation:Client in C and Server in Java.
Part of the C code being sent:
long count;
FILE *file;
char *file_data;
file=fopen("test.txt","rb");
fseek (file , 0 , SEEK_END);
count = ftell (file);
rewind (file);
file_data=(char*)malloc(sizeof(char)*count);
fread(file_data,1,count+1,file);
fclose(file);
if ((numbytes = send(sockfd, file_data, strlen(file_data)+1 , 0)) == -1)
{
perror("client: send");
exit(1);
}
Part of Java code receiving:
public String receiveFile()
{
String fileName="";
try
{
int bytesRead;
InputStream in = clientSocket.getInputStream();
DataInputStream clientData = new DataInputStream(in);
fileName=clientData.readUTF();
}
}
After readUTF() function is used, the server hangs up or is in infinite loop and doesnt proceed further. I've tried BufferedReader with readLine(). There is an error that "no suitable constructor found for BufferedReader(InputStream) & readLine() gives a warning.
Any other alternative besides BufferedReader??
readUTF() reads the format written by writeUTF(). You aren't sending that, so the server can't read it. Use read(byte[]), or readFully(), or new BufferedReader(newInputStreamReader(in));
If you use readLine() you need to send a newline. In either event you don't need to send the trailing null.

Java server socket communication is VERY slow

Local on Linux. It's about 10 seconds for a 20k message. My guess is my Java is bad and Python is fine.
py client:
def scan(self, msg):
try:
print 'begin scan'
HOST = 'localhost'
PORT = 33000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT));
s.sendall(msg)
data = s.recv(1024)
s.close()
print 'Received', repr(data)
except Exception, e:
print "error: " + str(e)
Java server:
ServerSocket service = new ServerSocket(33000);
while(true) {
debug("Begin waiting for connection");
//this spins
Socket connection = service.accept();
debug("Connection received from " + connection.getInetAddress().getHostName());
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
ScanResultsHeader results = new ScanResultsHeader();
Scanner scanner = new Scanner();
results = scanner.scan("scannerfake#gmail.com", "123", in);
and
public ScanResultsHeader scan (String userEmail,
String imapRetrievalId,
BufferedInputStream mimeEmail)
throws IOException, FileNotFoundException, MimeException, ScannerException {
//how fast would it be to just slurp up stream?
debug("slurp!");
String slurp = IOUtils.toString(mimeEmail);
debug("slurped " + slurp.length() + " characters");
slurp = slurp.toLowerCase();
debug("lc'ed it");
//...
My guess is I'm juggling the input streams wrong. One catch is the "BufferedInputStream mimeEmail" signature is required by the library API scan is using, so I'll need to get to that form eventually. But I noticed the simple act of slurping up a string takes ludicrously long so I'm already doing something incorrect.
Revising my answer....
If you are reading efficiently, and it appears you are, it will only be taking a lot time because either
You are creating a new connection every time you send a message which can be very expensive.
You are not sending the data as fast as you think.
The message is very large (unlikely but it could be)
There are plenty of examples on how to do this and a good library you can use is IOUtils which makes it simpler.
You should be able to send about 200K/s messages over a single socket in Java.
If you have a sends X bytes protocol using Big Endian you can do this.
DataInputStream dis = new DataInputStream( ...
int len = dis.readInt();
byte[] bytes = new byte[len];
dis.readFully(bytes);
String text = new String(bytes, "UTF-8");
Original problem was that the client isn't sending an end-of-input so the "slurp" operation keeps waiting for more stuff to cross the connection.
Solution was to implement an application-layer protocol to send the size of the message in advance, then stop listening for more message after that many bytes. I would have preferred a standard library -- something like, FiniteInputStream extends BufferedInputStream and takes a size as an argument, but wrote my own.

Multiple println from server to client

I am currently having an issue with a Server - Client interaction.
I am needed to read multiple println that a server sends to a client, this works but after it has read the lines it doesn't seem to go back to waiting for the user of the client to enter a new command
This is the method that opens up the streams
private void openStreams() throws IOException
{
final boolean AUTO_FLUSH = true;
is = socket.getInputStream();
os = socket.getOutputStream();
fromServer = new BufferedReader(new InputStreamReader(is));
toServer = new PrintWriter(os, AUTO_FLUSH);
}
This is the method that sends the request then reads them out
private void sendRequest() throws IOException
{
String request;
String reply;
Scanner sc = new Scanner(System.in);
request = sc.nextLine();
while(!(request.equals(CLIENT_QUITTING)))
{
toServer.println(request);
while((reply = fromServer.readLine()) != null)
{
System.out.println(reply);
}
request = sc.nextLine();
}
}
It seem to be getting stuck on the inner while loop
Can anyone point me in the direction of where I am going wrong?
The receiving code somehow needs to know when to stop reading text, and when to expect the next command.
For example, the Simple Mail Transfer Protocol (SMTP) defines that the body of the mail consists of several lines, and the line .\r\n marks the end.
Another possibility is what HTTP does for chunked encoding (with bytes, but the concept is the same). It sends the body as a sequence of chunks. Each chunk consists of a length field and the data (which is length bytes long). In your case you would probably send the number of lines that the receiver may expect, and then the lines themselves.

Categories

Resources