本帖最后由 berry64 于 2021-3-12 03:52 编辑 
| 
 | 
| 
 | |
| HTTP (Hyper Text Transfer Protocol) 就是我们平时上网时浏览器与网站服务器交流的协议,因为本教程再怎么说也是教插件的所以如果想要仔细了解http的具体内容请自行搜索. 为什么插件会要用到http? 在网络上有很多方便的API, 比如spiget, 或者是开发者自己编写的其他RESTAPI, 甚至有些微软Azure, 谷歌Tensor以及github 的人工智能的应用有不少都可以使用HTTP 的方式来调用,所以如果你的插件会要用到这些功能的话就需要使用HTTP。 目前在网上很多java的http客户端,比如Apache的http-client, MC使用的netty,还有(好像是?)java9里新加入的httpapi,不过为了方便学习&兼容性&我懒&一般插件其实用不到那么复杂的http,本教程将会使用java里面自带的旧的(url connection) API. 并且 | 
| 
 | |
| 首先我们要了解一下HTTP 的几种常见类型以及一般插件会使用到的其他功能: 1. GET 请求 - 这大概是网络上最常见的请求了,顾名思义就是get(获得)一些资料,这个请求不可以附带body(等会会讲) 2. POST - 理论上是用于创建资源的请求,这个请求可以附带body,也就是附加资料(插件大多会是以JSON形式) 3. PUT - 与post同样,不过是用来更新资源的 4. DELETE - 用于删除资源 还有很多不同请求,这个网上都有的 虽然是这样定义的。。。但是众所周知软性规矩基本上是没有人遵守的,所以一般理解为GET就是用于不需要客户端/插件提供数据的,post/put/delete就是需要插件提供数据的,如果你用的api没有遵循具体的这些请求类型的规定就...蛮正常的。 还有返回值一些常用的: - 200 正确 - 400 客户端错误 - 喜闻乐见的404是不存在的 - 500 服务器错误 - 302 重定向 - 418 划重点 这个要考 最后一个就是HTTP的Header, 不管是GET还是任何其他请求都可以提供Header, 这个header一般用于提供关于这个请求的资料比如: - User-Agent 我们在用什么东西发送这个请求? - Content-Type 如果是有内容的请求,我们的请求内容是什么格式的? - Accepts 我们希望什么样的回复? 还有很多很多,甚至很多时候登陆/注册账号的时候都是把资料放在这个header里面,不过具体要看你服务器的要求了 | 
| 
 | |
| 先放一大段 
 
 1. 如果是https怎么办 2. 读取回来个stream要老子怎么搞mmp 1. 如果我们查阅Java内部代码可以发现java的HttpsURLConnection 实际上是HttpUrlConnection的子类,也就是说Java自动会帮我们处理HTTPS的TLS以及其他加密步骤。 2. Stream 是为了增加灵活性,因为不是所有时候http的返回值都是一次完成的,有时候可能分开成好几段(跟你下载盗版游戏压缩包好几个一个意思),有时候可能是实时的(直播的时候)不过一般mc不太会用到,所以在这里提供一个简易的把stream读取成字符串string的方法,这个就不解释了,如果想学习可以自己慢慢品: 这个时候如果你运行 StreamUtils.getStringFromStream(con.getInputStream()); 你获得的就会是服务器的返回内容啦! 如果需要json... 自行搜索GSON, 阿里巴巴的fastjson, apache也有json,自己去玩吧反正现在有string了[br] 那么如何像服务器发送post这种带内容的请求呢? 
 
 | 
| 
 | |
| 众所周知mc服务器是一个对多线程非常友好的程序,比如基本上所有东西都是同一个线程运行的,也就是说如果java再等待你的http请求回复的时候是不会进行服务器的计算的,就像你盯着寒假作业就算有时间也不会写一样,服务器会空空等着http请求结束才会继续运算。一般http请求都很快,几十毫秒就结束了所以服务器上并不会感觉到太大的卡顿,可是当网络掉链子的时候,服务器就会一直等6秒(我们刚刚设置的)之后才会意识到这个请求发不出去,然后丢出异常。   这个问题很容易解决,就像如果你学会影分身之后就可以让影分身替你去玩游戏,然后 到这里,你可能想起来我之前教程讲过的BukkitRunnable. 当然这里完全可以用BukkitRunnable来实现不过我们今天要讲的是BukkitRunnable基于的Java自带的Runnable类。在Java里实现多线程有很多办法,不过本教程将只会解释利用Runnable与new Thread的方法。先上一个模板: 
 拆开解释一下:在第一行我们创建了一个继承Runnable接口的类,这个类指明了一个方法叫run(); 没错就和bukkitrunnable里面的是一模一样的。 然后为了我们自己方便我们创建了一个start()方法,在这个方法里我们: 1. 检测了我们没用尝试多线程一个多线程(是不是不合理,那就对了所以不能这么搞) 2. 如果没有,那么创建一个新的线程 3. 开始运行这个线程 这样操作之后就就可以使用new MyClass().start()运行了,这时候服务器就像这样了:   可以看到现在虽然说加了一个步骤,但是可以肯定new MyClass().start()的运行时间绝对比发送并等待http快得多,而且所有等待都会同步进行。所以我们就把刚刚那个HTTP 的代码放到这个run里面来再给他搞一个构造函数,这个样子 
 | 
| 
 | |
| 异步发送http做到了,那么异步读取呢?这里我们将会使用到Java8的Completable Future 因为如果我们要读取http的信息,那么我们的http请求就肯定需要结束,而且反正我们为了发送http请求新开了一个线程,所以我们就可以直接使用这个线程来读取信息。那么我们可以这样写代码 
 
 这里你可能会很疑惑,这个thenaccpet到底干了什么。 要知道Java本身并不会把所有东西异步处理,所以当我们运行到这一行的时候虽然我们运行了start方法,但是这个方法仍然是在祝线程运行的,也就是说当我们从左到右运行到thenaccept的时候,实际上虽然线程已经创建了,但是http请求估计还没有完成,这里我们使用thenAccept就相当于向CompletableFuture里面存了一段代码,而这段代码将会在我们调用task.complete()的时候被调用,并且这段代码会有一个string函数,也就是我们处理出来的http返回值,这样我们就可以保证是http请求完成之后再运行的这段代码。 | 
| 
 | |
| 至此我们已经差不多讲解了java里面使用http的基础以及如果用多线程来实现异步获取http资源,但是我们这里要注意一个点:因为MC卓越的多线程兼容性,如果你试着在thenaccept里面(记得我们会在新的线程里运行这个方法,也就是不是跟服务器同一个线程的)实行一些mc游戏的操作,比如更改世界的一个方块那么轻则报错重则死机,所以这里我们可以用Bukkit提供的一个方法 new BukkitRunnable(代码).runTask(this); 这个将会把你的代码在下一个tick同步运行,也就是说我们可以从其他线程里向主线程添加任务了。 在本教程里我们并没有讲解POST请求的实行方式,这里留给你自己摸索,欢迎回复本帖放上你自己的答案哦~ (注意post请求的话你还可以自己提供数据向用http请求发送) | 
哇,你是真的厉害,受教了
少熬夜,真的辛苦你了,太谢谢了,我感觉学费省了
感谢你的教程!
这真的是零基础的吗
跳到这有点蒙,希望把之前过期的帖子解锁一下...
一脸蒙什么的看不懂
终于更新了啊
很喜欢你的教程,希望继续更新qwq
感谢教程                           
感谢教程
太谢谢了,我感觉学费省了
学习了11111111111111111
可以先去熟悉熟悉一些插件,会改插件之类的再来学习这个就很简单了
真的好有用