I'm implementing a WebSocket server (for learning purposes) and I have it correctly handling the handshake (websocket.onopen is called so I assume this means handshake was successful), however, when the client (browser) sends a message after the handshake, the server never receives it.
Using Chrome's developer tools, I'm able to see that all the headers were correctly received and no errors are thrown. It also says that it sent the "hello" despite the readLine() never firing in Java.
What's wrong in my code?
EDIT 1: I discovered that if I refresh the web page, then (and only then) the ServerSocket receives the data from the last connection (that the refresh just killed)! Why is this the only way it receives it?
EDIT 2: I also found that I can send a message to the client after the handshake and the client receieves it but STILL the server never receives the client's message! I sent the message to the client like this:
byte[] message = new byte[ 7 ];
message[ 0 ] = new Integer(129).byteValue();
message[ 1 ] = new Integer(5).byteValue();
byte[] raw = "hello".getBytes();
message[ 2 ] = raw[ 0 ];
message[ 3 ] = raw[ 1 ];
message[ 4 ] = raw[ 2 ];
message[ 5 ] = raw[ 3 ];
message[ 6 ] = raw[ 4 ];
outStream.write( message);
out.println();
HTML PAGE
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>WebSocket Test</title></head>
<body>
<script>
try
{
function writeToScreen(message)
{
var p = document.createElement( "p" );
p.innerHTML = message;
document.getElementsByTagName( "body" )[ 0 ].appendChild( p );
}
function onOpen(evt)
{
writeToScreen( "opened" );
doSend( "hello" );
//We reach here but the server never recieves the message! (and bufferedAmount == 0)
writeToScreen( "sent: " + websocket.bufferedAmount );
}
function onClose(evt)
{
alert( "closed" );
websocket.close();
}
function onMessage(evt)
{
alert( "Message: " + evt.data );
}
function onError(evt)
{
alert( "Error: " + evt );
}
function doSend (message)
{
websocket.send( message );
}
//PUT IN YOUR OWN LOCAL IP ADDRESS HERE TO GET IT TO WORK
var websocket = new WebSocket( "ws://192.168.1.19:4444/" );
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage;
websocket.onerror = onError;
}
catch(e)
{
}
</script>
</body>
</html>
JAVA CODE
import java.net.*;
import java.io.*;
import java.security.*;
public class WebListener
{
public static void main(String[] args) throws Exception
{
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(-1);
}
while (listening) new ServerThread(serverSocket.accept()).start();
serverSocket.close();
}
}
class ServerThread extends Thread {
private Socket socket = null;
public ServerThread(Socket socket) {
super("ServerThread");
this.socket = socket;
}
public void run() {
try {
OutputStream outStream = null;
PrintWriter out = new PrintWriter( outStream = socket.getOutputStream(), true);
BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream()));
String inputLine, outputLine;
//Handle the headers first
doHeaders( out, in );
//Now read anything they have to send
while ( ( inputLine = in.readLine() ) != null )
{
//WE NEVER REACH HERE!
System.out.println( inputLine );
}
out.close();
in.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void doHeaders(PrintWriter out, BufferedReader in) throws Exception
{
String inputLine = null;
String key = null;
//Read the headers
while ( ( inputLine = in.readLine() ) != null )
{
//Get the key
if ( inputLine.startsWith( "Sec-WebSocket-Key" ) ) key = inputLine.substring( "Sec-WebSocket-Key: ".length() );
//They're done
if ( inputLine.equals( "" ) ) break;
}
//We need a key to continue
if ( key == null ) throw new Exception( "No Sec-WebSocket-Key was passed!" );
//Send our headers
out.println( "HTTP/1.1 101 Web Socket Protocol Handshake\r" );
out.println( "Upgrade: websocket\r" );
out.println( "Connection: Upgrade\r" );
out.println( "Sec-WebSocket-Accept: " + createOK( key ) + "\r" );
out.println( "\r" );
}
public String createOK(String key) throws NoSuchAlgorithmException, UnsupportedEncodingException, Exception
{
String uid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
String text = key + uid;
MessageDigest md = MessageDigest.getInstance( "SHA-1" );
byte[] sha1hash = new byte[40];
md.update( text.getBytes("iso-8859-1"), 0, text.length());
sha1hash = md.digest();
return new String( base64( sha1hash ) );
}
public byte[] base64(byte[] bytes) throws Exception
{
ByteArrayOutputStream out_bytes = new ByteArrayOutputStream();
OutputStream out = new Base64.OutputStream(out_bytes); //Using http://iharder.net/base64
out.write(bytes);
out.close();
return out_bytes.toByteArray();
}
private String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while(two_halfs++ < 1);
}
return buf.toString();
}
}
WebSocket messages are not terminated by \r\n so you can't use in.readline() to read them. See the data framing section of the spec for how to messages are constructed.
For text messages from client (browser) to server, messages will have the form:
(byte)0x81
1, 3 or 9 byte structure indicating message length and whether the message body is masked. (Messages from a browser should always be masked.)
4 byte mask
Message (utf-8 encoded)
There is no end-of-message marker you can search for. You just need to read the first few bytes of a client request to figure out the length of its payload.
your code WebListener must running on windows, and you will find out line.separator is CRLF
byte[] lineSeperator=System.getProperty("line.separator").getBytes();
System.out.println("line seperator: "+Arrays.toString(lineSeperator));
In your response header
out.println( "Header xxxx"+ "\r" );
so header is ended with \r\r\n
Per HTTP rfc2616
Response = Status-Line ; Section 6.1
*(( general-header ; Section 4.5
| response-header ; Section 6.2
| entity-header ) CRLF) ; Section 7.1
CRLF
[ message-body ] ; Section 7.2
Client don't can not decode your header with \r\r\n.
Related
I am coding a Web Framework for me with an WebServer and an WebSocket Server.
My Current Problem since days is, that the Response Content of my WebSocket Client is very funny...
It sends me not the Content as bytes, every time the value is another.
Web Response for normal HTTP and the Socket request works perfectly.
My current Code:
poolIO.execute(new Thread() {
#Override
public void run() {
try {
InputStream inputIO = clientIO.getInputStream();
StringBuilder builderIO = new StringBuilder();
while (clientIO.isConnected()) {
int cacheIO = 0;
int countIO = 0;
byte[] binaryIO = new byte[0];
while ((inputIO.available() != 0 && (cacheIO = inputIO.read()) != 0)) {
binaryIO = new byte[binaryIO.length + 1];
binaryIO[countIO] = (byte) cacheIO;
builderIO.append((char) cacheIO);
countIO++;
}
if (builderIO.length() > 0) {
string(clientIO, builderIO.toString());
binary(clientIO, binaryIO);
binaryIO = new byte[0];
builderIO.delete(0, builderIO.length());
}
}
inputIO.close();
disconnect(clientIO);
this.stop();
} catch (IOException errorIO) {
if (errorIO.getMessage().equalsIgnoreCase("Stream closed.")) {
logIO.debug(GuardianLog.Type.INFO, "Client with IP " + clientIO.getInetAddress().getHostAddress() + " disconnected from Server with Port " + networkIO.getPort() + ".");
} else logIO.append(GuardianLog.Type.ERROR, "Socket throw an Error ", errorIO);
}
}
});
Regards
Jan
Fixed getting raw bytes with following Code:
byte[] bufferIO = new byte[inputIO.available()];
inputIO.read(bufferIO);
if (bufferIO.length != 0) {
binary(clientIO, bufferIO);
string(clientIO, new String(bufferIO));
}
I'm writing a Client/Server application where the client sends a server a username and the server responds with a challenge however I'm having issues with the client receiving the challenge. It seems as though the challenge is just getting dropped or replaced with another array of bytes (which happens to be exactly the same each time). Any suggestions for this would be much appreciated.
Client Code:
public class ClientOrig {
public static void main( String[] args ) {
// Complain if we don't get the right number of arguments.
if ( args.length != 1 ) {
System.out.println( "Usage: Client <host>" );
System.exit( -1 );
}
try {
// Try to create a socket connection to the server.
Socket sock = new Socket( args[ 0 ], ServerOriginal.PORT_NUMBER );
// Get formatted input/output streams for talking with the server.
DataInputStream input = new DataInputStream( sock.getInputStream() );
DataOutputStream output = new DataOutputStream( sock.getOutputStream() );
// Get a username from the user and send it to the server.
Scanner scanner = new Scanner( System.in );
System.out.print( "Username: " );
String name = scanner.nextLine();
output.writeUTF( name );
output.flush();
byte[] challenge = ServerOriginal.getMessage( input );
System.out.println(challenge);
String request = "";
System.out.print( "cmd> " );
// We are done communicating with the server.
sock.close();
} catch( IOException e ){
System.err.println( "IO Error: " + e );
}
}
}
Server Code:
public static byte[] getMessage( DataInputStream input ) throws IOException {
int len = input.readInt();
byte[] msg = new byte [ len ];
input.readFully( msg );
System.out.println("Read: " + msg + " which is " + msg.length);
System.out.println("Byte to string: " + new String(msg));
return msg;
}
/** Function analogous to the previous one, for sending messages. */
public static void putMessage( DataOutputStream output, byte[] msg ) throws IOException {
output.writeInt(msg.length);
output.write( msg, 0, msg.length );
System.out.println("Writing: "+ msg + " which is " + msg.length);
output.flush();
}
/** Function to handle interaction with a client. Really, this should
be run in a thread. */
public void handleClient( Socket sock ) {
try {
// Get formatted input/output streams for this thread. These can read and write
// strings, arrays of bytes, ints, lots of things.
DataOutputStream output = new DataOutputStream( sock.getOutputStream() );
DataInputStream input = new DataInputStream( sock.getInputStream() );
// Get the username.
String username = input.readUTF();
System.out.println(username);
// Make a random sequence of bytes to use as a challenge string.
Random rand = new Random();
byte[] challenge = new byte [ 16 ];
System.out.println(challenge);
rand.nextBytes( challenge );
System.out.println(challenge);
putMessage(output, challenge);
// Send the client the challenge.
putMessage( output, challenge );
getMessage(input);
} catch ( IOException e ) {
System.out.println( "IO Error: " + e );
} finally {
try {
// Close the socket on the way out.
sock.close();
} catch ( Exception e ) {
}
}
}
I am taking a networking module at university and am beginning Java socket programming. I have the server code written at the end of this post, which accepts client requests and sends text from a file called data.txt (a simple hello world type message). That all works fine.
I now want to modify the server to accept the file name at the command line and if the file is not found (i.e. what's entered in the command line is not "data.txt."), to return a 404 error. I have modified the constructor as you can see but it still returns the data. I'm not really sure how a http header features in all this to be honest and don't know where to go from here.
//
// Web8000.java an HTTP Web server from Elliotte Rusty Harold.
// Listens on a socket waiting for a client to send a GET request.
// Always responds with the contents of a fixed text file!
//
import java.io.*;
import java.net.*;
import java.util.*;
public class Web8000 extends Thread {
private byte[] header; // the message header
private byte[] content; // the message content
private final static int DEFAULT_PORT = 8000; // the default listening port
private int port = DEFAULT_PORT;
public Web8000(boolean fileFound, byte[] data, String encoding, String MIMEtype, int port) throws UnsupportedEncodingException {
content = data; // set up the message content
this.port = port; // use the port number passed
if (fileFound) {
String header = "HTTP/1.0 200 OK\r\n"
+ "Server: Web8000 1.0\r\n"
+ "Content-length: " + content.length + "\r\n"
+ "Content-type: " + MIMEtype + "\r\n\r\n";
this.header = header.getBytes("ASCII"); // assume ASCII encoding system
}
else {
String header = "HTTP/1.0 404 Not Found\r\n"
+ "Server: Web8000 1.0\r\n"
+ "Content-length: " + content.length + "\r\n"
+ "Content-type: " + MIMEtype + "\r\n\r\n";
this.header = header.getBytes("ASCII"); // assume ASCII encoding system
}
}
// enable thread to deal with client
public void run() {
try {
ServerSocket serverSock = new ServerSocket(port);
// log details of server response on screen
System.out.println("Connecting on port: " + serverSock.getLocalPort());
System.out.println("Data to be sent: ");
System.out.write(content);
while (true) { // loop forever dealing with clients
Socket clientSock = null;
try {
clientSock = serverSock.accept(); // get skt for client-server comms
OutputStream out = new BufferedOutputStream(clientSock.getOutputStream());
InputStream in = new BufferedInputStream(clientSock.getInputStream());
// read request and assume buffer big enough!
StringBuffer request = new StringBuffer(800);
int ch = 0;
Boolean finished = false; // so we can read bytes until all of hdr.
while (!finished) {
ch = in.read(); // read next char (byte), actually returns int!
if (ch == -1) { // did sender somehow fail during transmission?
System.out.println("Sender died?");
finished = true;
} else { //append the char to the string buffer
System.out.print((char) ch);
request.append((char) ch);
if (ch == '\n') { //see if end of header
if (request.toString().indexOf("\r\n\r\n") != -1) {
finished = true; //what if "\r\r" or "\n\n"?
}
}
} //deal with bytes recvd
} //while more bytes to read
//check that it is an HTTP request. Should check for GET etc too!
if (request.toString().indexOf("HTTP/") != -1) {
out.write(header); //okay so write MIMEtype hdr details
} //now send the actual data content
out.write(content);
out.flush(); //to make sure the buffer is emptied immediately
} //try
catch (IOException ioE) {
} finally { //make sure this end of the connection gets closed
if (clientSock != null) {
clientSock.close();
}
}
} //while forever
} //try opening server socket
catch (IOException ioE) {
System.err.println("Could not start server. Port in use!");
}
}
public static void main(String args[]) {
try {
String fileName = /*(args[0])*/ "data.txt"; // user provides file name
String contentType = "text/html";
boolean fileFound = false;
if ("data.txt".equals(fileName)) fileFound = true;
InputStream in = new FileInputStream("C:\\Users\\Adam Matheson\\Documents\\NetBeansProjects\\scratch\\src\\resource\\data.txt"); // open specified file
ByteArrayOutputStream out = new ByteArrayOutputStream();
int bite;
while ((bite = in.read()) != -1) // Now read bytes from file ...
{
out.write(bite); // ...into byte stream
}
byte[] data = out.toByteArray(); // convert byte stream into an array of bytes ready for DatagramPacket
int port = DEFAULT_PORT;
String encoding = "ASCII";
//create the thread to send the contents of the file to any connection!
Thread thr = new Web8000(fileFound, data, encoding, contentType, port);
thr.start();
} catch (ArrayIndexOutOfBoundsException obE) {
//useful for when args should have been supplied!
System.err.println("Usage: java Web8000 filename port encoding");
} catch (Exception e) {
System.err.println(e);
}
}
}
When I open a websocket connection to my websocket server application from Java, the server sees two connections. The first one never sends any data and the second one sends all the proper headers, etc. Anyone know what the reason for this is?
Client side connection is:
var websocket = new WebSocket( "ws://192.168.1.19:3333/websession" );
On the server side, in a while loop I call "serverSocket.accept()" and this gets called twice. But one of them never sends any data (the in.read() simply times out eventually without returning anything).
JAVA SERVER CODE
import java.net.*;
import java.io.*;
import java.security.*;
public class WebListener {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(-1);
}
while (listening) new ServerThread(serverSocket.accept()).start();
serverSocket.close();
}
}
class ServerThread extends Thread {
private Socket socket = null;
public ServerThread(Socket socket) {
super("ServerThread");
this.socket = socket;
}
public void run() {
try {
OutputStream outStream = null;
PrintWriter out = new PrintWriter( outStream = socket.getOutputStream(), true);
BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream()));
String inputLine, outputLine;
//Handle the headers first
doHeaders( out, in );
// ..elided..
out.close();
in.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void doHeaders(PrintWriter out, BufferedReader in) throws Exception {
String inputLine = null;
String key = null;
//Read the headers
while ( ( inputLine = in.readLine() ) != null ) {
//Get the key
if ( inputLine.startsWith( "Sec-WebSocket-Key" ) )
key = inputLine.substring( "Sec-WebSocket-Key: ".length() );
//They're done
if ( inputLine.equals( "" ) ) break;
}
//We need a key to continue
if ( key == null ) throw new Exception( "No Sec-WebSocket-Key was passed!" );
//Send our headers
out.println( "HTTP/1.1 101 Web Socket Protocol Handshake\r" );
out.println( "Upgrade: websocket\r" );
out.println( "Connection: Upgrade\r" );
out.println( "Sec-WebSocket-Accept: " + createOK( key ) + "\r" );
out.println( "\r" );
}
public String createOK(String key) throws NoSuchAlgorithmException, UnsupportedEncodingException, Exception {
String uid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
String text = key + uid;
MessageDigest md = MessageDigest.getInstance( "SHA-1" );
byte[] sha1hash = new byte[40];
md.update( text.getBytes("iso-8859-1"), 0, text.length());
sha1hash = md.digest();
return new String( base64( sha1hash ) );
}
public byte[] base64(byte[] bytes) throws Exception {
ByteArrayOutputStream out_bytes = new ByteArrayOutputStream();
OutputStream out = new Base64.OutputStream(out_bytes); //Using http://iharder.net/base64
out.write(bytes);
out.close();
return out_bytes.toByteArray();
}
private String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while(two_halfs++ < 1);
}
return buf.toString();
}
}
This looks to be a bug with Firefox. In Chrome it only opens one connection, while the same page in Firefox 15 opens two connections.
I have a server written in C# and a client side in Android. If I send a message from client (Android) to server (c#) and from server to client, everything works fine. If I try to send one message from client , one from server, another from client, the client remains stuck at reading the message from the server. What could be the problem?
My client code is:
sendBytes("HELLOX".getBytes());
readBytes(byDataReceived);//here it gets stucked
...
try
{
int nrsend=sendBytes("HELLOX".getBytes());
readBytes(byDataReceived);
}
catch (Exception se)
{
Log.d("IdealLog","Exception: "+se.getMessage()+" ");
Toast.makeText(context, se.getMessage()+" " , 10000).show();
// MessageBox.Show(se.Message);
return false;
}
...
public static int readBytes(byte[] myByteArray) throws IOException
{
Log.d("IdealLog","readBytes-begin");
InputStream in = socket.getInputStream();
BufferedReader buffreader = new BufferedReader(new InputStreamReader(in));
String finalText = "";
String text = "";
while ((text = buffreader.readLine()) != null)
{
finalText += text;
}
myByteArray=new byte[myByteArray.length];
myByteArray=EncodingUtils.getAsciiBytes(finalText);
Log.d("IdealLog","Input Stream: "+finalText);
Log.d("IdealLog","TEST: "+EncodingUtils.getAsciiString(myByteArray));
Log.d("IdealLog","readBytes-end");
byDataReceived=myByteArray;
buffreader.close();
return myByteArray.length;//myByteArray.length;
}//readBytes end
public static int sendBytes(byte[] myByteArray) throws IOException
{
return sendBytes(myByteArray, 0, myByteArray.length);
}//sendBytes end
public static int sendBytes(byte[] myByteArray, int start, int len) throws IOException
{
if (len < 0)
{
throw new IllegalArgumentException("Negative length not allowed");
}
if (start < 0 || start >= myByteArray.length)
{
throw new IndexOutOfBoundsException("Out of bounds: " + start);
}
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
// dos.writeInt(len);
if (len > 0)
{
dos.write(myByteArray, start, len);
}
int size=dos.size();
dos.flush();
return size;
}//sendBytes end
My server code:
static void Main(string[] args)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 1408);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(ip);
socket.Listen(10);
Console.WriteLine("Waiting for a client...");
Socket client = socket.Accept();
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("Connected with {0} at port {1}", clientep.Address, clientep.Port);
string welcome = "HELLO&";
byte[] data = new byte[200];
client.Receive(data);
Console.WriteLine("Received data from CLIENT TEST1: {0}", System.Text.ASCIIEncoding.ASCII.GetString(data));
ASCIIEncoding asen = new ASCIIEncoding();
byte[] data2 = new byte[200];
data2 = asen.GetBytes(welcome);
client.Send(data2, data2.Length, SocketFlags.None);
//if i comment out from this 3 lines, everything is working fine
byte[] data3 = new byte[200];//this
client.Receive(data3);//this
Console.WriteLine("Received data from CLIENT TEST2: {0}", System.Text.ASCIIEncoding.ASCII.GetString(data3));//this
Console.WriteLine("Disconnected from {0}", clientep.Address);
client.Close();
socket.Close();
Console.ReadLine();
}
Modify into this:
//if i comment out from this 3 lines, everything is working fine
byte[] data3 = new byte[200];//this
client.Receive(data3);//this
Console.WriteLine("Received data from CLIENT TEST2: {0}", System.Text.ASCIIEncoding.ASCII.GetString(data3));//this
client.Send(data2, data2.Length, SocketFlags.None);
Console.WriteLine("Disconnected from {0}", clientep.Address);
client.Close();
socket.Close();
Console.ReadLine();
}