在最近的工作中,开始接收GPRS服务的前置服务业务,其中我做的最多的就是和第三方平台对接。在这里记录下目前遇到的几种第三方平台对接的形式。
(一)将报文数据整理为JSON字符串后以POST请求上传
这里主要根据第三方协议来进行业务代码的编写,传入诶的data为json字符串,url为了方便做成了可配置的方式。这里用到的HttpUtil会在下方贴出
public static String doTransf(String data, String url) {
try {
url = url + "?" + data;
logger.info("向后台服务器url:" + url);
String result = HttpUtil.requestPost(url, "");
logger.info("后台服务器回复json:" + result);
// 接收到的json 需要转成tcp数据
if (StringUtil.isNotEmpty(result)) {
// 成功
HashMap<String, Object> map = JksonUtil.toBean(result, HashMap.class);
if (map != null && !map.isEmpty()) {
if (map.get("status").toString().equals("200")) {
return result;
} else {
// 失败
logger.error("后台服务器返回,错误码:" + map.get("status") + ",错误信息:" + map.get("data"));
return null;
}
} else {
logger.error("解析json失败,json字符串为:" + result);
}
} else {
logger.error("请求失败");
}
} catch (Exception e) {
logger.error("请求" + url + "异常:" + e.getMessage());
e.printStackTrace();
return "";
}
return "";
}
HttpUtilpublic class HttpUtil {
public static final String GET_TYPE = "GET";
public static final String POST_TYPE = "POST";
private static MyLogger logger = LogManager.getLogger(HttpUtil.class);
public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json;charset=utf-8");
public static String requestGet(String url) throws IOException {
// 创建一个OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
try {
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()){
return response.body().string();
}else
throw new IOException("Unexpected code " + response);
} catch (IOException e) {
// TODO Auto-generated catch block
logger.error("GET请求"+ e.getClass() + "异常:" + e.getMessage());
throw e;
}
}
public static String requestPost(String url, String json)
throws Exception {
try {
// 创建一个OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
// 创建一个RequestBody(参数1:数据类型 参数2传递的json串)
RequestBody requestBody = RequestBody.create(MEDIA_TYPE_JSON, json);
// 创建一个请求对象
Request request = new Request.Builder().url(url).post(requestBody)
.build();
// 发送请求获取响应
Response response = okHttpClient.newCall(request).execute();
// 判断请求是否成功
if (response.isSuccessful()) {
// 打印服务端返回结果
String result = response.body().string();
return result;
}else{
logger.error("返回错误码:" + response.code());
}
} catch (IOException e) {
logger.error("POST请求"+ e.getClass() + "异常:" + e.getMessage());
throw e;
} catch (Exception e){
logger.error("POST请求"+ e.getClass() + "异常:" + e.getMessage());
throw e;
}
return "";
}
public static Response post(String url, String json)
throws Exception {
try {
// 创建一个OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
// 创建一个RequestBody(参数1:数据类型 参数2传递的json串)
RequestBody requestBody = RequestBody.create(MEDIA_TYPE_JSON, json);
// 创建一个请求对象
Request request = new Request.Builder().url(url).post(requestBody)
.build();
// 发送请求获取响应
Response response = okHttpClient.newCall(request).execute();
// 判断请求是否成功
// 打印服务端返回结果
return response;
} catch (IOException e) {
logger.error("POST请求"+ e.getClass() + "异常:" + e.getMessage());
throw e;
} catch (Exception e){
logger.error("POST请求"+ e.getClass() + "异常:" + e.getMessage());
throw e;
}
}
}
(二)将报文数据整理为JSON字符串后以流的形式上传
这里的RequestEntity和ResponseMsg都需要根据协议去定义就不贴出来了。唯一需要注意的是,在回复的json中,可能存在双层嵌套例如:{"data":{"sign":"xxx"},"message":"成功","status":200},则需要在ResponseMsg另外定义一个data实体,然后sign作为其属性即可。此外,一定要记得写get/set方法。
public boolean doUpData(NewWanDaTrade entity){
boolean flag = false;
String url =PropertiesUtil.getValue("Three_Interface");
logger.info("请求地址====>URL:"+url);
try {
System.out.println("发送的json:"+JksonUtil.toJson(entity));
RequestEntity req = new StringRequestEntity(JksonUtil.toJson(entity),"application/json","utf-8");
//RequestEntity req = new StringRequestEntity("trade_list:"+JksonUtil.toJson(entity),"application/json","utf-8");
HttpClient client = new HttpClient();
// client.getHttpConnectionManager().getParams().setConnectionTimeout(3000);
// client.getParams().setSoTimeout(3000);
PostMethod post = new PostMethod(url);
post.setRequestEntity(req);
client.executeMethod(post);
String recJson = post.getResponseBodyAsString();
logger.info("收到回复====>msg:"+recJson);
ResponseMsg res = JksonUtil.toBean(recJson, ResponseMsg.class);
//这里的判定条件需要更改
if("200".equals(res.getStatus())){
flag = true;
logger.warn("<======上传成功!======>");
}
else{
logger.warn("上传失败!收到消息====>:"+res.getMessage());
}
} catch (IOException e) {
logger.error("<======请求失败======>");
return flag;
}
return flag;
}
透传的意思就是我们解析出data域中的数据后不做处理,直接通过socket发送到第三方,只需要知道对方的ip和端口就可以了。直接调用下面这个工具就可以,不过需要注意的是,这个工具里面分为发送字符串和byte数组两总形式。在一次项目中,第三方平台只需要报文,而我错传了一次字符(调用的第一个方法),就导致java.net.SocketTimeoutException: Read timed out 这个异常,目前也一直没有时间细想其中原因,当时的想法有如下几种(希望大神看到顺便提点一下!)
1、是第三方不接收字符串从而不回复导致我这边接收不到消息,导致我str = reader.readLine();这句话抛出该异常。
2、readLine()该方法是根据“\r”,“\n”进行一行读数据的读取,而第三方发送过来的是一个byte数组,导致该方法出现异常。
后来我就自己重载了一个可以传byte数组的方法,利用TCP协议的会累计到一定量的数据才会发送的特性,用循环一个个写进去。最后返回的报文需要自己根据协议定义长度,有点不方便,以后有机会优化一下。
public class SocketUtil {
private static final MyLogger logger = LogManager.getLogger(SocketUtil.class);
public static String SocketSendAndReseive(long sessionId, String ip, int port, String sendbuff) {
String str = "";
Socket socket = null;
PrintWriter writer = null;
BufferedReader reader = null;
int timeout = 20;
try {
socket = new Socket(ip, port);
socket.setSoTimeout(timeout * 1000);
socket.setReuseAddress(true);
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer.write(sendbuff);
writer.flush();
str = reader.readLine();
} catch (BindException e) {
logger.error("session id:" + sessionId + "," + ip + ":" + port + "连接异常, 异常原因:" + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
logger.error("session id:" + sessionId + "," + ip + ":" + port + "连接失败, 异常原因:" + e.getMessage());
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
if (reader != null) {
reader.close();
}
if (socket != null && socket.isConnected()) {
socket.close();
}
} catch (IOException e) {
logger.error("session id:" + sessionId + "," + ip + ":" + port + "关闭连接异常,异常原因" + e.getMessage());
}
}
return str;
}
public static String SocketSendAndReseive(long sessionId, String ip, int port, byte[] sendbuff) {
Socket socket = null;
DataOutputStream out = null;
BufferedInputStream in = null;
int timeout = 10;
byte us[] = new byte[31];
try {
socket = new Socket(ip, port);
socket.setSendBufferSize(1024);
socket.setReceiveBufferSize(1024);
socket.setSoTimeout(timeout * 1000);
socket.setReuseAddress(true);
out = new DataOutputStream(socket.getOutputStream());
in = new BufferedInputStream(socket.getInputStream());
for(int i = 0;i < sendbuff.length;i++){
out.write(sendbuff[i]);
}
out.flush();
//得到的us是十进制的字节数组
in.read(us, 0, 31);
//System.out.println(StringUtils.bytesToHexStr(us));
} catch (BindException e) {
logger.error("session id:" + sessionId + "," + ip + ":" + port + "连接异常, 异常原因:" + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
logger.error("session id:" + sessionId + "," + ip + ":" + port + "连接失败, 异常原因:" + e.getMessage());
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
if (socket != null && socket.isConnected()) {
socket.close();
}
} catch (IOException e) {
logger.error("session id:" + sessionId + "," + ip + ":" + port + "关闭连接异常,异常原因" + e.getMessage());
}
}
//需要处理成原16进制字符串
return StringUtils.bytesToHexStr(us);
}
}
以上是我目前所遇到的几种形式,我在后续的工作中遇到新的情形会持续更新。我想我之后的文章会记录一下Mina的使用和通讯报文的转换。