― Statement, keep away from the brain-dead people. The core of this blog is not the if-else+ prefix, but how to define a private protocol through the URL protocol processing framework
The difference between a URI and a URL
URI (uniform resource identifier) Uniform resource identifier; URL (uniform resource location) Uniform resource locator (or unified resource locator); URI is a relatively broad concept. URL is a type of URI and a subset of the URI naming mechanism. It can be said that URI is abstract, and the specific use of URLs to locate resources. The URI generally points to not the physical resource path, but the mapped resource identifier in the entire system. URLs are strings used on the Internet to describe information resources, mainly used on various WWW client programs and server programs. Using URLs can use a unified format to describe various information resources, including files, server addresses and directories, etc.
1. Let's first preface
We're used to http
URL url=new URL(http://www.apptest.com:8080/test/ios.php);
We must get used to it, too
Of course, we also need to get used to the URL.
"https", "ftp", "mailto", "telnet", "file", "ldap", "gopher", "jdbc", "rmi", "jndi", "jar", "doc", "netdoc", "nfs", "verbatim", "finger", "daytime", "systemresource"
URL url=new URL("oschina://www.apptest.com:8080/test/ios.php");If you are not used to it, the following exceptions will always occur
java.net.MalformedURLException: unknown protocol
When using Ajax in Android browsers, undefined protocols will not be supported.
2. Understanding of custom protocols
Protocol: In the programming world, the protocol itself is a set of Input/ouput constraint rules. Therefore, our exact protocol should revolve around I/O, so the protocol here can be called the I/O protocol.
Agreement initiator: request
Protocol responder: response
The conditions for the establishment of the agreement are: request and response recognize the same set of agreements and communicate according to the agreement constraints.
3. The relationship between custom protocol and URL
In Java, does custom protocols require URLs?
The answer is no.
In fact, around I/O, our rule definitions are completely in our own hands. It does not say that the earth will not turn around after leaving the URL, and Java will be destroyed.
Why customize the protocol using URL classes?
The answer is because URL is a mature protocol communication processing framework.
The custom URL protocol mentioned here is essentially more about expanding the protocol through existing rules.
4. URL custom private protocol practice
We know that custom protocols require Response and Request, and both parties need to fully understand each other's agreement. For convenience here, we use the Http protocol server as a Response.
Here we use Ngnix server + PHP + FastCGI to build Reponse, and the deployment code is as follows
1. Define Response
<?php$raw_post_data = file_get_contents('php://input', 'r'); echo "-------/$_POST------------------/n<br/>"; echo var_dump($_POST) . "/n"; echo "-------php://input-------------/n<br/>"; echo $raw_post_data . "/n<br/>"; $rs = json_encode($_SERVER);file_put_contents('text.html',$rs);echo '写入成功';2. Define Request
2.1 Implementing the URLStreamHandlerFactory factory, mainly used to generate protocol processors
public class EchoURLStreamHandlerFactory implements URLStreamHandlerFactory {public URLStreamHandler createURLStreamHandler(String protocol){//Customize different schema requests through the diversion here. Of course, the brain-dead people think that this is the core code. URL is a protocol processing framework. If if-else is the core, is Oracle going to go bankrupt if(protocol.equals("echo") || protocol.equals("oschina")) { return new EchoURLStreamHandler(); //Instantiate the protocol processing Handler } return null; }}2.2 Implementing URLStreamHandler, the main function is to generate the corresponding connector of the protocol
public class EchoURLStreamHandler extends URLStreamHandler {@Overrideprotected URLConnection openConnection(URL u) throws IOException {return new EchoURLConnection(u); //We can also perform corresponding diversion here}} 2.3 Implement URLConnection, which is the customization of protocol communication rules. Here we use HTTP protocol as communication rules. Here we imitate http protocol requests
(The following is the core code. The http protocol borrowed here. Of course, you can interact with various protocols using websocket, smtp, and ftp, instead of the if-else+URL prefix that brain-dead people ask me to admit )
public class EchoURLConnection extends URLConnection {private Socket connection = null;public final static int DEFAULT_PORT = 80;public EchoURLConnection(URL url) {super(url);}public synchronized InputStream getInputStream() throws IOException {if (!connected) {connect();}return connection.getInputStream();}public synchronized OutputStream getOutputStream() throws IOException {if (!connected) {connect();}return connection.getOutputStream();}public String getContentType() {return "text/plain";}public synchronized void connect() throws IOException {if (!connected) {int port = url.getPort();if (port < 0 || port > 65535)port = DEFAULT_PORT; this.connection = new Socket(url.getHost(), port);// true means to turn off the buffering of the Socket and send data immediately.. The default value is false// If the underlying implementation of Socket does not support the TCP_NODELAY option, SocketExceptionthis.connection.setTcpNoDelay(true);// Indicates whether reuse of the local address bound to the Socket is allowed this.connection.setReuseAddress(true);// Indicates the waiting timeout when receiving data, in milliseconds.. The default value is 0, which means that there will be infinite waiting and will never timeout// When reading data through the Socket input stream, if there is no data, it will wait// After the timeout, the SocketTimeoutException will be thrown, and after the exception is thrown, the Socket is still connected, you can try to read the data again this.connection.setSoTimeout(30000);// Indicates whether the underlying Socket is immediately closed// When Socket is closed, the underlying Socket will be delayed by 5 seconds and then closed again. After 5 seconds, all remaining data that has not been sent will be discarded. By default, if the Socket.close() method is executed, the method will return immediately, but the underlying Socket is not actually closed immediately// It will delay for a period of time until all the remaining data is sent, and the Socket will be truly closed and disconnected// Tips: When the program writes data through the output stream, it only means that the program submits a batch of data to the network, and the network is responsible for sending it to the receiver // Tips: When the program closes the Socket, it is possible that the batch of data is still transmitted on the network and has not reached the receiver // Tips: The "remained remaining data" mentioned here refers to this data that is still transmitted on the network and has not been received by the receiver this.connection.setSoLinger(true, 5);// Indicates the size of the buffer where the data is sent this.connection.setSendBufferSize(1024);// Indicates the size of the buffer where the data is received this.connection.setReceiveBufferSize(1024);// Indicates the size of the buffer where the data is received this.connection.setReceiveBufferSize(1024);// Indicates whether to automatically close the Socket that is idle for a long time (the two ends of the connection do not transmit data to each other)? True is yes // Its default value is false, which means that TCP will not monitor whether the connection is valid, and inactive clients may exist permanently without notice that the server has crashed this.connection.setKeepAlive(true); // Indicates whether it supports sending one byte of TCP emergency data. socket.sendUrgentData(data) is used to send one byte of TCP emergency data // It defaults to false, that is, the receiver does not do any processing when receiving the emergency data and directly discards it. If the user wants to send emergency data, it should be set to true// After setting to true, the receiver will put the received emergency data in the same queue as the normal data this.connection.setOOBInline(true);// This method is used to set the service type. The following code requests high reliability and minimum delay transmission service (bits or operations of 0x04 and 0x10)// Socket class uses 4 integers to represent the service type// 0x02: low cost (the penultimate bit of binary is 1)// 0x04: high reliability (the penultimate bit of binary is 1)// 0x08: maximum throughput (the fourth penultimate bit of binary is 1)// 0x10: minimum delay (the fifth penultimate bit of binary is 1) this.connection.setTrafficClass(0x04 | 0x10);// This method is used to set the relative importance of connection time, delay, and bandwidth (the three parameters of this method represent three indicators of network transmission data)// connectionTime-This parameter means establishing a connection with the minimum time// latency-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- url.getPath() + " HTTP/1.1 /r/n");//if(url.getPort()<0 || url.getPort()>65536){sb.append("Host:").append(url.getHost()).append("/r/n");}else{sb.append("Host:").append(url.getHost()).append(":").append(url.getPort()).append("/r/n");}sb.append("Connection:keep-alive/r/n");sb.append("Date:Fri, 22 Apr 2016 13:17:35 GMT/r/n");sb.append("Vary:Accept-Encoding/r/n");sb.append("Content-Type: application/x-www-form-urlencoded,charset=utf-8/r/n");sb.append("Content-Length: ").append("name=zhangsan&password=123456".getBytes("UTF-8").length).append("/r/n");sb.append("/r/n");this.connection.getOutputStream().write(sb.toString().getBytes("UTF-8"));}}public synchronized void disconnect() throws IOException {if (connected) {this.connection.close();this.connected = false;}}} Here, the protocol definition has been completed.
Our test code is as follows
Try to connect to oschina://localhost:8080/test/ios.php
URL.setURLStreamHandlerFactory(new EchoURLStreamHandlerFactory());// URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory());URL url=new URL("oschina://localhost:8080/test/ios.php");EchoURLConnection connection=(EchoURLConnection)url.openConnection();connection.setDoOutput(true);connection.setDoInput(true); PrintWriter pw = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()));pw.write("name=zhangsan&password=123456");pw.flush(); InputStream stream = connection.getInputStream(); int len = -1; byte[] buf = new byte[256]; while((len=stream.read(buf, 0, 256))>-1) { String line = new String(buf, 0, len); if(line.endsWith("/r/n0/r/n/r/n")&&len<256) { //The server returns a Transfer-chunked encoding, /r/n0/r/n/r/n means that the read has ended, chunked encoding analysis: http://dbscx.iteye.com/blog/830644 line = line.substring(0, line.length()-"/r/n0/r/n/r/n".length()); System.out.println(line); break; }else{ System.out.println(line); } } pw.close(); stream.close();Running results
The result shows that the protocol has indeed been defined successfully
Of course, the above data analysis does not meet our requirements because it is chunked encoding information. How to analyze it meets the requirements, please move :
HTTP Chunked data encoding and parsing algorithm
5. Later on, custom mineType parser
ContentHandlerFactory is provided in Java to parse mineType. We formulate our own parser here. Of course, the JDK provides more abundantly. What we do here is to meet special needs.
public class EchoContentHandler extends ContentHandler {public Object getContent(URLConnection connection) throws IOException {InputStream in = connection.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(in));return br.readLine();}public Object getContent(URLConnection connection, Class[] classes) throws IOException {InputStream in = connection.getInputStream();for (int i = 0; i < classes.length; i++) {if (classes[i] == InputStream.class)return in;else if (classes[i] == String.class)return getContent(connection);}return null;}}The usage is very simple
URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory());