使用JavaSocket编写发送TCP请求的工具类

来源:互联网 发布:全能星战 知乎 编辑:程序博客网 时间:2024/05/21 14:03

原创:http://blog.csdn.net/jadyer/article/details/8788303  

      使用JavaSocket编写发送TCP请求的工具类       

        分类:            JavaSE190人阅读评论(0)收藏举报
javasockettcphttpmina
[java] view plaincopyprint?
  1. package com.jadyer.util; 
  2.  
  3. import java.io.ByteArrayOutputStream; 
  4. import java.io.IOException; 
  5. import java.io.InputStream; 
  6. import java.io.OutputStream; 
  7. import java.net.InetSocketAddress; 
  8. import java.net.Socket; 
  9. import java.util.HashMap; 
  10. import java.util.Map; 
  11.  
  12. /**
  13. * 使用JavaSocket编写发送TCP请求的工具类
  14. * @see 类似的有一个Mina工具类:http://blog.csdn.net/jadyer/article/details/8088068
  15. * @see 欲查询Scoket的有关属性:http://blog.csdn.net/jadyer/article/details/8788272
  16. * @create Apr 5, 2013 9:25:34 PM
  17. * @author 玄玉<http://blog.csdn.net/jadyer>
  18. */ 
  19. public class TCPUtil { 
  20.     private TCPUtil(){} 
  21.      
  22.     /**
  23.      * 发送TCP请求
  24.      * @see 本方法默认的连接超时和读取超时均为30秒
  25.      * @see 编码与解码请求响应字节时,均采用双方约定的字符集,即本方法的第四个参数reqCharset
  26.      * @param IP         远程主机地址
  27.      * @param port       远程主机端口
  28.      * @param reqData    待发送报文的中文字符串形式
  29.      * @param reqCharset 该方法与远程主机间通信报文的编码字符集(编码为byte[]发送到Server)
  30.      * @return localPort--本地绑定的端口,reqData--请求报文,respData--响应报文,respDataHex--远程主机响应的原始字节的十六进制表示
  31.      */ 
  32.     public static Map<String, String> sendTCPRequest(String IP, String port, String reqData, String reqCharset){ 
  33.         Map<String, String> respMap = new HashMap<String, String>(); 
  34.         OutputStream out = null;     //写 
  35.         InputStream in = null;       //读 
  36.         String localPort = null;     //本地绑定的端口(java socket, client, /127.0.0.1:50804 => /127.0.0.1:9901) 
  37.         String respData = null;      //响应报文 
  38.         String respDataHex = null;   //远程主机响应的原始字节的十六进制表示 
  39.         Socket socket = new Socket();//客户机 
  40.         try
  41.             socket.setTcpNoDelay(true); 
  42.             socket.setReuseAddress(true); 
  43.             socket.setSoTimeout(30000); 
  44.             socket.setSoLinger(true,5); 
  45.             socket.setSendBufferSize(1024); 
  46.             socket.setReceiveBufferSize(1024); 
  47.             socket.setKeepAlive(true); 
  48.             socket.connect(new InetSocketAddress(IP, Integer.parseInt(port)),30000); 
  49.             localPort = String.valueOf(socket.getLocalPort()); 
  50.             /**
  51.              * 发送TCP请求
  52.              */ 
  53.             out = socket.getOutputStream(); 
  54.             out.write(reqData.getBytes(reqCharset)); 
  55.             /**
  56.              * 接收TCP响应
  57.              */ 
  58.             in = socket.getInputStream(); 
  59.             ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 
  60.             byte[] buffer = newbyte[512]; 
  61.             int len = -1
  62.             while((len=in.read(buffer)) != -1){ 
  63.                 bytesOut.write(buffer, 0, len); 
  64.             } 
  65.             /**
  66.              * 解码TCP响应的完整报文
  67.              */ 
  68.             respData = bytesOut.toString(reqCharset); 
  69.             respDataHex = formatToHexStringWithASCII(bytesOut.toByteArray()); 
  70.         } catch (Exception e) { 
  71.             System.out.println("与[" + IP +":" + port + "]通信遇到异常,堆栈信息如下"); 
  72.             e.printStackTrace(); 
  73.         } finally
  74.             if (null!=socket && socket.isConnected() && !socket.isClosed()) { 
  75.                 try
  76.                     socket.close(); 
  77.                 } catch (IOException e) { 
  78.                     System.out.println("关闭客户机Socket时发生异常,堆栈信息如下"); 
  79.                     e.printStackTrace(); 
  80.                 } 
  81.             } 
  82.         } 
  83.         respMap.put("localPort", localPort); 
  84.         respMap.put("reqData", reqData); 
  85.         respMap.put("respData", respData); 
  86.         respMap.put("respDataHex", respDataHex); 
  87.         return respMap; 
  88.     } 
  89.      
  90.     /**
  91.      * 通过ASCII码将十进制的字节数组格式化为十六进制字符串
  92.      * @see 该方法会将字节数组中的所有字节均格式化为字符串
  93.      * @see 使用说明详见<code>formatToHexStringWithASCII(byte[], int, int)</code>方法
  94.      */ 
  95.     private static String formatToHexStringWithASCII(byte[] data){ 
  96.         return formatToHexStringWithASCII(data,0, data.length); 
  97.     } 
  98.      
  99.      
  100.     /**
  101.      * 通过ASCII码将十进制的字节数组格式化为十六进制字符串
  102.      * @see 该方法常用于字符串的十六进制打印,打印时左侧为十六进制数值,右侧为对应的字符串原文
  103.      * @see 在构造右侧的字符串原文时,该方法内部使用的是平台的默认字符集,来解码byte[]数组
  104.      * @see 该方法在将字节转为十六进制时,默认使用的是<code>java.util.Locale.getDefault()</code>
  105.      * @see 详见String.format(String, Object...)方法和new String(byte[], int, int)构造方法
  106.      * @param data   十进制的字节数组
  107.      * @param offset 数组下标,标记从数组的第几个字节开始格式化输出
  108.      * @param length 格式长度,其不得大于数组长度,否则抛出java.lang.ArrayIndexOutOfBoundsException
  109.      * @return 格式化后的十六进制字符串
  110.      */ 
  111.     private static String formatToHexStringWithASCII(byte[] data,int offset, int length){ 
  112.         int end = offset + length; 
  113.         StringBuilder sb = new StringBuilder(); 
  114.         StringBuilder sb2 = new StringBuilder(); 
  115.         sb.append("\r\n------------------------------------------------------------------------"); 
  116.         boolean chineseCutFlag = false
  117.         for(int i=offset; i<end; i+=16){ 
  118.             sb.append(String.format("\r\n%04X: ", i-offset));//X或x表示将结果格式化为十六进制整数 
  119.             sb2.setLength(0); 
  120.             for(int j=i; j<i+16; j++){ 
  121.                 if(j < end){ 
  122.                     byte b = data[j]; 
  123.                     if(b >=0){ //ENG ASCII 
  124.                         sb.append(String.format("%02X ", b)); 
  125.                         if(b<32 || b>126){//不可见字符 
  126.                             sb2.append(" "); 
  127.                         }else
  128.                             sb2.append((char)b); 
  129.                         } 
  130.                     }else{ //CHA ASCII 
  131.                         if(j == i+15){//汉字前半个字节 
  132.                             sb.append(String.format("%02X ", data[j])); 
  133.                             chineseCutFlag = true
  134.                             String s = new String(data, j,2); 
  135.                             sb2.append(s); 
  136.                         }else if(j == i&&chineseCutFlag){ //后半个字节 
  137.                             sb.append(String.format("%02X ", data[j])); 
  138.                             chineseCutFlag = false
  139.                             String s = new String(data, j, 1); 
  140.                             sb2.append(s); 
  141.                         }else
  142.                             sb.append(String.format("%02X %02X ", data[j], data[j +1])); 
  143.                             String s = new String(data, j, 2); 
  144.                             sb2.append(s); 
  145.                             j++; 
  146.                         } 
  147.                     } 
  148.                 }else
  149.                     sb.append("   "); 
  150.                 } 
  151.             } 
  152.             sb.append("| "); 
  153.             sb.append(sb2.toString()); 
  154.         } 
  155.         sb.append("\r\n------------------------------------------------------------------------"); 
  156.         return sb.toString(); 
  157.     } 


下面是测试代码

[java] view plaincopyprint?
  1. public staticvoid main(String[] args) { 
  2.     String reqData = "0003721000510110199201209222240000020120922000069347814303000700000813``中国联通交费充值`为号码18655228826交费充值100.00元`UDP1209222238312219411`10000```20120922`chinaunicom-payFeeOnline`UTF-8`20120922223831`MD5`20120922020103806276`1`02`10000`20120922223954`20120922`BOCO_B2C```http://192.168.20.2:5545/ecpay/pay/elecChnlFrontPayRspBackAction.action`1`立即支付,交易成功`"
  3.     String IP = "127.0.0.1"
  4.     String port = "9901"
  5.     String reqCharset = "GB18030"
  6.     Map<String, String> respMap = sendTCPRequest(IP, port, reqData, reqCharset); 
  7.     System.out.println("============================================================================="); 
  8.     System.out.println("请求报文如下"); 
  9.     System.out.println(respMap.get("reqData")); 
  10.     System.out.println("============================================================================="); 
  11.     System.out.println("响应报文如下"); 
  12.     System.out.println(respMap.get("respData")); 
  13.     System.out.println("============================================================================="); 
  14.     System.out.println("响应十六进制如下"); 
  15.     System.out.println(respMap.get("respDataHex")); 
  16.     System.out.println("============================================================================="); 

下面是控制台输出

[java] view plaincopyprint?
  1. //控制台输出如下 
  2. //============================================================================= 
  3. //  请求报文如下 
  4. //  0003721000510110199201209222240000020120922000069347814303000700000813``中国联通交费充值`为号码18655228826交费充值100.00元`UDP1209222238312219411`10000```20120922`chinaunicom-payFeeOnline`UTF-8`20120922223831`MD5`20120922020103806276`1`02`10000`20120922223954`20120922`BOCO_B2C```http://192.168.20.2:5545/ecpay/pay/elecChnlFrontPayRspBackAction.action`1`立即支付,交易成功` 
  5. //  ============================================================================= 
  6. //  响应报文如下 
  7. //  00015800000000订单结果通知:商户系统未成功接收到通知,需要继续通知                                               6717356649614827173120130405201628201304051` 
  8. //  ============================================================================= 
  9. //  响应十六进制如下 
  10. // 
  11. //  ------------------------------------------------------------------------ 
  12. //  0000: 30 30 30 31 35 38 30 30 30 30 30 30 30 30 B6 A9 | 00015800000000订 
  13. //  0010: B5 A5 BD E1 B9 FB CD A8 D6 AA 3A C9 CC BB A7 CF | 单结果通知:商户系 
  14. //  0020: B5 CD B3 CE B4 B3 C9 B9 A6 BD D3 CA D5 B5 BD CD | ?统未成功接收到通 
  15. //  0030: A8 D6 AA 2C D0 E8 D2 AA BC CC D0 F8 CD A8 D6 AA | ?知,需要继续通知 
  16. //  0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |                 
  17. //  0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |                 
  18. //  0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |                 
  19. //  0070: 00 00 36 37 31 37 33 35 36 36 34 39 36 31 34 38 |   67173566496148 
  20. //  0080: 32 37 31 37 33 31 32 30 31 33 30 34 30 35 32 30 | 2717312013040520 
  21. //  0090: 31 36 32 38 32 30 31 33 30 34 30 35 31 60       | 1628201304051` 
  22. //  ------------------------------------------------------------------------ 
  23. //  ============================================================================= 
  24. //