Java URL自定义私有网络协议

这篇具有很好参考价值的文章主要介绍了Java URL自定义私有网络协议。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

URI (uniform resource identifier)统一资源标志符;
URL(uniform resource location )统一资源定位符(或统一资源定位器);
URI是一个相对来说更广泛的概念,URL是URI的一种,是URI命名机制的一个子集,可以说URI是抽象的,而具体要使用URL来定位资源。
URI指向的一般不是物理资源路径,而是整个系统中的映射后的资源标识符。
URL是Internet上用来描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上。采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。
一.先来序言一段

我们习惯了http

 URL url=new URL("http://www.apptest.com:8080/test/ios.php");

我们也要习惯

 "https", "ftp", "mailto", "telnet", "file", "ldap", "gopher",  "jdbc", "rmi", "jndi", "jar", "doc", "netdoc", "nfs", "verbatim", "finger", "daytime",
  "systemresource"

当然,我们还要让URL习惯我们

 URL url=new URL("oschina://www.apptest.com:8080/test/ios.php");

如果不习惯,总会出现如下异常

java.net.MalformedURLException: unknown protocol

在Android浏览器使用Ajax时也会不支持没有定义的过的协议。

二.协议的自定义的理解

协议:在编程的世界里,协议本身就是一套Input/ouput约束规则,因此,我们确切的协议应该围绕I/O展开的,所以,这里的协议可以称为I/O协议。

协议发起方:request

协议响应方:response

协议成立的条件是:request和reponse认可同一套协议,并按照协议约束进行通信。

三.自定义协议与URL的关系

在java中,自定义协议一定需要用URL吗?

答案是否定的。

 事实上,围绕I/O,我们的规则定义完全有我们本身掌握,并没有说离开URL地球不转了,Java要毁灭了。

为什么使用URL类来自定义协议?

答案是因为URL是一套成熟的协议通信处理框架。

这里说的自定义URL协议,实质上更多的是通过已有的规则进行扩充协议。

四.URL自定义私有协议实战

我们知道,自定义协议需要Response 和Request,双方需要充理解对方的协议。这里为了方便起见,我们使用Http协议服务器来作为Response。

这里我们使用了Ngnix服务器+PHP+FastCGI来构建Reponse,部署代码如下

1.定义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.定义Request

2.1实现URLStreamHandlerFactory工厂,主要用来产生协议处理器

public class EchoURLStreamHandlerFactory implements URLStreamHandlerFactory {
	
	public URLStreamHandler createURLStreamHandler(String protocol){
	//通过这里的分流处理不同的schema请求,当然脑残人士认为这里才是核心代码,URL是一套协议处理框架,如果if-else就是核心,是不是oracle要倒闭
	    if(protocol.equals("echo") || protocol.equals("oschina"))  
	    {
	        return new EchoURLStreamHandler(); //实例化协议处理Handler
	    }
	   return null;
	  
	}
	
}

2.2实现URLStreamHandler,主要作用是生成协议对应的连接器

public class EchoURLStreamHandler extends URLStreamHandler {

	@Override
	protected URLConnection openConnection(URL u) throws IOException {
		return new EchoURLConnection(u);  //在这里我们也可以进行相应的分流
	}
}

2.3  实现URLConnection,作用是协议通信规则的自定义,这里我们使用HTTP协议作为通信规则,我们这里仿制http协议请求

(以下才是核心代码,这里借用的http协议,当然你可以用websocket,smtp,ftp各种协议进行交互,而不是脑残人士让我承认的 if-else+URL前缀)

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表示关闭Socket的缓冲,立即发送数据..其默认值为false
			// 若Socket的底层实现不支持TCP_NODELAY选项,则会抛出SocketException
			this.connection.setTcpNoDelay(true);
			// 表示是否允许重用Socket所绑定的本地地址
			this.connection.setReuseAddress(true);
			// 表示接收数据时的等待超时时间,单位毫秒..其默认值为0,表示会无限等待,永远不会超时
			// 当通过Socket的输入流读数据时,如果还没有数据,就会等待
			// 超时后会抛出SocketTimeoutException,且抛出该异常后Socket仍然是连接的,可以尝试再次读数据
			this.connection.setSoTimeout(30000);
			// 表示当执行Socket.close()时,是否立即关闭底层的Socket
			// 这里设置为当Socket关闭后,底层Socket延迟5秒后再关闭,而5秒后所有未发送完的剩余数据也会被丢弃
			// 默认情况下,执行Socket.close()方法,该方法会立即返回,但底层的Socket实际上并不立即关闭
			// 它会延迟一段时间,直到发送完所有剩余的数据,才会真正关闭Socket,断开连接
			// Tips:当程序通过输出流写数据时,仅仅表示程序向网络提交了一批数据,由网络负责输送到接收方
			// Tips:当程序关闭Socket,有可能这批数据还在网络上传输,还未到达接收方
			// Tips:这里所说的"未发送完的剩余数据"就是指这种还在网络上传输,未被接收方接收的数据
			this.connection.setSoLinger(true, 5);
			// 表示发送数据的缓冲区的大小
			this.connection.setSendBufferSize(1024);
			// 表示接收数据的缓冲区的大小
			this.connection.setReceiveBufferSize(1024);
			// 表示对于长时间处于空闲状态(连接的两端没有互相传送数据)的Socket,是否要自动把它关闭,true为是
			// 其默认值为false,表示TCP不会监视连接是否有效,不活动的客户端可能会永久存在下去,而不会注意到服务器已经崩溃
			this.connection.setKeepAlive(true);
			// 表示是否支持发送一个字节的TCP紧急数据,socket.sendUrgentData(data)用于发送一个字节的TCP紧急数据
			// 其默认为false,即接收方收到紧急数据时不作任何处理,直接将其丢弃..若用户希望发送紧急数据,则应设其为true
			// 设为true后,接收方会把收到的紧急数据与普通数据放在同样的队列中
			this.connection.setOOBInline(true);
			// 该方法用于设置服务类型,以下代码请求高可靠性和最小延迟传输服务(把0x04与0x10进行位或运算)
			// Socket类用4个整数表示服务类型
			// 0x02:低成本(二进制的倒数第二位为1)
			// 0x04:高可靠性(二进制的倒数第三位为1)
			// 0x08:最高吞吐量(二进制的倒数第四位为1)
			// 0x10:最小延迟(二进制的倒数第五位为1)
			this.connection.setTrafficClass(0x04 | 0x10);
			// 该方法用于设定连接时间,延迟,带宽的相对重要性(该方法的三个参数表示网络传输数据的3项指标)
			// connectionTime--该参数表示用最少时间建立连接
			// latency---------该参数表示最小延迟
			// bandwidth-------该参数表示最高带宽
			// 可以为这些参数赋予任意整数值,这些整数之间的相对大小就决定了相应参数的相对重要性
			// 如这里设置的就是---最高带宽最重要,其次是最小连接时间,最后是最小延迟
			this.connection.setPerformancePreferences(2, 1, 3);

			this.connected = true;

			StringBuilder sb = new StringBuilder();
			sb.append("POST " + 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;
		}
	}
}

在这里,协议定义已经完成。

我们测试代码如下

尝试连接  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) 
	    	{    
	    	        //服务器返回的是Transfer-chunked编码,\r\n0\r\n\r\n表示读取结束了,chunked编码解析: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();

运行结果

结果说明,协议确实定义成功了

当然,如上数据解析不符合我们的要求,因为是chunked编码信息,如何解析符合要求有,请移步

HTTP Chunked数据编码与解析算法

我们可以通过如下方式解析数据

            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();
	    HttpInputStream his = new HttpInputStream(stream);
	    System.out.println(his.getHttpHeaders());
	    int len = 0;
	    byte[] buf = new byte[127];
	    StringBuilder sb = new StringBuilder();
	    while((len=his.read(buf, 0, buf.length))!=-1)
	    {
	    	sb.append(new String(buf, 0, len));
	    }
	    
	    System.out.println(sb.toString());
五.后话,自定义mineType解析器

java中提供了ContentHandlerFactory,用来解析mineType,我们这里制定我们自己的解析器,当然,JDK中提供的更丰富,这里所做的只是为了符合特殊需求

public class EchoContentHandlerFactory implements ContentHandlerFactory{
	
  public ContentHandler createContentHandler(String mimetype){
    if(mimetype.equals("text/html") || mimetype.equals("text/plain")){
      return new EchoContentHandler();
    }else{
      return null;
    }
  }
}
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;
	}
}

用法很简单

  URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory());

作者:IamOkay

原文地址:https://my.oschina.net/ososchina/blog/664743文章来源地址https://www.toymoban.com/news/detail-835453.html

喜欢 0

到了这里,关于Java URL自定义私有网络协议的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java检测网络是否连通检查ip、URL和API接口

    只能通过状态码是否为404判断,如果网络不通则会连接失败到 catch 中返回false 请求方式必须全部字母大写,比如POST请求,不能写成post请求 如果请求方式错误,并不会连接失败,而是请求码得到的为405错误。但是得到的是405状态码非200状态码,可以显示连通。 JAVA检测url是否

    2024年02月10日
    浏览(42)
  • java minio通过getPresignedObjectUrl设置(自定义)预签名URL下载文件的响应文件名之minio源码改造方案

    用户上传文件到Minio时,一般存储在Minio中的对象名称都是后端以UUID或者其他随机或非随机方案生成的唯一标识做为文件名,这个对象名称一般都不会是用户上传时的原文件名称。 在用户下载时,想让文件流不通过后端服务器,而是用户直接申请并使用某个要下载对象的Min

    2024年01月25日
    浏览(64)
  • tomcat(跟着宝哥学java:tomcat)tomcat安装 发布项目 配置eclipse http协议详解、get请求、post请求、url详解

    在%CATALINA_HOME%webapps下创建一个文件夹:hehe 在hehe中创建子文件夹:WEB-INF和资源文件夹(html/jsp/css/imgs/js) 在WEB-INF中创建子文件夹classes::存储java源文件生成的字节码文件 在WEB-INF中创建子文件夹lib::存储项目以来的jar 在WEB-INF中创建子web项目的核心配置文件:web.xml web

    2024年02月03日
    浏览(45)
  • 【华为OD机考 统一考试机试C卷】拼接URL(C++ Java JavaScript Python C语言)

    目前在考C卷,经过两个月的收集整理, C卷真题已基本整理完毕 抽到原题的概率为2/3到3/3, 也就是最少抽到两道原题。 请注意:大家刷完C卷真题,最好要把B卷的真题刷一下,因为C卷的部分真题来自B卷。 另外订阅专栏还可以联系笔者开通在线OJ进行刷题,提高刷题效率。

    2024年02月19日
    浏览(37)
  • 基于JAVA开发的数字化智慧工地管理平台源码,可私有化部署、带可视化大屏

    智能工地应用价值 智慧工地现场构建了基于物联网的智能化数据传感器通用的管理平台。利用计算机、人工智能、无线通信,全天候现场监视、施工检查、质量管理、服务,提高数字化管理、安全、绿色、施工等现场管理能力,标志着现场管理进入信息化时代。 1、支持产业

    2024年01月21日
    浏览(71)
  • JAVA通过HTTP协议下载网络文件

            分享一个通过原生的Java代码下载网络资源文件方法,使用URL类连接上网络服务器通过多线程使用I/O流的方式下载文件。 流程: 获取到资源的大小及各项参数 使用RandomAccessFile类生成对应大小的文件用于防止空间不足 启动下载任务线程,传入线程号等参数 下载线程与

    2024年02月11日
    浏览(51)
  • 【Java】--网络编程:基于TCP协议的网络通信

    TCP协议(Transmission Control Protocol),即传输控制协议,是一种 面向连接 的, 可靠 的,基于 字节流 的传输层通信协议。数据大小无限制。 建立连接的过程需要 三次握手 。 断开连接的过程需要 四次挥手 。 使用TCP协议的通信双方分别为 客户端 和 服务器端 。 客户端负责向服务

    2024年01月23日
    浏览(61)
  • Java 网络编程 —— 客户端协议处理框架

    Java 对客户程序的通信过程进行了抽象,提供了通用的协议处理框架,该框架封装了 Socket,主要包括以下类: URL 类:统一资源定位符,表示客户程序要访问的远程资源 URLConnection 类:表示客户程序与远程服务器的连接,客户程序可以从 URLConnection 获得数据输入流和输出流

    2024年02月07日
    浏览(45)
  • Java网络编程 *TCP与UDP协议*

    把分布在 不同地理区域 的具有独立功能的计算机, 通过通信设备与线路 连接起来,由功能完善的软件实现资源共享和信息传递的 系统 简单来说就是把不同地区的计算机通过设备连接起来,实现不同地区之前的数据传输 网络编程 是借助计算机网络,实现我们所写的程序,在不同

    2024年01月16日
    浏览(58)
  • HarmonyOS鸿蒙基于Java开发: Java UI 自定义布局

    当Java UI框架提供的布局无法满足需求时,可以创建自定义布局,根据需求自定义布局规则。 Component类相关接口  表1  Component类相关接口 接口名称 作用 setEstimateSizeListener 设置测量组件的侦听器 setEstimatedSize 设置测量的宽度和高度 onEstimateSize 测量组件的大小以确定宽度和高度

    2024年02月19日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包