package Client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.channels.IllegalBlockingModeException;
import Config.ParameterCfg;
import ErrorExcp.ErrorExcp;
import Log.Log;
import Msg.Msg.Msg;
import MsgCfg.MsgGlobalInfo;
/**
*
* @author xiehui 通信类,数据的发送与接受
*
*/
public class Client {
/**
* 负责数据发送与接受的套接字
*/
public Socket ncpClient;
/**
* socket读取数据管道
*/
public OutputStream writeIO;
/**
* socket写数据管道
*/
public InputStream receiveIO;
/**
*日志记录
*/
public Log log;
/**
* 是否做了资源释放,避免二次释放引起bug,默认为假
*/
public boolean isCloseDeal = false;
/**
* 在套接字上获取的报文信息
*/
public MsgInfo msgInfo;
/**
* Client()函数为构造函数
*
* @param acccpetSocket
* :为服务器投递过来的套接字<br>
*
* log:为传递的日志对象,方便在通信的过程中将状态记录下来
*
* @return 没有返回值的内容
*
* @exception exceptions
* 没有异常被抛出
*/
public Client(Socket acccpetSocket, Log log) {
/**
* 套接字本地创建的,发起方是农产品前置机
*/
this.ncpClient = acccpetSocket;
this.log = log;
msgInfo = new MsgInfo();
/**
* 将发起的IP地址写入日志
*/
log.add(acccpetSocket.getRemoteSocketAddress());
}
/**
* 向服务器建立连接
*
* @param seconds
* :为向服务器发起连接的超时时间<br>
*
* errorSnd:为发送期间可能出现异常的对象,一旦发生异常,完善异常对象信息然后抛出
*
* @return 没有返回值的内容
* @throws ErrorExcp
*
* @exception ErrorSend
* 抛出一个发送数据的异常
*/
public void conncet() throws ErrorExcp
{
try {
/**
* 从程序全局配置参数取出地址(地址,连接超时时间)
*/
ncpClient.connect(ParameterCfg.bankAdreess,
ParameterCfg.conncetTime);
} catch (IllegalArgumentException e) {
ErrorExcp error = new ErrorExcp(2, 1, 3, 301, "conncet");
throw error;
} catch (UnknownHostException e) {
ErrorExcp error = new ErrorExcp(2, 1, 3, 302, "conncet");
throw error;
} catch (IllegalBlockingModeException e) {
ErrorExcp error = new ErrorExcp(2, 1, 3, 303, "conncet");
throw error;
} catch (SocketTimeoutException e) {
ErrorExcp error = new ErrorExcp(2, 1, 3, 304, "conncet");
throw error;
} catch (IOException e) {
ErrorExcp error = new ErrorExcp(2, 1, 3, 305, "conncet");
throw error;
} finally {
close();
}
}
public void checkIsClose(int result, int errorCode, String info)
throws ErrorExcp {
if (ncpClient.isClosed()) {
ErrorExcp error = new ErrorExcp(1, result, 3, errorCode, info);
throw error;
}
}
/**
* 从套接字上发送一个字节数组
*
* @param sendByte
* : 为要发送的字节数组对象<br>
*
* start : 为尝试从sendByte的第start个字节开始发送<br>
*
* end : 表示发到到sendByte的第end个字节结束
*
* @return 没有返回值的内容
*
* @exception IllegalBlockingModeException
* 抛出一个使用了非阻塞套接字的异常<br>
* IOException 发送失败抛出一个IO异常(可能是网络突然中断)
*
*/
public void send(byte[] sendByte, int start, int end)
throws IllegalBlockingModeException, IOException {
writeIO = ncpClient.getOutputStream();
writeIO.write(sendByte, start, end);
writeIO.flush();
}
/**
* 在套接字上接受数据
*
* @param receviceMsg
* :表示要接受的报文字节数组
* @param start
* :表示从报文字节数组的哪一位直接序列开始接受
* @param end
* :表示从报文字节数组的哪一位结束数据的接受
* @return :返回接受了多少个字节的数据
* @throws ErrorExcp
* :如果接受时发现连接已经中断,抛出异常
* @throws IOException
* :如果接受数据异常,抛出IO异常
*/
private int receive(byte[] receviceMsg, int start, int end)
throws ErrorExcp, IOException
{
int length = -1;
receiveIO = ncpClient.getInputStream();
length = receiveIO.read(receviceMsg, start, end);
return length;
}
/**
* 发送报文数据的适配接口
*
* @param msg
* @throws ErrorExcp
*/
public void sendMsg(Msg msg) throws ErrorExcp
{
try {
send(msg.stdByte, 0, msg.stdByte.length);
} catch (IllegalBlockingModeException e) {
close();
ErrorExcp error = new ErrorExcp(2, 1, 3, 303, "SendMsg");
throw error;
}
catch (IOException e) {
close();
ErrorExcp error = new ErrorExcp(2, 1, 3, 305, "SendMsg");
throw error;
} catch (NullPointerException e) {
close();
ErrorExcp error = new ErrorExcp(2, 1, 3, 307, "SendMsg");
throw error;
}
}
/**
* 在套接字上收取一个报文
*
* @return
* @throws ErrorExcp
*/
public MsgInfo readMsg() throws ErrorExcp {
try {
/**
* 设置接受超时时间
*/
setRecTime();
/**
* 创建四个字节的数组,接受报文的msglength字段
*/
byte[] msgLength = new byte[4];
/**
* 接受头4个字节,并获取接受的直接的长度
*/
int recHeadLength = receive(msgLength, 0, 4);
/**
* 判断头4个字节的数据包是否接受完整
*/
if (recHeadLength != 4) {
ErrorExcp error = new ErrorExcp(1, 1, 1, 402, "msgLength");
throw error;
}
/**
* 将头4个字节写入日志
*/
log.addMsgHead(msgLength);
/**
* 写入报文长头四个字节信息
*/
msgInfo.msgLength = new String(msgLength);
/**
* 检查报文全局信息配置
*/
MsgGlobalInfo.macheingMsgLength(msgInfo);
int length = Integer.valueOf(msgInfo.msgLength);
/**
* 收取除报文头四个字节字段以外的全部报文
*/
byte rec[] = new byte[length];
/**
* 准备判断接受报文的长度是否达到期望的字节长度
*/
int recBodyLength;
recBodyLength = receive(rec, 0, length);
/**
* 判断数据包除头msgLength外,其他字段的长度是否正确
*/
if (recBodyLength != length) {
ErrorExcp error = new ErrorExcp(1, 1, 1, 404, "stdByte");
throw error;
}
String msgBody = new String(rec, 0, recBodyLength);
/**
* 将报文体写入日志
*/
log.addMsgBody(msgBody);
/**
* 完整的报文
*/
msgInfo.stdStr = msgInfo.msgLength + msgBody;
/**
* 写入报文编号信息
*/
msgInfo.msgNum = msgInfo.stdStr.substring(38, 41);
}
/**
* 捕捉连接中断的信息
*/
catch (IOException e) {
ErrorExcp error = new ErrorExcp(1, 1, 3, 407, "ReadMsg");
throw error;
}
return msgInfo;
}
/**
* 设置接受数据超时时间
*
* @throws ErrorExcp
*/
public void setRecTime() throws ErrorExcp {
try {
ncpClient.setSoTimeout(ParameterCfg.recTime);
} catch (SocketException e) {
ErrorExcp error = new ErrorExcp(2, 1, 3, 408, "setRecTime");
throw error;
}
}
public void close()
{
try {
/**
* 如果以前在catch 释放了则不需要在finally再次释放,否则可能会引发错误
*/
if (!isCloseDeal && !ncpClient.isClosed()) {
ncpClient.close();
}
} catch (Exception e) {
}
finally {
ncpClient = null;
isCloseDeal = true;
}
}
}
package Client;
import ErrorExcp.ErrorExcp;
import Msg.Msg.Msg;
/**
* 数据的接受与发送的抽象类
*
*/
public abstract class RecAndSnd
{
/**
* 缓存的日志对象
*/
public StringBuffer logInfo;
/**
* 通信对象
*/
public Client client;
/**
* 在套接字上发送一个报文
*
* @param msg
* 报文对象
* @throws ErrorExcp
*/
public abstract void send(Msg msg) throws ErrorExcp;
/**
* 在套接字上接受一个报文
*
* @return 报文对象Msg
* @throws ErrorExcp
*/
public abstract Msg read() throws ErrorExcp;
}
package Client;
import java.net.Socket;
import ErrorExcp.ErrorExcp;
import Log.Log;
import Msg.Msg.Msg;
import Msg.Msg.MsgFactory;
public class RecToSnd extends RecAndSnd
{
public RecToSnd(Log log, Socket accptSocket) {
client = new Client(accptSocket, log);
}
public void send(Msg msg) throws ErrorExcp {
try {
/**
* 检查发送前的连接是否正常
*/
client.checkIsClose(1, 306, "send");
client.sendMsg(msg);
}
/**
* 无论如何,对于银行发起的交易,应该断开连接
*/
finally {
client.close();
}
}
public Msg read() throws ErrorExcp
{
Msg msg = null;
MsgInfo msgInfo = null;
/**
* 接受套接字上的报文信息
*/
msgInfo = client.readMsg();
// /**
// * 如果非debug模式下,那么关闭连接(因为不需要应答)
// */
// if (!ParameterCfg.debug)
// {
// client.close();
// }
msg = MsgFactory.CreatMsg(msgInfo);
return msg;
}
}
package Client;
import ErrorExcp.ErrorExcp;
import Log.Log;
import Msg.Msg.Msg;
import Msg.Msg.MsgFactory;
import MsgCfg.MsgCfg;
/**
* 农产品发起交易时用到的通信类
*
*/
public class SndToRec extends RecAndSnd {
public MsgCfg msgCfg;
private SocketPool pool;
/**
* 从池中取出一个socket对象
*
* @param log
*/
public SndToRec(Log log) {
pool = SocketPool.getInstance();
client = new Client(pool.get(), log);
}
public void send(Msg msg) throws ErrorExcp {
/**
* 与银行服务器进行连接
*/
client.conncet();
/**
* 在套接字上发送一个报文
*/
client.sendMsg(msg);
}
/**
* 在套接字上接受发送后,银行返回的报文的信息
*
* @return 报文相关的信息
* @throws ErrorExcp
*/
public Msg read() throws ErrorExcp
{
Msg msg = null;
MsgInfo msgInfo = null;
try {
/**
* 检查连接是否正常
*/
client.checkIsClose(2, 406, "read");
msgInfo = client.readMsg();
} catch (ErrorExcp e) {
/**
* 因为是农产品发起的交易,没有收到数据,所以状态未明确
*/
e.result = 2;
throw e;
} finally {
client.close();
}
/**
* 如果出现接受的报文,不是发送报文所对应的报文应该报错,从此位置开始报文接受工作已经全部 完成
*/
if (msgCfg.relativeList.indexOf(msgInfo.msgNum) == -1) {
ErrorExcp error = new ErrorExcp(1, 1, 3, 501, "msgNum");
throw error;
}
/**
* 通过报文工厂创建报文
*/
msg = MsgFactory.CreatMsg(msgInfo);
return msg;
}
}