I am creating a simple Client-Server application and facing some weird behaviour when passing messages through a Socket: When the Client writes to the server, the message is passed correctly, however when the server sends a response, whichever value is sent through the socket seems to get duplicated...
Here is a sample code of what the server does:
.
.
.
public void respond(Socket socket)
{
try
{
InputStreamReader inStream = new InputStreamReader( socket.getInputStream() );
PrintWriter outStream = new PrintWriter(
new OutputStreamWriter( socket.getOutputStream(), "UTF-16" ) );
outStream.write("Message received\n");
outStream.flush();
.
.
.
}
catch (Exception e) { /* Do something */ }
}
.
.
.
Server and Client are currently running on the same machine.
Furthermore, encoding seems to be no issue when writing from client to server, but it is when writing from server to client: If I specify any other (or no) encoding than UTF-16 for the OutputStreamWriter, the Client won't be able to parse the message correctly.
Does any of you guys have an idea why that might be?
The character encoding on each end of the conversation needs to be the same: the Charset used for encoding by InputStreamReader at the client must match that used by the OutputStreamWriter at the server (and vice-versa).
If you don't specify one, it is going to use the JVM's default.
When you didn't provided your client's code, the fact that the server is using the default Charset to read and UTF-16 to write makes me think there is a potential mismatch.
Related
From this link, a demo for SSLSocketClient.java is given:
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
/*
* This example demostrates how to use a SSLSocket as client to
* send a HTTP request and get response from an HTTPS server.
* It assumes that the client is not behind a firewall
*/
public class SSLSocketClient {
public static void main(String[] args) throws Exception {
try {
SSLSocketFactory factory =
(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket =
(SSLSocket)factory.createSocket("www.verisign.com", 443);
/*
* send http request
*
* Before any application data is sent or received, the
* SSL socket will do SSL handshaking first to set up
* the security attributes.
*
* SSL handshaking can be initiated by either flushing data
* down the pipe, or by starting the handshaking by hand.
*
* Handshaking is started manually in this example because
* PrintWriter catches all IOExceptions (including
* SSLExceptions), sets an internal error flag, and then
* returns without rethrowing the exception.
*
* Unfortunately, this means any error messages are lost,
* which caused lots of confusion for others using this
* code. The only way to tell there was an error is to call
* PrintWriter.checkError().
*/
socket.startHandshake();
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())));
out.println("GET / HTTP/1.0");
out.println();
out.flush();
/*
* Make sure there were no surprises
*/
if (out.checkError())
System.out.println(
"SSLSocketClient: java.io.PrintWriter error");
/* read response */
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
out.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I have two questions:
According to this official document, if we are using a raw SSLSocketFactory rather than the HttpsURLConnection, there is no hostname verification enforced in the handshake process. Therefore, hostname verification should be done manually.
When using raw SSLSocket and SSLEngine classes, you should always check the peer's credentials before sending any data. The SSLSocket and SSLEngine classes do not automatically verify that the host name in a URL matches the host name in the peer's credentials. An application could be exploited with URL spoofing if the host name is not verified. Since JDK 7, endpoint identification/verification procedures can be handled during SSL/TLS handshaking. See the SSLParameters.getEndpointIdentificationAlgorithm method.
Does it mean the demo is insecure?
I saw a solution to add hostname verification in Java 7 as:
SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
sslSocket.setSSLParameters(sslParams);
When the algorithm is specified as "HTTPS", the handshake will verify the hostname. Otherwise (the algorithm is empty only using raw SSLSockeFactory), the hostname verification has not been invoked at all.
I curious about could I fix it as follows:
SSLSocketFactory factory =
(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket =
(SSLSocket)factory.createSocket("www.verisign.com", 443);
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
if(!hv.verify(socket.getSession().getPeerHost(),socket.getSession())){
threw CertificateException("Hostname does not match!")
}
I saw the HttpsURLConnection.getDefaultHostnameVerifier() can return a default HostnameVerifier, can I use it to do verification? I saw many people talking about use a custom HostnameVerifier. I don't understand if there is a default one why we need to customize it?
Borderline as an answer but got much too long for comments.
(1) yes, for HTTPS (as noted in the paragraph after the one you quoted) this is a security flaw; probably this example was written before Java 7 and not updated since. You could file a bug report for them to update it. (Of course there are some using SSL/TLS applications that don't validate hostname, like SNMPS and LDAPS, and don't even have URLs, but can still be implemented using Java JSSE.)
(2) the HTTP is wrong or poor also:
PrintWriter uses the JVM's lineSeparator which varies by platform, but HTTP standards (RFCs 2068, 2616, 7230) require CRLF for request header(s) on all platforms, though some servers (probably including google) will accept just-LF following the traditional Postel maxim 'be conservative in what you send and liberal in what you receive';
the read side assumes all data is line-oriented and won't be damaged by canonicalizing EOLs, which is true for HTTP header and some bodies like the text/html you will get from most webservers when request has no Accept (or Accept-encoding), but is not guaranteed;
the read side also assumes all data can be decoded from and re-encoded to the JVM default 'charset' safely; this is true for HTTP header (which is effectively 7-bit ASCII) but not many/most bodies: in particular handling 8859 or similar as UTF8 will destroy much of it, and handling UTF8 as 8859 or CP1252 will mojibake it.
(3) HTTP/1.0 is officially obsolete, although it is still widely supported and makes a significantly simpler demo, so I'd let that one slide.
I made a java server which the client requests info and gets response from the server. I am using BufferedWriter in order to send info to the client, and the info is in hebrew. The problem is that the client just gets gibberish if the server sends hebrew to him. I already tried different encoding, like ISO-8859-8, UTF-8, Unicode and so on, but none of them helped. I made a check and in the server side, and the string in the server is fine, in hebrew and I also made it so save it to file in hebrew and it worked. I really don't know what to do.. I tried almost every solution here and nothing worked.
Don't use the BufferedWriter for it, I just tinkered a little bit and found out that if you want to write something with UTF-8 encoding (e.g some non-Latin characters) you could just use the DataOutputStream variable, which has the method writeUTF().
It goes like this:
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeUTF(someString);
out.flush();
I read other SO questions about this problem and nothing solve my problem so I think this post is not a duplicate.
I want to send a Unicode string from android client socket to a server socket But I receive all Unicode character as question marks from server (I use a c# server on windows). I use WireShark to monitor network and it show all Unicode character as dots. This is my send code:
String msg = "تست سوکت";
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
out.println(msg);
out.flush();
Some stackOverFlow question says adding charSetEncoder to OutputStreamWriter constractor can solve this but that's not worked for me and the result was the same:
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(),
Charset.forName("UTF-8").newEncoder())),
true);
// Or
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(),
"UTF-8")),
true);
After that I try to send byte array as string, But that send nothing to server:
byte[] byteArray = msg.getBytes(Charset.forName("UTF-8"));
out.println(byteArray.toString() + "\n");
Your Java code is correct.
That Wireshark is not showing unicode characters is assumable a limitation of Wireshark.
Therefore the receiving C# program must be the problem. Are you sure that this program expects a stream of UTF-8 characters?
"Unicode" on Windows and C# usually means UTF-16, not UTF-8.
I have an HTML form that makes a POST request to a Socket I made with Java. I read each line with
/**
* Read a line from an {#link InputStream}
* #param inFromClient The {#link InputStream} to read from
* #return The {#link String} read
* #throws IOException When something went wrong while reading
*/
private String readLine(InputStream inFromClient) throws IOException {
StringBuilder lineb = new StringBuilder();
char c = (char) inFromClient.read();
while (c != '\n'){
lineb.append(Character.toString(c));
c = (char) (inFromClient.read());
}
String line = lineb.toString();
return line.substring(0,line.lastIndexOf('\r')<0?0:line.lastIndexOf('\r'));
}
That way, I'm able to parse the request till the boundary and then save the file sent. Everything works perfectly.
However, I'm also trying to make a POST request with Java to the same socket. First, I create a second socket connected to my server socket. Then I do:
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
pw.println("POST / HTTP/1.1");
pw.println("Host: ...");
...
The problem
The problem is, my method cannot read any line and it all ends up with a "OutOfMemory' exception at line 5. Why am I not able to read lines sent from a Java socket while I can read those sent from my browser (html form) ? Thank you.
Your server code must read() into an int and check whether that's -1 before casting to a char. You're ignoring end-of-file from the stream and appending -1 to your string builder for ever.
However:
I'd recommend using an existing HTTP server framework in your server to read and parse requests, rather than writing your own. (Or at least use an off-the-shelf HTTP request parser / response serialiser if you want to use your own socket code.)
Both your client and server code ignore character encoding. You need to convert bytes to/from chars using a Charset instance.
Use HttpURLConnection in your client, rather than a simple TCP socket.
Better, use something like https://hc.apache.org/ for your HTTP functionality.
I am trying to create a proxy server.
I want to read the websites byte by byte so that I can display images and all other stuff. I tried readLine but I can't display images. Do you have any suggestions how I can change my code and send all data with DataOutputStream object to browser ?
try{
Socket s = new Socket(InetAddress.getByName(req.hostname), 80);
String file = parcala(req.url);
DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());
BufferedReader dis = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter socketOut = new PrintWriter(s.getOutputStream());
socketOut.print("GET "+ req.url + "\n\n");
//socketOut.print("Host: "+req.hostname);
socketOut.flush();
String line;
while ((line = dis.readLine()) != null){
System.out.println(line);
}
}
catch (Exception e){}
}
Edited Part
This is what I should have to do. I can block banned web sites but can't allow other web sites in my program.
In the filter program, you will open a TCP socket at the specified port and wait for connections. If a
request comes (i.e. the client types a URL to access a web site), the application will process it to
decide whether access is allowed or not and then, using the same socket, it will send the reply back
to the client. After the client opened her connection to WebPolice (and her request has been checked
and is allowed), the real web page needs to be shown to the client. Therefore, since the user already gave her request, now it is WebPolice’s turn to forward the request so that the user can get the web page. Thus, WebPolice acts as a client and requests the web page. This means you need to open a connection to the web server (without closing the connection to the user), forward the request over this connection, get the reply and forward it back to the client. You will use threads to handle multiple connections (at the same time and/or at different times).
I don't know what exactly you're trying to do, but crafting an HTTP request and reading its response incorporates somewhat more than you have done here. Readline won't work on binary data anyway.
You can take a look at the URLConnection class (stolen here):
URL oracle = new URL("http://www.oracle.com/");
URLConnection yc = oracle.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
Then you can read textual or binary data from the in object.
Read line will treat the line read as a String, so unless you want to mess around with conversions over to bytes, I wouldn't recommend that.
I would just read bytes until you can't read anymore, then write them out to a file, this should allow you to grab the images, keeping file headers intact which can be important when dealing with files other than text.
Hope this helps.
Instead of using BufferedReader you can try to use InputStream.
It has several methods for reading bytes.
http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html