websocket的发送接收数据的实现(java实现)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
websocket的发送接收数据的实现(java实现)
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
---------------------------------------------------------发送功能-------------------------------------------------------------------------------------
在上⼀篇⽂章中我深刻地认识了websocket数据帧的格式,还是从数据帧⼊⼿,我刚⼊门,就不写太复杂的,以免⾃⼰以后回头看也看不懂。
只要懂得如何接收,就可以知道如何发送,接收数据是拆包嘛,那发送数据就要进⾏包装,
第⼀个字节是0x81(1000 0001)或者0x82(1000 0010),1是字符数据,2是⼆进制数据,如果单纯发送⽂字,就1,如果还要发送⽂件就2,8就没什么好说的,数据不分⽚(我也不知道要怎么分);
如下如下第⼆个字节是掩码⼀位+PayLoadLen,服务端发送给客户端的数据没有严格要求⼀定要掩码,那就设为0,PayLoadLen的值就等于数据的长度或者126(126<=数据长度<=0xff)或者127(0x100<=数据长度<=0xffffffff);
如果PayLoadLen的值等于数据的长度,那接下来从第三个字节开始就是数据部分PayLoadData;
那如果PayLoadLen的值等于126,则接下来第三,第四字节就是extendedPayLoadLen的值;
如果PayLoadLen的值等于127,则接下来第三到第⼗字节就是extendedPayLoadLen的值;在extendPayLoadLen后的字节就是PayLoadData。
因为发送的数据帧没有掩码,所以maskingkey4个字节也不⽤写。
代码如下:
public void sendMsg(String msg) throws IOException {
byte[] data = msg.getBytes("UTF-8");
int payLoadlen = data.length;
byte[] first = new byte[1];
first[0] = (byte) 0x81; //1000 0010 ,表⽰最后⼀个分⽚,数据为字符
byte mask = 0; //先试试没有mask的情况,之后再试试有mask的情况
byte[] second = new byte[1];
byte[] extendedLoad = null;
if (payLoadlen < 126){
payLoadlen = payLoadlen; //脱裤⼦放屁
}else if (payLoadlen < (0xffff)){
//00000000 00000000 11111111 11111111
extendedLoad = new byte[2];
int index = 0;
int rshift = 8;
while(index < 2){
extendedLoad[index++] = (byte) (payLoadlen>>rshift&0xff);
rshift-=8;
}
payLoadlen = 126;
}else{
//范围:0x00010000 - 0x0fffffff,最⾼位为0
//这⾥其实有⼀个问题,就是发送的数据理论上能达到2^64-1,
// 但是就现实⽽⾔,能达到2^31-1(即int类型的最⼤值)都难
// 因为数组length的返回值是int类型,最⼤也就32位了,所以,我只能将4个⾼位设置成全0)
extendedLoad = new byte[8];
int index = 0;
while(index < 4){
extendedLoad[index++] = 0;
}
int rshift = 24;
while(index < 8){
extendedLoad[index++] = (byte) (payLoadlen>>rshift&0xff);
rshift-=8;
}
payLoadlen = 127;
}
second[0] = (byte) (payLoadlen&((mask<<8)+0x7f));
byte[] msgbyte = null;
if (extendedLoad == null){
msgbyte = new byte[first.length+second.length+data.length];
System.arraycopy(first,0,msgbyte,0,first.length);
System.arraycopy(second,0,msgbyte,first.length,second.length);
System.arraycopy(data,0,msgbyte,first.length+second.length,data.length);
}else{
msgbyte = new byte[first.length+second.length+extendedLoad.length+data.length];
System.arraycopy(first,0,msgbyte,0,first.length);
System.arraycopy(second,0,msgbyte,first.length,second.length);
System.arraycopy(extendedLoad,0,msgbyte,first.length+second.length,extendedLoad.length);
System.arraycopy(data,0,msgbyte,first.length+second.length+extendedLoad.length,data.length);
}
outs.write(msgbyte);
outs.flush();
}
---------------------------------------------------------------------接收功能-------------------------------------------------------------------------
接收功能改动不⼤,只是把while⾥的代码写成⼀个⽅法,然后是payloadlen=127那⾥改成了实际上只有四个字节的数据,⾼位当作0读取,和上⾯发送功能的⼀样。
理由如下:
payloadlen=126时,最⼤的接收数据是0xffff,只有16位,2个字节,⼀位表⽰1字节的话也就是64KB,如果发送的⽂件稍⼤⼀点可能就不⾏了,所以127有必要⽤,但是127时最⼤的接收数据有64位,占数据帧8个字节,⽽java中int类型的数据占32位,4个字节,然后int类型的最⼤值是2147483647,能表⽰最⼤⼤约2G的数据长度,以我需求来说,4个字节的数据长度完全够⽤,就不⽤long类型了。
发送和接收数据的时候应该是很少遇到超过int类型表⽰的范围的。
//接收数据的⽅法
public void recvMsg() throws IOException {
byte[] first = new byte[1];
ins.read(first); //将流中第⼀个字节的数据读⼊first
int finflag = (first[0]&0x80)>>7; //终⽌位信息fin = (xyyyyyyyy & 10000000)>>7 = x,
int opcode = first[0]&0x0f; //表⽰接收的信息类型opcode = yyyyxxxx & 0000ffff = 0000xxxx
//opcode为0时是接收附加数据,1是⽂本数据,2是⼆进制流数据,8是关闭连接
if (opcode == 8){
System.out.println("连接被浏览器关闭");
this.close();
return ;
}
byte[] second = new byte[1];
ins.read(second); //读⼊第⼆个字节
int mask = (second[0]&0x80)>>7; //是否有掩码,从浏览器发送过来的信息⼀定有,mask = (xyyyyyyyy & 10000000)>>7 = x
int payloadlen = second[0]&0x7f; // payloadlen = yxxxxxxxx & 011111111 = 0xxxxxxxx
int extendPayloadLen = 0; //
if (payloadlen == 126){
// 若payloadlen == 126,则接收的数据长度为后两个字节的值,
int index = 0;
byte[] extended = new byte[2];//附加长度,下同
ins.read(extended);
while(index < 2){
extendPayloadLen = extendPayloadLen<<8;
extendPayloadLen += (extended[index++]&0xff);
}
payloadlen = extendPayloadLen;
}else if(payloadlen == 127){
// 若payloadlen == 127,则接收的数据长度为后⼋个字节的值。
int index = 0;
byte[] extended = new byte[8];
ins.read(extended);
while (index < 4){
index++;
}
while(index < 8){
extendPayloadLen = extendPayloadLen<<8;
extendPayloadLen += (extended[index++]&0xff);
}
payloadlen = extendPayloadLen;
}else{
// 若payloadlen < 126,接收的数据长度为0~125,
payloadlen = payloadlen;
}
byte[] maskingkey = new byte[4];
ins.read(maskingkey); //读⼊maskingkey
byte[] themsg = new byte[payloadlen]; //假设数据段最多收1024字节
//bains.read(themsg,0,payloadlen); //从themsg的0下标出发,读⼊payloadlen字节
ins.read(themsg,0,payloadlen);
int index = 0;
if (mask == 1){
while(index < payloadlen){
themsg[index] = (byte) ((themsg[index]^maskingkey[index%4])&0xff);
index++;
}
}
System.arraycopy(themsg,0,themsg,0,payloadlen);
String msg = new String(themsg,"UTF-8");
System.out.println(msg);
}
然后既然websocket是全双⼯,那我就把两个功能合起来测试,但是不论是inputstream的read还是scanner.next都会阻塞进程,所以我就写了⼀个给接收数据专⽤的线程,发送数据就还是主线程。
运⾏结果:
HandShake OK
connect success
hello peter
hi?do i know u?
hi?do
i
know
u?
这⾥说明⼀下:‘hello peter‘是浏览器输⼊框输⼊后发送的;‘hi?do i know u?’是在idea运⾏的终端输⼊的,空格就被当作分隔符,下⾯的分段是浏览器返回的,js⾥⾯设置了把接收到的数据发送回去。