package ALBasicClient; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.LinkedList; import java.util.concurrent.locks.ReentrantLock; import ALServerLog.ALServerLog; import BasicServer.S2C_BasicClientVerifyResult; public class ALBasicClientSocket { /** 对应于服务端的ID */ private long _m_iClientID; /** 对应的处理对象 */ private _AALBasicClientListener _m_clClient; /** 是否正在登录 */ private boolean _m_bLoginIng; /** 是否登录成功 */ private boolean _m_bLoged; /** 连接服务器的IP,端口 */ private String _m_sServerIP; private int _m_iServerPort; /** 连接的端口对象 */ private SocketChannel _m_scSocket; /** 发送队列锁 */ private ReentrantLock _m_rSendListMutex; /** 需要发送的消息队列 */ private LinkedList<ByteBuffer> _m_lSendBufferList; /** 缓存读取字节的位置,长度根据配置设置 */ private int _m_sBufferLen; private ByteBuffer _m_bByteBuffer; public ALBasicClientSocket(_AALBasicClientListener _client, String _serverIP, int _serverPort) { _m_iClientID = 0; _m_sServerIP = _serverIP; _m_iServerPort = _serverPort; _m_scSocket = null; _m_clClient = _client; _m_rSendListMutex = new ReentrantLock(); _m_lSendBufferList = new LinkedList<ByteBuffer>(); _m_bLoginIng = false; _m_bLoged = false; _m_sBufferLen = 0; _m_bByteBuffer = ByteBuffer.allocate(ALBasicClientConf.getInstance().getRecBufferLen()); _m_bByteBuffer.clear(); } public long getID() {return _m_iClientID;} public boolean getIsLoginIng() {return _m_bLoginIng;} public _AALBasicClientListener getClient() {return _m_clClient;} /************** * 判断是否正在连接状态 * * @author alzq.z * @time Mar 17, 2013 10:52:53 PM */ public boolean getIsConnecting() { if(_m_bLoginIng || _m_bLoged) return true; return false; } /****************** * 登录操作 * * @author alzq.z * @time Feb 19, 2013 9:57:25 PM */ public void login(int _clientType, String _userName, String _userPassword, String _customMsg) { if(_m_bLoginIng || _m_bLoged) return ; _m_bLoginIng = true; ALBasicClientRecThread recThread = new ALBasicClientRecThread(_clientType, _userName, _userPassword, _customMsg, this, _m_sServerIP, _m_iServerPort); recThread.start(); } /******************** * 将消息添加到发送队列,等待发送 * * @author alzq.z * @time Feb 19, 2013 9:57:33 PM */ public void send(ByteBuffer _buf) { if(null == _m_scSocket || null == _buf || _buf.remaining() == 0) return ; boolean needAddToSendList = false; _lockBuf(); //判断当前队列是否有剩余协议,表明是否需要将socket添加到对应发送队列中 if(_m_lSendBufferList.isEmpty()) needAddToSendList = true; //先插入长度数据,后插入实际数据 ByteBuffer lenthBuffer = ByteBuffer.allocate(4); lenthBuffer.putInt(_buf.remaining()); lenthBuffer.flip(); _m_lSendBufferList.add(lenthBuffer); _m_lSendBufferList.add(_buf); _unlockBuf(); if(needAddToSendList) ALBasicSendingClientManager.getInstance().addSendSocket(this); } /**************** * 对数据添加临时头的发送方式 * * @author alzq.z * @time Feb 19, 2013 9:59:13 PM */ public void send(ByteBuffer _tmpHeader, ByteBuffer _buf) { if(null == _m_scSocket || null == _buf || _buf.remaining() == 0) return ; boolean needAddToSendList = false; _lockBuf(); //判断当前队列是否有剩余协议,表明是否需要将socket添加到对应发送队列中 if(_m_lSendBufferList.isEmpty()) needAddToSendList = true; //先插入长度数据,后插入实际数据 ByteBuffer lenthBuffer = ByteBuffer.allocate(4); lenthBuffer.putInt(_buf.remaining() + _tmpHeader.remaining()); lenthBuffer.flip(); _m_lSendBufferList.add(lenthBuffer); _m_lSendBufferList.add(_tmpHeader); _m_lSendBufferList.add(_buf); _unlockBuf(); if(needAddToSendList) ALBasicSendingClientManager.getInstance().addSendSocket(this); } /********************** * 实际的发送函数,尝试发送尽量多的消息,并判断是否有剩余消息需要发送<br> * 发送完成后判断是否有剩余消息,并在计划队列中添加节点<br> * * @author alzq.z * @time Feb 19, 2013 9:59:24 PM */ protected void _realSendMessage() { if(null == _m_scSocket) return ; if(!_m_scSocket.isConnected()) { ALBasicSendingClientManager.getInstance().addSendSocket(this); return ; } boolean needAddToSendList = false; _lockBuf(); while(!_m_lSendBufferList.isEmpty()) { //Socket 允许写入操作时 ByteBuffer buf = _m_lSendBufferList.getFirst(); if(buf.remaining() <= 0) { ALServerLog.Error("try to send a null buffer"); ALServerLog.Error("Wrong buffer:"); for(int i = 0; i < buf.limit(); i++) { ALServerLog.Error(buf.get(i) + " "); } } try { _m_scSocket.write(buf); //判断写入后对应数据的读取指针位置 if(buf.remaining() <= 0) _m_lSendBufferList.pop(); else break; } catch (IOException e) { ALServerLog.Error("Client Socket send message error! socket id[" + getID() + "]"); e.printStackTrace(); break; } } //当需要发送队列不为空时,继续添加发送节点 if(!_m_lSendBufferList.isEmpty()) needAddToSendList = true; _unlockBuf(); if(needAddToSendList) ALBasicSendingClientManager.getInstance().addSendSocket(this); } /********************* * 接收函数中将接收到的字节放入消息中,根据Socket之前收的残留信息进行一并处理。 * * @author alzq.z * @time Feb 19, 2013 10:00:23 PM */ protected void _socketReceivingMessage(ByteBuffer _buf) { //将数据放入缓冲中 try { _m_bByteBuffer.put(_buf); } catch (BufferOverflowException e) { ALServerLog.Error("_socketReceivingMessage length is too long, Socket Buffer need more!"); _m_bByteBuffer.put(_buf.array(), 0, _m_bByteBuffer.remaining()); //放置缓冲区读取指针 _buf.position(_m_bByteBuffer.remaining()); } if(0 == _m_sBufferLen) { //尚未读取长度前 if(_m_bByteBuffer.position() >= 4) { //当缓冲中字节大于2时可获取对应的消息长度 _m_sBufferLen = _m_bByteBuffer.getInt(0); } } //当长度有效则判断是否到达消息末尾 int bufLen = _m_bByteBuffer.position(); int startPos = 0; while(0 != _m_sBufferLen && bufLen >= startPos + _m_sBufferLen + 4) { //到达消息末尾,将消息取出,并清除缓存消息 ByteBuffer message = ByteBuffer.allocate(_m_sBufferLen); message.put(_m_bByteBuffer.array(), startPos + 4, _m_sBufferLen); message.flip(); startPos = startPos + _m_sBufferLen + 4; //添加消息 if(_m_bLoged) { _m_clClient.receiveMes(message); } else { //处理登录操作 _checkLoginMes(message); } //根据长度设置对应消息长度 if(bufLen - startPos > 4) { //当缓冲中字节大于2时可获取对应的消息长度 _m_sBufferLen = _m_bByteBuffer.getInt(startPos); } else { _m_sBufferLen = 0; break; } } //当数据经过了拷贝则将剩余数据拷贝放入缓存 if(startPos != 0) { ByteBuffer tmpBuf = ByteBuffer.allocate(bufLen - startPos); tmpBuf.put(_m_bByteBuffer.array(), startPos, bufLen - startPos); tmpBuf.flip(); _m_bByteBuffer.clear(); _m_bByteBuffer.put(tmpBuf); } //如原先缓存数据未完全放入,此时将剩余数据放入 if(_buf.remaining() > 0) { _m_bByteBuffer.put(_buf); } } protected SocketChannel _getSocketChannel() throws Exception { if(null == _m_scSocket) _m_scSocket = SocketChannel.open(); return _m_scSocket; } /************* * 未登录情况下对返回信息进行处理 * * @author alzq.z * @time Feb 19, 2013 10:02:04 PM */ protected void _checkLoginMes(ByteBuffer _mes) { _m_bLoginIng = false; try { S2C_BasicClientVerifyResult msg = new S2C_BasicClientVerifyResult(); msg.readPackage(_mes); //获取ID _m_iClientID = msg.getSocketID(); //直接当作登录成功处理,未成功将直接断开 _m_bLoged = true; _m_clClient.LoginSuc(msg.getCustomRetMsg()); } catch (Exception e) { _logout(); } } /************* * 断开连接 * * @author alzq.z * @time Feb 19, 2013 10:07:34 PM */ protected void _logout() { if(null != _m_scSocket) { try { _m_scSocket.close(); } catch (IOException e){} } _clearLoginValidate(); } /*********** * 清空连接相关变量 * * @author alzq.z * @time Feb 19, 2013 10:07:39 PM */ protected void _clearLoginValidate() { if(_m_bLoged) { //已经登录了为退出操作 _m_clClient.Disconnect(); } else if(_m_bLoginIng) { //正在登录为连接失败操作 _m_clClient.ConnectFail(); } else { //其他情况为登录失败操作 _m_clClient.LoginFail(); } _m_bLoged = false; _m_bLoginIng = false; _m_scSocket = null; } protected void _lockBuf() { _m_rSendListMutex.lock(); } protected void _unlockBuf() { _m_rSendListMutex.unlock(); } }