I got to implement a chat in my application. Connection to a server is made using sockets. I should register to that server and the server will aknowledge that with a reply.
I have implemented this in a single method where I send the command using a BufferedWriter, and then start reading from the input stream until it tells me there is no more data.
I read properly the server reply. However, I never get the negative value from the second in.read call and thus my method stays blocked in the while loop (in the conditionnal statement where I make that call).
How should this be done with sockets? I usually do that with files or other input streams without problem.
If I should read only the bytes I am supposed to read, does that mean that I either have to:
Know in advance the length of the server response?
or make the server send a code to notify it has finished to send its response?
Currently I am doing the following:
private String sendSocketRequest(String request, boolean skipResponse) throws ChatException {
if (!isConnected()) openConnection();
try {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()), 2048);
out.append(request);
out.flush();
out = null;
} catch (IOException e) {
LogHelper.error("Unable to send socket request: " + request, e);
throw new ChatException("Unable to send socket request: " + request, e);
}
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()), 2048);
StringBuffer response = new StringBuffer();
char[] buffer = new char[2048];
int charsRead = -1;
// >>>>>>>> This is where it gets blocked <<<<<<<<<
while ((charsRead = in.read(buffer)) >= 0) {
if (charsRead > 0) response.append(new String(buffer, 0, charsRead));
}
return response.toString();
} catch (IOException e) {
LogHelper.error("Unable to read socket response: " + request, e);
throw new ChatException("Unable to read socket response: " + request, e);
}
}
Connection to the server is made with the following method:
public synchronized void openConnection() throws ChatException {
try {
socket = new Socket(Constants.API_CHAT_SERVER_ADDRESS, Constants.API_CHAT_SERVER_PORT);
socket.setKeepAlive(true);
LogHelper.debug("CHAT >> Connected to the chat server: " + Constants.API_CHAT_SERVER_ADDRESS);
} catch (UnknownHostException e) {
LogHelper.error("Unable to open chat connection", e);
throw new ChatException("Unable to open chat connection", e);
} catch (IOException e) {
LogHelper.error("Unable to open chat connection", e);
throw new ChatException("Unable to open chat connection", e);
}
}
The amount of data to be sent/received over a socket based connection is protocol dependend and not known to the TCP/IP stack, but only to the application layer.
The protocol used is developer dependend ... ;-) so coming to your questions:
If I should read only the bytes I am supposed to read, does that mean that I either have to:
Know in advance the length of the server response?
Yes, this is one possibility.
or make the server send a code to notify it has finished to send its response?
Also yes, as this is another possibility. Common markers are \n or \r\n. The NUL/'\0' character also might make sense.
A third option is to prefix each data chunk with a constant number of bytes describing the amount of bytes to come.
Instead of dealing with bytes, maybe it's simpler handling instances of ad-hoc classes, like - for instance - a Message class:
The server:
// Streams
protected ObjectInputStream fromBuffer = null;
protected ObjectOutputStream toBuffer = null;
// Listening for a new connection
ServerSocket serverConn = new ServerSocket(TCP_PORT);
socket = serverConn.accept();
toBuffer = new ObjectOutputStream(socket.getOutputStream());
fromBuffer = new ObjectInputStream(socket.getInputStream());
// Receiving a new Message object
Message data = (Message)fromBuffer.readObject();
The client then sends a message by simply:
// Sending a message
Message data = new Message("Hello");
toBuffer.writeObject(data);
Message can be as complex as needed as long as its members implement Serializable interface.
Related
Hello I have an app that connect to a remote server using sockets
socket = new Socket();
socket.connect(new InetSocketAddress(Ip, portNum), 7000);
I have 2 methods send & receive
the scenario in send is
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
in method receive
String msg = "";
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (in.ready()) {
msg = msg + (char) in.read();
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
in AsyncTask of a different class I call
send();
String response=receive();
The above code is not sending or receiving without a wait period
i.e
Thread.sleep(2000);
I know sleep is a bad approach
what is the best scenario should I use?
Is it better to make an AsyncTask within send method and another one for receive method.
Here is where I use sleep and what data send & receive
client.send(some sql statement representED as json format);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// sql select result represented as json
String RESULT = client.recive();
Likely your server is not listening before you try to connect. Though this is unclear from the code you have posted. You'll need to show both server and client code there.
I can set up client Socket to send request to server (sendData() method) and read the received message (readData() method) correctly, but I only received message each time I send the request to server by using MOBILE_REQUEST string, through these codes:
#Override
protected Boolean doInBackground(String... params) {
try {
mSocket = new Socket(
// PC Ip is 192.168.1.199
// It is the other device, Not be local host : 127.0.0.1
Pas.pas.getPcIP(), 17001);
DataOutputStream mDos = new DataOutputStream(mSocket.getOutputStream());
String RESPONSE = null;
String MOBILE_BLOCK = "MobileBlock#";
// Converting collected data in byte array into String.
RESPONSE = sendData(mDos, MOBILE_BLOCK);
/**
* The result response from PC app in here
*/
// Log : response - #WindowsResp#192.168.1.199#
Log.i("", "response '" + RESPONSE + "'");
}
} catch (SocketTimeoutException e) {
IS_SOCKET_TIME_OUT = true;
e.printStackTrace();
} catch (ConnectException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
sendData() method - Client send the request to server and wait to get response data - String data.
private String sendData(DataOutputStream mDos, String MOBILE_REQUEST) {
try {
// Log : MOBILE_REQUEST.getBytes() - [B#82f10f8
mDos.write(MOBILE_REQUEST.getBytes());
// todo I should set this sleep, bcs TCP has delay time,
// so i need set the delay time for client should receive data
// otherwise, sometimes I did not receive anything
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Log : #WindowsResp#192.168.1.199#
return new String(readData(mSocket));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
readData() method - Read data after received from server.
public static byte[] readData(Socket mSocket) {
/* Since data are accepted as byte, all of them will be collected in the
following byte array which initialised with accepted data length. */
DataInputStream mDis = null;
try {
mDis = new DataInputStream(mSocket.getInputStream());
// Log : mDis.available() - 23
byte[] data = new byte[mDis.available()];
// Collecting data into byte array
for (int i = 0; i < data.length; i++)
data[i] = mDis.readByte();
// Log : data - [B#30c044a4
return data;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
What I want is every time server send to my socket client the message by clicking button, the socket client should receive it. But in above codes, it's not.
Please help me how to set up socket client always listen from server?
p/s : Or do I need set up ServerSocket? If use ServerSocket I cannot use same port, right? Because when I open ServerSocket for listen first (ex. at port 17001), I can not use client socket to send request via port 17001 because that port is already used.
UPDATED
The way server (PC app - laptop device) send to client (Mobile device - Android) is via Socket TCP, through these steps :
1 - Client (Android device) set up TCP socket connection to Server (PC app) (this connection never closed until exit app in onDestroy() method).
2 - Client send request to server, ex. MOBILE_REQUEST = "MobileID#MobileIP#"
3 - Server received the request from client, It replied to client via Socket connection, actually client received data correctly. ex. "WindowsRep#WindowsIP"
This way not work for me, even socket TCP connection not closed, and getInputStream() not shut down yet. In this case :
Server send string data to client via Socket connection, client received data correctly.
What I want is every time "Server send string data to client via Socket connection, client received data correctly". But in my case, client only receive data after sent request to server.
C# Server
Server socket
IPEndPoint ipe = new IPEndPoint("192.168.1.199", 17001);
svSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
svSocket.Bind(ipe);
Server Send data
string data_send = "#WBroad#" + "192.168.1.199" + "#";
byte[] byteData = Encoding.UTF8.GetBytes(data_send);
c.ClientSocket.Send(byteData);
As you haven't posted the sending code it is impossible to tell why you're not receiving data, but here is a quick critique of what you have posted:
// mSocket.setReuseAddress(true);
You've commmented this out, but it would be pointless to call this method now. You would have to construct the socket as new Socket(), with no parameters, then call this method, then call connect(). And as you aren't providing a source port or IP address to be re-used, it would still be pointless.
byte[] data = new byte[mDis.available()];
This is a complete misuse of available(). It does not provide a message length. See the Javadoc. There is no reason to believe that whatever data has arrived at this point, if any, is a complete message, or only one message. If you want messages you won't get any help from TCP: you will have to implement them yourself. As your protocol appears to be text-based I suggest you just use lines and readLine(), with BufferedReader and BufferedWriter instead of the DataInput/OutputStreams. And construct those once for the life of the socket, not once per application message, otherwise you will lose data.
// Collecting data into byte array
for (int i = 0; i < data.length; i++)
data[i] = mDis.readByte();
The huge problem with this is that it won't block, because, most of the time, available() will be zero, so this method will do nothing except return an empty byte[] array.
In any case this is entirely equivalent to mDis.readFully(data);, only several times less efficient, but you shouldn't be doing this anyway: see above.
return data;
} catch (IOException e) {
e.printStackTrace();
}
return null;
This is poor practice. You should let the IOException be thrown by this method and let the caller deal with it.
mDos.write(MOBILE_REQUEST.getBytes());
See above. This should include a line terminator, a length-word prefix, or some other way of delimiting the message.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
This sleep is literally a complete and utter waste of time. And space. Remove it. Sleeping in networking code is just cargo-cult programming.
return new String(readData(mSocket));
This will throw a NullPointerException if readData() returns null, which it does if there was an IOException, which is another reason to let that method propagate that exception instead of catching it internally and returning null.
private boolean splitData(int mobile_send_request_case, String DATA) {
This method is entirely irrelevant to the problem and should not have been posted.
Or do I need set up ServerSocket?
No. Why do you think that?
If use ServerSocket I cannot use same port, right?
Wrong.
Because when I open ServerSocket for listen first (ex. at port 17001), I can not use client socket to send request via port 17001 because that port is already used.
Wrong again. It isn't.
As I said above, it's impossible to help you further when you don't post all the relevant code, but there's enough wrong with this already that you really need to start again.
I have created a server by using ServerSocket. After that, I have created Client using Socket, and connect to this server.
After that, I do "some stuff" with InputStream and OutputStream is taken from Socket Object. But, I don't really understand inputStream and outputStream so much. Here is my simple code :
private Socket sock = null;
private InputStream sockInput = null;
private OutputStream sockOutput = null;
...
String msg = "Hello World";
byte[] buffer = null;
try {
sockOutput.write(msg.getBytes(), 0, test.length());
sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
buffer = new byte[test.length()];
sockInput.read(buffer, 0, test.length());
System.out.println(new String(buffer));
sockInput.read(buffer, 0, test.length());
System.out.println(new String(buffer));
} catch (IOException e1) {
e1.printStackTrace();
}
The result will be : "Hello World" and "Hello StackOverFlow".
Here is server side code :
private int serverPort = 0;
private ServerSocket serverSock = null;
public VerySimpleServer(int serverPort) {
this.serverPort = serverPort;
try {
serverSock = new ServerSocket(this.serverPort);
}
catch (IOException e){
e.printStackTrace(System.err);
}
}
// All this method does is wait for some bytes from the
// connection, read them, then write them back again, until the
// socket is closed from the other side.
public void handleConnection(InputStream sockInput, OutputStream sockOutput) {
while(true) {
byte[] buf=new byte[1024];
int bytes_read = 0;
try {
// This call to read() will wait forever, until the
// program on the other side either sends some data,
// or closes the socket.
bytes_read = sockInput.read(buf, 0, buf.length);
// If the socket is closed, sockInput.read() will return -1.
if(bytes_read < 0) {
System.err.println("Server: Tried to read from socket, read() returned < 0, Closing socket.");
return;
}
System.err.println("Server: Received "+bytes_read
+" bytes, sending them back to client, data="
+(new String(buf, 0, bytes_read)));
sockOutput.write(buf, 0, bytes_read);
// This call to flush() is optional - we're saying go
// ahead and send the data now instead of buffering
// it.
sockOutput.flush();
}
catch (Exception e){
System.err.println("Exception reading from/writing to socket, e="+e);
e.printStackTrace(System.err);
return;
}
}
}
public void waitForConnections() {
Socket sock = null;
InputStream sockInput = null;
OutputStream sockOutput = null;
while (true) {
try {
// This method call, accept(), blocks and waits
// (forever if necessary) until some other program
// opens a socket connection to our server. When some
// other program opens a connection to our server,
// accept() creates a new socket to represent that
// connection and returns.
sock = serverSock.accept();
System.err.println("Server : Have accepted new socket.");
// From this point on, no new socket connections can
// be made to our server until we call accept() again.
sockInput = sock.getInputStream();
sockOutput = sock.getOutputStream();
}
catch (IOException e){
e.printStackTrace(System.err);
}
// Do something with the socket - read bytes from the
// socket and write them back to the socket until the
// other side closes the connection.
handleConnection(sockInput, sockOutput);
// Now we close the socket.
try {
System.err.println("Closing socket.");
sock.close();
}
catch (Exception e){
System.err.println("Exception while closing socket.");
e.printStackTrace(System.err);
}
System.err.println("Finished with socket, waiting for next connection.");
}
}
public static void main(String argv[]) {
int port = 54321;
VerySimpleServer server = new VerySimpleServer(port);
server.waitForConnections();
}
My question is :
When I use sockOutput.write and I can get back those message back by sockInput.read. So, those message has been saved, right? If this true, does it saved on Server I have created or just saved in some other thing such as Socket Object.
If I have written to socket String A1, A2,... An so I will receive A1, A2, ... An String respectively, right?
A socket is an abstraction that you use to talk to something across the network. See diagram below...
In Java, to send data via the socket, you get an OutputStream (1) from it, and write to the OutputStream (you output some data).
To read data from the socket, you get its InputStream, and read input from this second stream.
You can think of the streams as a pair of one-way pipes connected to a socket on the wall. What happens on the other side of the wall is not your problem!
In your case, the server has another socket (the other end of the connection) and another pair of streams. It uses its InputStream (2) to read from the network, and its OutputStream (3) to write the same data back across the network to your client, which reads it again via its InputStream (4) completing the round trip.
Client Server
1. OutputStream -->\ /--> 2. InputStream -->
Socket <--> network <--> ServerSocket |
4. InputStream <--/ \<--3. OutputStream <--
Updated: in reply to comment:
Note that the streams and sockets just send raw bytes; they have no notion of a "message" at this level of abstraction. So if you send X bytes and another X bytes, then read X bytes and read another X bytes, then your system behaves as if there are two messages, because that's how you've divided up the bytes.
If you send X bytes, and another X bytes, then read a reply of length 2X, then you might be able to read a single combined "message", but as you've noticed, the underlying implementation of the streams can choose when to deliver chunks of bytes, so it might return X bytes, then X bytes, later, or 2X at once, or 0.5X four times...
InputStream and OutputStream are two completely separate streams. What you write into one has no a priori relation to what you read from the other. The InputStream gives you whatever data the server decides to send to you. I would also like to comment on this piece of your code:
sockOutput.write(msg.getBytes(), 0, test.length());
sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
You use the length of a string test (not shown in your code), which has nothing to do with the byte array you are passing as the first argument. This can cause an ArrayIndexOutOfBoundsException or truncation of your intended message.
Additional comments to your updated question
Reviewing your server-side code, it is not quite correctly written. You need to have try { handleConnection(...); } finally { socket.close(); } to ensure proper cleanup after an error, as well as when completing normally. Your code never closes anything on the server side.
Finally, and most critically, your entire code is written in a way that can result in a deadlock. Normally you need a separate thread to read and to write; otherwise the following may happen:
You attempt to write some data to the output;
The server reads it and tries to respond with data in your input;
But, since the buffers are too small, you don't manage to send everything because the server wants to first send something to you, then receive the rest; but you don't get to the receiving part before you have sent everything you've got.
I'm reading a string from a buffer and writing it to a server. The problem I'm having is that the string never gets received by the server when I leave the socket open and write in a loop.
When I use this:
try {
Socket send = new Socket("localhost", 1490);
DataOutputStream out = new DataOutputStream(send.getOutputStream());
String message = null;
while ((message = buffer.get()) != null){
out.writeBytes(message);
}
out.close();
send.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
the server doesn't receive the string, but when I do this it works properly:
try {
String message = null;
while ((message = buffer.get()) != null){
Socket send = new Socket("localhost", 1490);
DataOutputStream out = new DataOutputStream(send.getOutputStream());
out.writeBytes(message);
out.close();
send.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
Obviously I don't want to keep opening and closing the socket, though. What is the problem?
You need to flush your socket every time you want to send a data packet.
Closing a socket forces an automatic flush and that explains why your data is getting sent on socket close.
The data is not being written to the socket even when you close it? (in your first snippet that is)
Also, have you tried to use the flush method? You can read about it here: http://docs.oracle.com/javase/1.4.2/docs/api/java/io/DataOutputStream.html#flush() and your code will look like:
try {
Socket send = new Socket("localhost", 1490);
DataOutputStream out = new DataOutputStream(send.getOutputStream());
String message = null;
while ((message = buffer.get()) != null){
out.writeBytes(message);
out.flush();
}
out.close();
send.close();
} catch (IOException ex) {
ex.printStackTrace();
}
Let me make a guess.
Does the buffer.get() method block? If so, then the problem is that out.writeBytes(message) does not guarantee that the entire byte representation to be pushed to the server. Instead. there is a good chance that your client has buffered bytes waiting to be flushed through to the server.
If this is what is going on, then calling flush after each call to writeBytes will fix the problem.
But if the buffer.get() method doesn't block, then calling flush won't make any difference. In fact, it will just increase the network traffic. So adding the flush "just in case" is a bad idea.
Another possibility is that there is something wrong with the server-side code.
I am writing a client application that will receive a continuous flow of data through tcp/ip. The problem I'm having is that the buffered reader object isn't receiving any data and is hanging at the readline method.
The way the server works is that you connect to it, and then send authentication information in order to receive data. The gist of my code is below
socket = new Socket(strHost, port);
authenticate();
inStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
process(inStream);
authenticate()
{
PrintWriter pwriter = new PrintWriter(socket.getOutputStream(), true);
pwriter.println(authString);
}
process(BufferedReader bufferedReader)
{
while((line = bufferedReader.readLine()) != null)
dostuff
}
I created a sample server application that sends data the way (I think) the server is sending data and it connects, and receives and processes the data fine. I can connect to the server fine in my application. I can also telnet to the server and write the authentication string and receive a flood of data using telnet. However my application just hangs at readLine with the server and I'm out of idea's why.
The data coming in (through telnet atleast) looks like a continuous stream of the following:
data;data;data;data;data
data;data;data;data;data
Why is my app hanging at readline, am I not outputting the authentication line correctly? I'm not receiving any errors...
EDIT
My sample server code (which is working correctly)...again this is only mimicking the way I think the real server is running but I can connect to both in my application just not receive data from the real server.
public static void main(String[] args) throws IOException
{
ServerSocket serverSocket = null;
try
{
serverSocket = new ServerSocket(1987);
}
catch (IOException e)
{
System.out.println("Couldn't listen on port: 1987");
System.exit(-1);
}
Socket clientSocket = null;
try
{
clientSocket = serverSocket.accept();
}
catch (IOException e) {
System.out.println("Accept failed: 1987");
System.exit(-1);
}
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String something;
while ((something = in.readLine()) != null)
{
while(true)
{
out.println(message);
}
}
out.close();
in.close();
clientSocket.close();
serverSocket.close();
}
Firstly you should call BufferedReader.ready() before calling readLine(), as the ready() will tell you if it's ok to read.
PrintWriter doesn't throw I/O Exception so the write may have failed without your knowledge which is why there is nothing to read. Use PrintWriter.checkError() to see if anything as gone wrong during the write.
You ought to set up the input and output streams on the Socket at the same time before you write anything down the pipe. If your reader is not ready when the other end tries to write you will get a broken pipe in the server and it won't send any more data. Telnet sets up read and write before you have written or read anything.
You can make use of Wireshark to tell if the server is actually sending data.
BufferdReader.readLine() reads lines, i.e. sequences of characters ended with \r or \r\n. I guess that your server writes its output into one single line. Your telnet output proves this assumption. Just use PrintWriter.println() at server side.
this work with me
with socket without flush
void start_listen()
{
String result1="";
char[] incoming = new char[1024];
while (!s.isClosed())
{
try {
int lenght = input.read(incoming);
result1 = String.copyValueOf(incoming,0,lenght);
}
catch (IOException e)
{
e.printStackTrace();
}
Log.d("ddddddddddd",result1);
}