/**
* Copyright (c) 2005-2012 https://github.com/zhangkaitao
*
* Licensed under the Apache License, Version 2.0 (the "License");
*/
package com.sishuok.chapter4.web.upgrade.client;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
/**
*
* 需要走socket,因为HttpURLConnection的实现是半双工的,即必须Output后才能Input。
*
* 客户端没有使用非阻塞I/O
*
* <p>User: Zhang Kaitao
* <p>Date: 13-7-21 上午8:26
* <p>Version: 1.0
*/
@WebServlet(name = "echoClientServlet", urlPatterns = "/echo", asyncSupported = true)
public class EchoClientServlet extends HttpServlet {
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Connection", "Keep-Alive");
resp.addHeader("Cache-Control", "private");
resp.addHeader("Pragma", "no-cache");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("正在交互...");
resp.getWriter().flush();
resp.flushBuffer();
Socket socket = new Socket(InetAddress.getByName(req.getLocalAddr()), req.getLocalPort());
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
os.write(("POST " + req.getContextPath() + "/upgrade HTTP/1.1\r\n").getBytes("ISO-8859-1"));
os.write(("Connection: Upgrade\r\n").getBytes("ISO-8859-1"));
os.write(("Upgrade: echo\r\n").getBytes("ISO-8859-1"));
os.write(("\r\n").getBytes("ISO-8859-1"));
os.flush();
StringBuilder buffer = new StringBuilder();
int ch = -1;
while((ch = is.read()) != -1) {
buffer.append((char) ch);
int bufferLength = buffer.length();
//如果请求到 \r\n\r\n 表示头信息读完了
if(bufferLength > 4 &&
buffer.charAt(bufferLength - 4) == '\r' && buffer.charAt(bufferLength - 3) == '\n' &&
buffer.charAt(bufferLength - 2) == '\r' && buffer.charAt(bufferLength - 1) == '\n') {
//状态行 和 响应头接收完毕
System.out.println(buffer.toString());
break;
}
}
//处理响应的状态行 和 响应头
String[] data = buffer.toString().split("\r\n");
//HTTP/1.1 101 Web Socket Protocol Handshake
String statusLine = data[0];
int statusCode = Integer.valueOf(statusLine.substring(9, 12));
String statusMessage = statusLine.substring(13);
String connectionHeader = null;
String upgradeHeader = null;
String protocolHeader = null;
for(int i = 1; i < data.length; i++) {
String[] header = data[i].split(":");
if("Connection".equalsIgnoreCase(header[0])) {
connectionHeader = header[1].trim();
}
if("Upgrade".equalsIgnoreCase(header[0])) {
upgradeHeader = header[1].trim();
}
if("protocol".equalsIgnoreCase(header[0])) {
protocolHeader = header[1].trim();
}
}
if(statusCode != 101) {
System.out.print("客户端:切换协议失败了,响应状态码:" + statusCode + " " + statusMessage);
} else {//切换协议可能成功
if(!"Upgrade".equalsIgnoreCase(connectionHeader)) {
System.out.println("客户端:服务器返回了错误的Connection头,应该为:Upgrade,实际是:" + connectionHeader);
} else if(!"echo".equalsIgnoreCase(upgradeHeader)) {
System.out.println("客户端:服务器返回了错误的Upgrade头,应该为:echo,实际是:" + connectionHeader);
} else {
System.out.println("客户端:切换协议成功了,服务器支持的协议:" + protocolHeader);
//协议切换成功
buffer = new StringBuilder();
for(int i = 1; i <= 10; i++) {
//1、写数据
os.write(("hello " + i + "\r\n").getBytes("ISO-8859-1"));
os.flush();
//2、读数据
while(true) {
ch = is.read();
buffer.append((char)ch);
int bufferLength = buffer.length();
//如果接收到的是CRLF,表示一次读取完毕
if(bufferLength >= 2 &&
buffer.charAt(bufferLength - 2) == '\r' && buffer.charAt(bufferLength - 1) == '\n') {
System.out.println("客户端接收到回显:" + buffer);
buffer = new StringBuilder();
break;
}
}
}
//客户端告诉服务器通信结束
os.write("BYE\r\n".getBytes("ISO-8859-1"));
os.flush();
System.out.println("客户端结束通信");
}
}
os.close();
is.close();
resp.getWriter().write("交互完成...");
resp.getWriter().flush();
resp.flushBuffer();
}
}