使用Mina2.x編寫同時支援TCP和HTTP的服務端
阿新 • • 發佈:2019-01-28
這是一個JavaProject
首先是服務啟動類MainApp.java
package com.jadyer.core; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.ArrayList; import java.util.List; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.executor.ExecutorFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; /** * 服務啟動類 * @see 服務啟動後(即執行這裡的main方法),可用瀏覽器訪問下面兩個地址 * @see http://127.0.0.1:8000/ (瀏覽器會顯示這幾個字:歡迎訪問由Mina2.0.7編寫的Web伺服器) * @see http://127.0.0.1:8000/login(瀏覽器會顯示這幾個字:登入成功) * @see 另外這裡並未配置backlog,那麼它會採用作業系統預設的連線請求佇列長度50 * @see 詳見org.apache.mina.core.polling.AbstractPollingIoAcceptor類原始碼的96行 * @create Jul 7, 2013 1:28:04 PM * @author 玄玉<http://blog.csdn.net/jadyer> */ public class MainApp { public static void main(String[] args) throws IOException { NioSocketAcceptor acceptor = new NioSocketAcceptor(); acceptor.setBacklog(0); acceptor.setReuseAddress(true); acceptor.getSessionConfig().setWriteTimeout(10000); acceptor.getSessionConfig().setBothIdleTime(90); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ServerProtocolCodecFactory())); acceptor.getFilterChain().addLast("executor", new ExecutorFilter()); acceptor.setHandler(new ServerHandler()); //服務端繫結兩個埠,8000用於接收並處理HTTP請求,9901用於接收並處理TCP請求 List<SocketAddress> socketAddresses = new ArrayList<SocketAddress>(); socketAddresses.add(new InetSocketAddress(8000)); socketAddresses.add(new InetSocketAddress(9901)); acceptor.bind(socketAddresses); //判斷服務端啟動與否 if(acceptor.isActive()){ System.out.println("寫 超 時: 10000ms"); System.out.println("發呆配置: Both Idle 90s"); System.out.println("埠重用: true"); System.out.println("服務端初始化完成......"); System.out.println("服務已啟動....開始監聽...." + acceptor.getLocalAddresses()); }else{ System.out.println("服務端初始化失敗......"); } } }
下面是業務分發類ServerHandler.java(也是Mina中的Handler)
package com.jadyer.core; import java.net.HttpURLConnection; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import com.jadyer.util.JadyerUtil; /** * 業務分發類 * @see 本例中目前只接收兩種請求 * @see TCP請求的固定業務編碼為10005,HTTP請求的固定業務編碼為/login(http://127.0.0.1:8000/login) * @see TCP報文格式為前6個位元組表示報文整體長度(長度不足6位時左補零),第7位開始代表業務編碼(固定長度為5),第12位開始是業務資料 * @create Jul 7, 2013 2:24:45 PM * @author 玄玉<http://blog.csdn.net/jadyer> */ public class ServerHandler extends IoHandlerAdapter { @Override public void messageReceived(IoSession session, Object message) throws Exception { String respData = null; Token token = (Token)message; /* * 列印收到的原始報文 */ System.out.println("渠道:" + token.getBusiType() + " 交易碼:" + token.getBusiCode() +" 完整報文(HEX):" + JadyerUtil.buildHexStringWithASCII(JadyerUtil.getBytes(token.getFullMessage(), "UTF-8"))); StringBuilder sb = new StringBuilder(); sb.append("\r\n------------------------------------------------------------------------------------------"); sb.append("\r\n【通訊雙方】").append(session); sb.append("\r\n【收發標識】Receive"); sb.append("\r\n【報文內容】").append(token.getFullMessage()); sb.append("\r\n------------------------------------------------------------------------------------------"); System.out.println(sb.toString()); /* * 根據請求的業務編碼做不同的處理 */ if(token.getBusiCode().equals("/")){ respData = this.buildHTTPResponseMessage("<h2>歡迎訪問由Mina2.0.7編寫的Web伺服器</h2>"); }else if(token.getBusiCode().equals("/favicon.ico")){ respData = this.buildHTTPResponseMessage("<link rel=\"icon\" href=\"https://epay.10010.com/per/favicon.ico\"" + "type=\"image/x-icon\"/>\n<link rel=\"shortcut icon\" href=\"http" + "s://epay.10010.com/per/favicon.ico\" type=\"image/x-icon\"/>"); }else if(token.getBusiCode().equals("/login")){ System.out.println("收到請求引數=[" + token.getBusiMessage() + "]"); respData = this.buildHTTPResponseMessage("登入成功"); }else if(token.getBusiCode().equals("10005")){ System.out.println("收到請求引數=[" + token.getBusiMessage() + "]"); respData = "00003099999999`20130707144028`"; }else{ if(token.getBusiType().equals(Token.BUSI_TYPE_TCP)){ respData = "ILLEGAL_REQUEST"; }else if(token.getBusiType().equals(Token.BUSI_TYPE_HTTP)){ respData = this.buildHTTPResponseMessage(501, null); } } /* * 列印應答報文 */ sb.setLength(0); sb.append("\r\n------------------------------------------------------------------------------------------"); sb.append("\r\n【通訊雙方】").append(session); sb.append("\r\n【收發標識】Response"); sb.append("\r\n【報文內容】").append(respData); sb.append("\r\n------------------------------------------------------------------------------------------"); System.out.println(sb.toString()); session.write(respData); } @Override public void messageSent(IoSession session, Object message) throws Exception { System.out.println("已迴應給Client"); if(session != null){ session.close(true); } } @Override public void sessionIdle(IoSession session, IdleStatus status){ System.out.println("請求進入閒置狀態....迴路即將關閉...."); session.close(true); } @Override public void exceptionCaught(IoSession session, Throwable cause){ System.out.println("請求處理遇到異常....迴路即將關閉...."); cause.printStackTrace(); session.close(true); } /** * 構建HTTP響應報文 * @see 該方法預設構建的是HTTP響應碼為200的響應報文 * @param httpResponseMessageBody HTTP響應報文體 * @return 包含了HTTP響應報文頭和報文體的完整報文 */ private String buildHTTPResponseMessage(String httpResponseMessageBody){ return buildHTTPResponseMessage(HttpURLConnection.HTTP_OK, httpResponseMessageBody); } /** * 構建HTTP響應報文 * @see 200--請求已成功,請求所希望的響應頭或資料體將隨此響應返回..即伺服器已成功處理了請求 * @see 400--由於包含語法錯誤,當前請求無法被伺服器理解..除非進行修改,否則客戶端不應該重複提交這個請求..即錯誤請求 * @see 500--伺服器遇到了一個未曾預料的狀況,導致其無法完成對請求的處理..一般來說,該問題都會在伺服器的程式碼出錯時出現..即伺服器內部錯誤 * @see 501--伺服器不支援當前請求所需要的某個功能..當伺服器無法識別請求的方法,且無法支援其對任何資源的請求時,可能返回此程式碼..即尚未實施 * @param httpResponseCode HTTP響應碼 * @param httpResponseMessageBody HTTP響應報文體 * @return 包含了HTTP響應報文頭和報文體的完整報文 */ private String buildHTTPResponseMessage(int httpResponseCode, String httpResponseMessageBody){ if(httpResponseCode == HttpURLConnection.HTTP_OK){ StringBuilder sb = new StringBuilder(); sb.append("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: "); sb.append(JadyerUtil.getBytes(httpResponseMessageBody, "UTF-8").length); sb.append("\r\n\r\n"); sb.append(httpResponseMessageBody); return sb.toString(); } if(httpResponseCode == HttpURLConnection.HTTP_BAD_REQUEST){ return "HTTP/1.1 400 Bad Request"; } if(httpResponseCode == HttpURLConnection.HTTP_INTERNAL_ERROR){ return "HTTP/1.1 500 Internal Server Error"; } return "HTTP/1.1 501 Not Implemented"; } }
下面是用來封裝客戶端請求報文的實體類Token.java
package com.jadyer.core; /** * 封裝客戶端請求報文 * @create Jul 7, 2013 1:42:57 PM * @author 玄玉<http://blog.csdn.net/jadyer> */ public class Token { public static final String BUSI_TYPE_TCP = "TCP"; public static final String BUSI_TYPE_HTTP = "HTTP"; public String busiCode; //業務碼 public String busiType; //業務型別:TCP or HTTP public String busiMessage; //業務報文:TCP請求時為TCP完整報文,HTTP_POST請求時為報文體部分,HTTP_GET時為報文頭第一行引數部分 public String busiCharset; //報文字符集 public String fullMessage; //原始完整報文(用於在日誌中列印最初接收到的原始完整報文) /*-- 五個屬性的setter和getter略 --*/ }
下面是用於組裝服務端的編解碼器的工廠類ServerProtocolCodecFactory.java
package com.jadyer.core;
import org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory;
/**
* 組裝服務端的編解碼器的工廠
* @see 暫不提供客戶端編解碼器(其實它與服務端的編解碼器差不多差不多)
* @see ====================================================================================
* @see 其內部維護了一個MessageDecoder陣列,用於儲存新增的所有解碼器
* @see 每次decode()的時候就呼叫每個MessageDecoder的decodable()逐個檢查
* @see 只要發現一個MessageDecoder不是對應的解碼器,就從陣列中移除,知道找到合適的MessageDecoder
* @see 如果最後發現數組為空,就表示沒有找到對應的MessageDecoder,最後丟擲異常
* @see ====================================================================================
* @create Jul 7, 2013 1:41:10 PM
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
public class ServerProtocolCodecFactory extends DemuxingProtocolCodecFactory {
public ServerProtocolCodecFactory(){
super.addMessageEncoder(String.class, ServerProtocolEncoder.class);
super.addMessageDecoder(ServerProtocolTCPDecoder.class);
super.addMessageDecoder(ServerProtocolHTTPDecoder.class);
}
}
下面是服務端用到的協議編碼器ServerProtocolEncoder.java
package com.jadyer.core;
import java.nio.charset.Charset;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.filter.codec.demux.MessageEncoder;
/**
* Server端協議編碼器
* @see 用於編碼響應給Client的報文(報文編碼一律採用UTF-8)
* @create Jul 7, 2013 1:43:12 PM
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
public class ServerProtocolEncoder implements MessageEncoder<String> {
@Override
public void encode(IoSession session, String message, ProtocolEncoderOutput out) throws Exception {
IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true);
buffer.putString(message, Charset.forName("UTF-8").newEncoder());
buffer.flip();
out.write(buffer);
}
}
重點來了:下面是服務端的TCP協議解碼器ServerProtocolTCPDecoder.java
package com.jadyer.core;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import com.jadyer.util.JadyerUtil;
/**
* Server端TCP協議解碼器
* @see 用於解碼接收到的TCP請求報文(報文編碼一律採用UTF-8)
* @see 當收到資料包時,程式首先會執行decodable()方法,通過讀取資料判斷當前資料包是否可進行decode
* @see 當decodable()方法返回MessageDecoderResult.OK時,接著會呼叫decode()方法,正式decode資料包
* @see 在decode()方法進行讀取操作會影響資料包的大小,decode需要判斷協議中哪些已經decode完,哪些還沒decode
* @see decode完成後,通過ProtocolDecoderOutput.write()輸出,並返回MessageDecoderResult.OK表示decode完畢
* @create Jul 7, 2013 1:44:53 PM
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
public class ServerProtocolTCPDecoder implements MessageDecoder {
/**
* 該方法相當於預讀取,用於判斷是否是可用的解碼器,這裡對IoBuffer讀取不會影響資料包的大小
* 該方法結束後IoBuffer會復原,所以不必擔心呼叫該方法時,position已經不在緩衝區起始位置
*/
@Override
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
//TCP報文格式約定為前6個位元組表示報文整體長度,長度不足6位時左補零,第7位開始代表業務編碼,業務編碼固定長度為5,第12位開始是業務資料
if(in.remaining() < 6){
return MessageDecoderResult.NEED_DATA;
}
//服務端啟動時已繫結9901埠,專門用來處理TCP請求的
if(session.getLocalAddress().toString().contains(":9901")){
byte[] messageLength = new byte[6];
in.get(messageLength);
if(in.limit() >= Integer.parseInt(JadyerUtil.getString(messageLength, "UTF-8"))){
return MessageDecoderResult.OK;
}else{
return MessageDecoderResult.NEED_DATA;
}
}else{
return MessageDecoderResult.NOT_OK;
}
}
@Override
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
byte[] message = new byte[in.limit()];
in.get(message);
String fullMessage = JadyerUtil.getString(message, "UTF-8");
Token token = new Token();
token.setBusiCharset("UTF-8");
token.setBusiType(Token.BUSI_TYPE_TCP);
token.setBusiCode(fullMessage.substring(6, 11));
token.setBusiMessage(fullMessage);
token.setFullMessage(fullMessage);
out.write(token);
return MessageDecoderResult.OK;
}
@Override
public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
//暫時什麼都不做
}
}
下面是服務端的HTTP協議解碼器ServerProtocolHTTPDecoder.java
package com.jadyer.core;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import com.jadyer.util.JadyerUtil;
/**
* Server端HTTP協議解碼器
* @see 用於解碼接收到的HTTP請求報文(報文編碼一律採用UTF-8)
* @create Jul 7, 2013 1:44:20 PM
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
public class ServerProtocolHTTPDecoder implements MessageDecoder {
@Override
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
if(in.remaining() < 5){
return MessageDecoderResult.NEED_DATA;
}
//服務端啟動時已繫結8000埠,專門用來處理HTTP請求的
if(session.getLocalAddress().toString().contains(":8000")){
return this.isComplete(in) ? MessageDecoderResult.OK : MessageDecoderResult.NEED_DATA;
}else{
return MessageDecoderResult.NOT_OK;
}
}
@Override
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
byte[] message = new byte[in.limit()];
in.get(message);
String fullMessage = JadyerUtil.getString(message, "UTF-8");
Token token = new Token();
token.setBusiCharset("UTF-8");
token.setBusiType(Token.BUSI_TYPE_HTTP);
token.setFullMessage(fullMessage);
if(fullMessage.startsWith("GET")){
if(fullMessage.startsWith("GET / HTTP/1.1")){
token.setBusiCode("/");
}else if(fullMessage.startsWith("GET /favicon.ico HTTP/1.1")){
token.setBusiCode("/favicon.ico");
}else{
//GET /login?aa=bb&cc=dd&ee=ff HTTP/1.1
if(fullMessage.substring(4, fullMessage.indexOf("\r\n")).contains("?")){
token.setBusiCode(fullMessage.substring(4, fullMessage.indexOf("?")));
token.setBusiMessage(fullMessage.substring(fullMessage.indexOf("?")+1, fullMessage.indexOf("HTTP/1.1")-1));
//GET /login HTTP/1.1
}else{
token.setBusiCode(fullMessage.substring(4, fullMessage.indexOf("HTTP")-1));
}
}
}else if(fullMessage.startsWith("POST")){
//先獲取到請求報文頭中的Content-Length
int contentLength = 0;
if(fullMessage.contains("Content-Length:")){
String msgLenFlag = fullMessage.substring(fullMessage.indexOf("Content-Length:") + 15);
if(msgLenFlag.contains("\r\n")){
contentLength = Integer.parseInt(msgLenFlag.substring(0, msgLenFlag.indexOf("\r\n")).trim());
if(contentLength > 0){
token.setBusiMessage(fullMessage.split("\r\n\r\n")[1]);
}
}
}
//POST /login?aa=bb&cc=dd&ee=ff HTTP/1.1
//特別說明一下:此時報文體本應該是空的,即Content-Length=0,但不能排除對方偏偏在報文體中也傳了引數
//特別說明一下:所以這裡的處理手段是busiMessage=請求URL中的引數串 + "`" + 報文體中的引數串(如果存在報文體的話)
if(fullMessage.substring(5, fullMessage.indexOf("\r\n")).contains("?")){
token.setBusiCode(fullMessage.substring(5, fullMessage.indexOf("?")));
String urlParam = fullMessage.substring(fullMessage.indexOf("?")+1, fullMessage.indexOf("HTTP/1.1")-1);
if(contentLength > 0){
token.setBusiMessage(urlParam + "`" + fullMessage.split("\r\n\r\n")[1]);
}else{
token.setBusiMessage(urlParam);
}
//POST /login HTTP/1.1
}else{
token.setBusiCode(fullMessage.substring(5, fullMessage.indexOf("HTTP/1.1")-1));
}
}
out.write(token);
return MessageDecoderResult.OK;
}
@Override
public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
//暫時什麼都不做
}
/**
* 校驗HTTP請求報文是否已完整接收
* @see 目前僅授理GET和POST請求
* @see ====================================================================================
* @see GET /notify_yeepay?p1_MerId=11&r0_Cmd=Buy&r1_Code=1&r2_TrxId=22 HTTP/1.1^M
* @see Content-Type: application/x-www-form-urlencoded; charset=GBK^M
* @see Cache-Control: no-cache^M
* @see Pragma: no-cache^M
* @see User-Agent: Java/1.5.0_14^M
* @see Host: 123.125.97.248^M
* @see Accept: text/html, image/gif, image/jpeg, *; q=.2, 星號/*; q=.2^M
* @see Connection: keep-alive^M
* @see ^M
* @see ====================================================================================
* @see POST /tra/trade/noCardNoPassword.htm HTTP/1.1^M
* @see Content-Type: application/x-www-form-urlencoded;charset=GB18030^M
* @see Cache-Control: no-cache^M
* @see Pragma: no-cache^M
* @see User-Agent: Java/1.6.0_24^M
* @see Host: 192.168.20.1^M
* @see Accept: text/html, image/gif, image/jpeg, *; q=.2, 星號/*; q=.2^M
* @see Connection: keep-alive^M
* @see Content-Length: 541^M
* @see ^M
* @see cooBankNo=CMBC_CREDIT&signType=MD5&amount=499900&orderValidityNum=15&CVVNo=255
* @see ====================================================================================
* @see 至於上面所列的GET和POST請求原始報文中為何會出現^M
* @see 我的部落格上有詳細說明http://blog.csdn.net/jadyer/article/details/8212067
* @see ====================================================================================
* @param in 裝載HTTP請求報文的IoBuffer
*/
private boolean isComplete(IoBuffer in){
/*
* 先獲取HTTP請求的原始報文
*/
byte[] messages = new byte[in.limit()];
in.get(messages);
String message = JadyerUtil.getString(messages, "UTF-8");
/*
* 授理GET請求
*/
if(message.startsWith("GET")){
return message.endsWith("\r\n\r\n");
}
/*
* 授理POST請求
*/
if(message.startsWith("POST")){
if(message.contains("Content-Length:")){
//取Content-Length後的字串
String msgLenFlag = message.substring(message.indexOf("Content-Length:") + 15);
if(msgLenFlag.contains("\r\n")){
//取Content-Length值
int contentLength = Integer.parseInt(msgLenFlag.substring(0, msgLenFlag.indexOf("\r\n")).trim());
if(contentLength == 0){
return true;
}else if(contentLength > 0){
//取HTTP_POST請求報文體
String messageBody = message.split("\r\n\r\n")[1];
if(contentLength == JadyerUtil.getBytes(messageBody, "UTF-8").length){
return true;
}
}
}
}
}
/*
* 僅授理GET和POST請求
*/
return false;
}
}
最後再貼一下這裡用到的工具類JadyerUtil.java
package com.jadyer.util;
import java.io.UnsupportedEncodingException;
public class JadyerUtil {
private JadyerUtil(){}
/**
* 判斷輸入的字串引數是否為空
* @return boolean 空則返回true,非空則flase
*/
public static boolean isEmpty(String input) {
return null==input || 0==input.length() || 0==input.replaceAll("\\s", "").length();
}
/**
* 判斷輸入的位元組陣列是否為空
* @return boolean 空則返回true,非空則flase
*/
public static boolean isEmpty(byte[] bytes){
return null==bytes || 0==bytes.length;
}
/**
* 位元組陣列轉為字串
* @see 如果系統不支援所傳入的<code>charset</code>字符集,則按照系統預設字符集進行轉換
*/
public static String getString(byte[] data, String charset){
if(isEmpty(data)){
return "";
}
if(isEmpty(charset)){
return new String(data);
}
try {
return new String(data, charset);
} catch (UnsupportedEncodingException e) {
System.out.println("將byte陣列[" + data + "]轉為String時發生異常:系統不支援該字符集[" + charset + "]");
return new String(data);
}
}
/**
* 字串轉為位元組陣列
* @see 如果系統不支援所傳入的<code>charset</code>字符集,則按照系統預設字符集進行轉換
*/
public static byte[] getBytes(String data, String charset){
data = (data==null ? "" : data);
if(isEmpty(charset)){
return data.getBytes();
}
try {
return data.getBytes(charset);
} catch (UnsupportedEncodingException e) {
System.out.println("將字串[" + data + "]轉為byte[]時發生異常:系統不支援該字符集[" + charset + "]");
return data.getBytes();
}
}
/**
* 通過ASCII碼將十進位制的位元組陣列格式化為十六進位制字串
* @see 該方法會將位元組陣列中的所有位元組均格式化為字串
* @see 使用說明詳見<code>formatToHexStringWithASCII(byte[], int, int)</code>方法
*/
public static String buildHexStringWithASCII(byte[] data){
return buildHexStringWithASCII(data, 0, data.length);
}
/**
* 通過ASCII碼將十進位制的位元組陣列格式化為十六進位制字串
* @see 該方法常用於字串的十六進位制列印,列印時左側為十六進位制數值,右側為對應的字串原文
* @see 在構造右側的字串原文時,該方法內部使用的是平臺的預設字符集,來解碼byte[]陣列
* @see 該方法在將位元組轉為十六進位制時,預設使用的是<code>java.util.Locale.getDefault()</code>
* @see 詳見String.format(String, Object...)方法和new String(byte[], int, int)構造方法
* @param data 十進位制的位元組陣列
* @param offset 陣列下標,標記從陣列的第幾個位元組開始格式化輸出
* @param length 格式長度,其不得大於陣列長度,否則丟擲java.lang.ArrayIndexOutOfBoundsException
* @return 格式化後的十六進位制字串
*/
public static String buildHexStringWithASCII(byte[] data, int offset, int length){
int end = offset + length;
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
sb.append("\r\n------------------------------------------------------------------------");
boolean chineseCutFlag = false;
for(int i=offset; i<end; i+=16){
sb.append(String.format("\r\n%04X: ", i-offset)); //X或x表示將結果格式化為十六進位制整數
sb2.setLength(0);
for(int j=i; j<i+16; j++){
if(j < end){
byte b = data[j];
if(b >= 0){ //ENG ASCII
sb.append(String.format("%02X ", b));
if(b<32 || b>126){ //不可見字元
sb2.append(" ");
}else{
sb2.append((char)b);
}
}else{ //CHA ASCII
if(j == i+15){ //漢字前半個位元組
sb.append(String.format("%02X ", data[j]));
chineseCutFlag = true;
String s = new String(data, j, 2);
sb2.append(s);
}else if(j == i&&chineseCutFlag){ //後半個位元組
sb.append(String.format("%02X ", data[j]));
chineseCutFlag = false;
String s = new String(data, j, 1);
sb2.append(s);
}else{
sb.append(String.format("%02X %02X ", data[j], data[j + 1]));
String s = new String(data, j, 2);
sb2.append(s);
j++;
}
}
}else{
sb.append(" ");
}
}
sb.append("| ");
sb.append(sb2.toString());
}
sb.append("\r\n------------------------------------------------------------------------");
return sb.toString();
}
}
最後的最後貼一下使用JUnit4.x編寫的測試用例TestServer.java
package com.jadyer.test;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
/**
* 這裡用到的MinaUtil和HttpClientUtil均取自我的博文,地址如下
* http://blog.csdn.net/jadyer/article/details/8088068
* http://blog.csdn.net/jadyer/article/details/8087960
* @create Jul 9, 2013 7:59:11 PM
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
public class TestServer {
@Test
public void testTcp(){
String message = "00004710005101101992012092222400000201307071605";
String respData = MinaUtil.sendTCPMessage(message, "127.0.0.1", 9901, "UTF-8");
Assert.assertEquals("00003099999999`20130707144028`", respData);
}
/**
* 也可直接瀏覽器訪問http://127.0.0.1:8000/login以及http://127.0.0.1:8000/login?a=b&c=d&e=f
* 只要瀏覽器頁面顯示"登入成功",即表示HTTP_GET測試通過
*/
@Test
public void testHttpGet(){
//先測試帶引數的GET請求
String respData11 = HttpClientUtil.sendGetRequest("http://127.0.0.1:8000/login?a=b&c=d&e=f");
Assert.assertEquals("登入成功", respData11);
//再測試不帶引數的GET請求
String respData22 = HttpClientUtil.sendGetRequest("http://127.0.0.1:8000/login");
Assert.assertEquals("登入成功", respData22);
}
@Test
public void testHttpPost(){
//先測試帶報文體的POST請求(即帶引數,模擬表單提交)
String reqURL = "http://127.0.0.1:8000/login";
Map<String, String> params = new HashMap<String, String>();
params.put("username", "Jadyer");
params.put("password", "hongyu");
String respData11 = HttpClientUtil.sendPostSSLRequest(reqURL, params, "UTF-8");
Assert.assertEquals("登入成功", respData11);
//再測試不帶報文體的POST請求(不帶引數)
String respData22 = HttpClientUtil.sendPostSSLRequest(reqURL, new HashMap<String, String>(), "UTF-8");
Assert.assertEquals("登入成功", respData22);
//最後測試一下特殊情況,即不帶報文體,但在請求地址上帶有引數的POST請求(建行外聯平臺就這麼幹的)
reqURL = "http://127.0.0.1:8000/login?username=Jadyer&password=hongyu&aa=bb&cc=dd";
String respData33 = HttpClientUtil.sendPostSSLRequest(reqURL, new HashMap<String, String>(), "UTF-8");
Assert.assertEquals("登入成功", respData33);
}
}