最近在用 Workerman 学习 WebSocket 的时候,遇到了一个问题:
结果浏览器报了这个错:
查阅了 RFC文档 得知问题在于新建WebSocket时的第二个参数,表示客户端接受的子协议。
子协议是什么?比如浏览器向服务端发起两个WebSocket连接,一条连接使用明文传输数据,另一条使用加密传输数据。
服务端可以开启两个端口分别处理明文和加密连接。也可以只开一个端口,通过客户端提供的子协议来判断该连接传输数据的方式。
那么新建WebSocket时的子协议参数,最终是以什么形式发送到服务端的?就藏在请求Header的 Sec-Websocket-Protocol 中:
服务端接收到 Sec-Websocket-Protocol 后,必须在响应 Header 中也包含 Sec-Websocket-Protocol ,取值和客户端传来的一样。否则浏览器就会报错: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was receive
再提一点,子协议参数可以是个数组,比如:
表示该WebSocket连接既接收字符格式的数据,也可以接收JSON格式的数据,这时服务端必须在两者中选择其一返回给浏览器。
有点扯远了,今天的主角是 Workerman。Workerman 要获得子协议参数,需要在 onConnect 回调中设置 onWebSocketConnect 回调:
然后通过 $_SERVER['HTTP_SEC_WEBSOCKET_PROTOCOL'] 获得浏览器发来的子协议参数,是个字符串。如果浏览器接受多个子协议,那么协议之间会以 逗号 分隔。方便起见,例子中总是返回第一种子协议。
服务端响应时,将 Sec-Websocket-Protocol 字段放入 $connection->headers 数组中,这样就完成了子协议的协商。
//客户端响应Response.Headers.Add("connection", "keep-alive")
//发起请求
Request.Headers.Add("connection", "keep-alive")
response.setHeader()的用法
1. HTTP消息头
(1)通用信息头
即能用于请求消息中,也能用于响应信息中,但与被传输的实体内容没有关系的信息头,如Data,Pragma
主要: Cache-Control , Connection , Data , Pragma , Trailer , Transfer-Encoding , Upgrade
(2)请求头
用于在请求消息中向服务器传递附加信息,主要包括客户机可以接受的数据类型,压缩方法,语言,以及客户计算机上保留的信息和发出该请求的超链接源地址等.
主要: Accept , Accept-Encoding , Accept-Language , Host ,
(3)响应头
用于在响应消息中向客户端传递附加信息,包括服务程序的名称,要求客户端进行认证的方式,请求的资源已移动到新地址等.
主要: Location , Server , WWW-Authenticate(认证头)
(4)实体头
用做实体内容的元信息,描述了实体内容的属性,包括实体信息的类型,长度,压缩方法,最后一次修改的时间和数据的有效期等.
主要: Content-Encoding , Content-Language , Content-Length , Content-Location , Content-Type
(5)扩展头
主要:Refresh, Content-Disposition
2. 几个主要头的作用
(1)Content-Type的作用
该实体头的作用是让服务器告诉浏览器它发送的数据属于什么文件类型。
例如:当Content-Type 的值设置为text/html和text/plain时,前者会让浏览器把接收到的实体内容以HTML格式解析,后者会让浏览器以普通文本解析.
(2)Content-Disposition 的作用
当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。
Content-Disposition扩展头的例子:
<%@ page pageEncoding="GBK" contentType="text/htmlcharset=utf-8" import="java.util.*,java.text.*" %><%=DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.CHINA).format(new Date())
%>
<%
response.setHeader("Content-Type","video/x-msvideo")
response.setHeader("Content-Disposition", "attachmentfilename=aaa.doc")
%>
Content-Disposition中指定的类型是文件的扩展名,并且d出的下载对话框中的文件类型图片是按照文件的扩展名显示的,点保存后,文件以filename的值命名,保存类型以Content中设置的为准。
注意:在设置Content-Disposition头字段之前,一定要设置Content-Type头字段。
3.如何实现文件下载
要实现文件下载,我们只需要设置两个特殊的相应头,它们是什么头?如果文件名带中文,该如何解决?
两个特殊的相应头:
----Content-Type: application/octet-stream
----Content-Disposition: attachmentfilename=aaa.zip
例如:
response.setContentType("image/jpeg")response.setHeader("Content- Disposition","attachmentfilename=Bluehills.jpg")
如果文件中filename参数中有中文,则就会出现乱码。
解决办法:
(1)MimeUtility.encodeWord("中文.txt")//现在版本的IE还不行(2)new String("中文".getBytes("GB2312"),"ISO8859- 1")//实际上这个是错误的
4. 测试并分析文件名乱码问题
response.setHeader()下载中文文件名乱码问题
response.setHeader("Content-Disposition", "attachment filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"))response.setHeader(...)文件名中有空格的时候
String fileName = StringUtils.trim(file.getName())String formatFileName = encodingFileName(name)//在后面定义方法encodingFileName(String fileName)
response.setHeader("Content-Disposition", "attachment filename=" + formatFileName )
//处理文件名中出现的空格
//其中%20是空格在UTF-8下的编码
public static String encodingFileName(String fileName) {
String returnFileName = ""
try {
returnFileName = URLEncoder.encode(fileName, "UTF-8")
returnFileName = StringUtils.replace(returnFileName, "+", "%20")
if (returnFileName.length() > 150) {
returnFileName = new String(fileName.getBytes("GB2312"), "ISO8859-1")
returnFileName = StringUtils.replace(returnFileName, " ", "%20")
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace()
if (log.isWarnEnabled()) {
log.info("Don't support this encoding ...")
}
}
return returnFileName
}
一秒刷新页面一次
response.setHeader("refresh","1")二秒跳到其他页面
response.setHeader("refresh","2URL=otherPagename")没有缓存:
response.setHeader("Pragma", "No-cache")response.setHeader("Cache-Control", "no-cache")
设置过期的时间期限
response.setDateHeader("Expires", System.currentTimeMillis()+自己设置的时间期限)访问别的页面:
response.setStatus(302)response.setHeader("location","url")
通知浏览器数据采用的压缩格式:
response.setHeader("Content-Encoding","压缩后的数据")高速浏览器压缩数据的长度:
response.setHeader("Content-Length",压缩后的数据.length+"")高速浏览器图片或视频:
response.setHeader("Content-type","这个参数在tomcat里conf下的web.xml里面找")inputstream in= this.getServletContext.getResourceAsStream("/2.jpg")
int len=0
byte buffer[]= new byte[1024]
outputStream out = response.getOutputStream()
while(len=in.read(buffer)>0){
out.write(buffer,0,len)
}
高速浏览器已下载的形式:
response.setHeader("Content-disposition","attachmentfilename=2.jpg")inputstream in= this.getServletContext.getResourceAsStream("/2.jpg")
int len=0
byte buffer[]= new byte[1024]
outputStream out = response.getOutputStream()
while(len=in.read(buffer)>0){
out.write(buffer,0,len)
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)