先上代码
ByteBuffer readBuffer = ByteBuffer.allocate(requestSize);// 这里是 1024*1024*1024 = 1G
readBuffer.clear();
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
while (socketChannel.read(readBuffer) > 0) {}
/* 获取请求报文 */
readBuffer.flip();
byte[] bytes = new byte[readBuffer.limit()];
readBuffer.get(bytes);
如上面代码所示,缓冲区 ByteBuffer 的容量设置成了 1G,但是上传一个 30M 左右的文件,经常会丢包,导致文件不全,然后 formData 解析时报错。
这个缓冲区的容量如果比文件大很多,则一点问题都没,但是我又不能把缓冲区设置的太大,否则会堆溢出。
麻烦熟悉 NIO 的 大神们指点一下
1
BBCCBB 2020-12-12 21:33:43 +08:00 1
这个 read(buf)方法返回 0 也是正常的现象, 各种情况会返回 0, 但是是正常现象, 所以这里这个 while 应该不对
要自己定一个协议, 数据缓存起来, 只要 read 不是返回-1, 就继续往 buf 里写数据, 直到达到你定义的协议的包的结束点.. 比如定长的协议. |--- length-- | your data |, eg: 开头 4 个字节代表数据包长度, 读到这么长的数据才算完成读取. 可以直接用 netty, 看看里面的 LengthFieldBasedFrameDecoder 实现, 直接用 netty 吧. 看懂里面的实现就行. |
2
BBCCBB 2020-12-12 21:36:01 +08:00
我百度的返回 0 的情况:
其实 read 返回 0 有 3 种情况,一是某一时刻 socketChannel 中当前(注意是当前)没有数据可以读,这时会返回 0,其次是 bytebuffer 的 position 等于 limit 了,即 bytebuffer 的 remaining 等于 0,这个时候也会返回 0,最后一种情况就是客户端的数据发送完毕了(注意看后面的程序里有这样子的代码),这个时候客户端想获取服务端的反馈调用了 recv 函数,若服务端继续 read,这个时候就会返回 0 。 |
3
Joker123456789 OP @BBCCBB 谢谢,我尝试了一下改成 -1,但是 这样一来就一直死循环了,只有强行取消本次请求 才会变成-1,否则到 0 就下不去了,有点头疼。
|
4
sagaxu 2020-12-12 23:04:57 +08:00 via Android
socket 是个 stream,你有两个选择,
1. 短连接,每次写入完 close,读的读到-1 结束 2. 长连接,自定义协议确定边界,支持复用 循环读,-1 就结束,0 继续等待可读通知,遇到边界就解出一个完整报文放入处理队列 |
5
Joker123456789 OP |
6
laminux29 2020-12-13 01:38:17 +08:00
1.Buffer 只是个临时缓存,requestSize 没必要等于 file size,不然百度网盘的 SVIP 价格得翻几倍。你下载一个迅雷,以及其他 BT 软件,看看里面的硬盘缓存最大值是多少。
一般来说 requestSize 的 sizes 是 4k 的倍数,老旧服务端一般是 4/8/16 KB,再有钱的 bat 也最多 1/2MB 。 2.建议 debug,看看 ByteBuffer.allocate 之后里面到底是什么,有没有必要再做 clear()。 3.里面很多方法都会抛异常,建议阅读文档,该处理的一定要处理。 4.while (socketChannel.read(readBuffer) > 0) {} ,这也是没读文档造成的。先不说异常处理,read 方法有几种返回值,认真看一下文档。 5.重新改正后,先找个 100 字节文档测试一下,java 端收到后立马 write file,然后与 source file 对比一下内容。没问题后再测试 100KB file 、100MB file 甚至几十 GB 的 file 。先 md5 对比,不一样后再找一款基于 byte 对比的软件,比如 BeyondCompare 、TextDiff 甚至 Ultra Edit 。 |
7
zacharyjia 2020-12-13 09:42:25 +08:00
@Joker123456789 Http 的话,header 里面应该有当前请求的长度,可以根据它判断边界
|
8
cheng6563 2020-12-13 12:21:22 +08:00 via Android
http 一是看 content-length 判断长度,
如果没有 content-length 则是一行空行表示结束。 |
9
chenshun00 2020-12-13 13:02:05 +08:00
NIO 开发 http,你这样解析 http 的呀,可以先链接一下 netty 的 http 协议是怎么做的。 这样的读取肯定是不行的.
|
10
Joker123456789 OP @zacharyjia
@cheng6563 @laminux29 @BBCCBB 谢谢大佬们,已经解决了,通过查看读到的内容中有没有 \r\n\r\n 来判断头读完了没,如果读完了则获取到 Content-Length, 然后继续读 body,再判断已经读到的 body 的长度是否>= Content-Length 。 |
11
lei2j 2020-12-13 14:25:27 +08:00
为啥不用 netty 呢
|
12
Joker123456789 OP @lei2j 不想依赖第三方,netty 很好很强大,但是里面我用不到的东西也有很多,不够小。
|
13
shentar 2020-12-13 15:33:33 +08:00
@Joker123456789 看起来需要用 select 啊,监听读写事件。还是要读一下 netty,jetty 的网络处理部分: https://codefine.site/652.html
|