package com.neucrack.protocol;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.neucrack.GUI.PandaTVDanmu;
import com.neucrack.server.HttpRequest;
public class ConnectDanMuServer {
final static byte[] STARTFLAG= {0x00,0x06,0x00,0x02}; //连接弹幕服务器帧头
final static byte[] RESPONSE = {0x00,0x06,0x00,0x06}; //连接弹幕服务器响应
final static byte[] KEEPALIVE= {0x00,0x06,0x00,0x00}; //与弹幕服务器心跳心跳保持
final static byte[] RECEIVEMSG= {0x00,0x06,0x00,0x03}; //接收到消息
final static byte[] HEARTBEATRESPONSE ={0x00,0x06,0x00,0x01};//心跳保持服务器返回的值
final static String DANMUSERVERURL="http://www.panda.tv/ajax_chatinfo";
final static int IGNOREBYTELENGTH = 16;//弹幕消息体忽略的字节数
final static int MAX_AUTO_CONNECT_TIME = 5;//自动断线重连次数
static int count=0,count2=0;
private int mRid;
private int mAppid;
private String mTs;
private String mSign;
private String mAuthtype;
private String mIP;
private int mPort;
private int mErrno;
private String mErrMsg;
private Socket socket=null;
DataOutputStream os=null;
DataInputStream is=null;
private boolean mIsHeartBeatThreadStop=true;
private boolean mIsReceivMsgThreadStop=true;
private String mRoomID;
private boolean mIsSendHeartbeatPack=false;
private PandaTVDanmu mUIFrame=null;
public int getmRid() {
return mRid;
}
public void setmRid(int mRid) {
this.mRid = mRid;
}
public int getmAppid() {
return mAppid;
}
public void setmAppid(int mAppid) {
this.mAppid = mAppid;
}
public String getmTs() {
return mTs;
}
public void setmTs(String mTs) {
this.mTs = mTs;
}
public String getmSign() {
return mSign;
}
public void setmSign(String mSign) {
this.mSign = mSign;
}
public String getmAuthtype() {
return mAuthtype;
}
public void setmAuthtype(String mAuthtype) {
this.mAuthtype = mAuthtype;
}
public String getmIP() {
return mIP;
}
public void setmIP(String mIP) {
this.mIP = mIP;
}
public int getmPort() {
return mPort;
}
public void setmPort(int mPort) {
this.mPort = mPort;
}
public int getmErrno() {
return mErrno;
}
public void setmErrno(int mErrno) {
this.mErrno = mErrno;
}
public String getmErrMsg() {
return mErrMsg;
}
public void setmErrMsg(String mErrMsg) {
this.mErrMsg = mErrMsg;
}
public ConnectDanMuServer(PandaTVDanmu frame) {
mUIFrame=frame;
}
public ConnectDanMuServer(JSONObject json) {
JSONObject jsonData = (JSONObject) json.get("data");
JSONArray jsonArray=jsonData.getJSONArray("chat_addr_list");
String danMuAddress = jsonArray.getString(0);
mIP = danMuAddress.split(":")[0];
mPort = Integer.parseInt(danMuAddress.split(":")[1]);
mRid = (int) jsonData.getInt("rid");
mAppid = (int) jsonData.getInt("appid");
mAuthtype = jsonData.getString("authtype");
mSign = jsonData.getString("sign");
mTs = jsonData.getString("ts");
mErrno = json.getInt("errno");
mErrMsg = json.getString("errmsg");
}
public boolean JsonDecode(JSONObject json){
try{
JSONObject jsonData = (JSONObject) json.get("data");
JSONArray jsonArray=jsonData.getJSONArray("chat_addr_list");
String danMuAddress = jsonArray.getString(0);
mIP = danMuAddress.split(":")[0];
mPort = Integer.parseInt(danMuAddress.split(":")[1]);
mRid = (int) jsonData.getInt("rid");
mAppid = (int) jsonData.getInt("appid");
mAuthtype = jsonData.getString("authType");
mSign = jsonData.getString("sign");
mTs = String.valueOf(jsonData.getLong("ts"));
mErrno = json.getInt("errno");
mErrMsg = json.getString("errmsg");
}catch(JSONException e){
return false;
}catch(ClassCastException e){
return false;
}
return true;
}
/*
* 获取建立连接需要发送的数据
*/
public byte[] GetConnectData(){
String danMUMsg = "u:"+mRid+"@"+mAppid+"\nk:1\nt:300\nts:"+mTs+"\nsign:"+mSign+"\nauthtype:"+mAuthtype;
byte content[]=danMUMsg.getBytes();
byte length[]={(byte)(content.length>>8),(byte)(content.length&0xff)};
byte sendMessage[]=new byte[STARTFLAG.length+2+content.length];
System.arraycopy(STARTFLAG, 0, sendMessage, 0, STARTFLAG.length);
System.arraycopy(length, 0, sendMessage, STARTFLAG.length, length.length);
System.arraycopy(content, 0, sendMessage, STARTFLAG.length+length.length, content.length);
return sendMessage;
}
public boolean ConnectToDanMuServer(String roomID) {
mRoomID = roomID;
boolean isSuccess=false;
String result="";
JSONObject json;
result= HttpRequest.sendGet(DANMUSERVERURL,"roomid="+roomID);//发送http请求,获取基本参数
if(result==null)
return false;
try{
json= new JSONObject(result);//构建json对象
}catch(JSONException e){
return false;
}
if(!JsonDecode(json))//将json内容解析到成员变量中
return false;
//发送连接弹幕服务器请求(通过socket)
try {
socket=new Socket(mIP,mPort);//建立socket连接
os=new DataOutputStream(socket.getOutputStream());//构建输入输出流对象
is=new DataInputStream(socket.getInputStream());
os.write(GetConnectData());//发送
//接收响应数据
byte readData[]=new byte[6];
int rpLength=is.read(readData);
if(rpLength>=6){
if(!(readData[0]==RESPONSE[0]&&readData[1]==RESPONSE[1]&&readData[2]==RESPONSE[2]&&readData[3]==RESPONSE[3]))
isSuccess=false;
else{
isSuccess=true;
//消息主体,暂时用不到
/* short dataLength=(short) (readData[5]|(readData[4]<<8));
byte[] data=new byte[dataLength];//数据
is.read(data);//主体数据,appid+r的值,eg:id:845694055\nr:0 暂时用不到
*/
}
}else
isSuccess=false;
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(isSuccess){
//心跳保持
HeartBeat heartBeat=new HeartBeat();
Thread heartBeatThread=new Thread(heartBeat);//为心跳保持创建新的线程
mIsHeartBeatThreadStop=false;
heartBeatThread.start();//开始线程
//接收弹幕消息
ReceiveMessage ReceiveMsg=new ReceiveMessage();
Thread receiveMsgThread = new Thread(ReceiveMsg);
mIsReceivMsgThreadStop=false;
receiveMsgThread.start();
return true;
}
return false;
}
//实现Runnable的内,用来作为一个新线程与弹幕服务器保持连接(心跳)
private class HeartBeat implements Runnable{
byte[] heartBeatRsponse=new byte[4];
int autoConnectedTime=0;
public void run() {
while(!mIsHeartBeatThreadStop){
try {
os.write(KEEPALIVE);
if(mIsSendHeartbeatPack){//没有接收到响应,已经与服务器断开了
//连接断开,自动重新连接
ConnectToDanMuServer(mRoomID);
++autoConnectedTime;
if(autoConnectedTime>MAX_AUTO_CONNECT_TIME){//超过最大断线重连次数
mUIFrame.UpdateDanMu(new Danmu(1, 1, "", "30", "", "", "重连次数达到最大!", "", "1", ""));
autoConnectedTime=0;
mUIFrame.CloseConnection();
}
else{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
continue;
}
}
autoConnectedTime=0;//能走到这里,表示连接正常,所以对断线重连计数置零
mIsSendHeartbeatPack=true;//标志已经发送给了服务器心跳包
System.out.println("心跳");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(300000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/*
* 用来接收消息的新线程
*/
private class ReceiveMessage implements Runnable{
public void run() {
byte[] receivMsgFlag=new byte[4];
short msgLength;
byte[] ignoreBytes=new byte[IGNOREBYTELENGTH];
int mssageLength=0;
while(!mIsReceivMsgThreadStop){
try {
if(is.read(receivMsgFlag)>=4){//接收到消息
if(receivMsgFlag[0]==RECEIVEMSG[0]&&
receivMsgFlag[1]==RECEIVEMSG[1]&&
receivMsgFlag[2]==RECEIVEMSG[2]&&
receivMsgFlag[3]==RECEIVEMSG[3]){//接收到弹幕消息
//System.out.println("消息"+(count++));
msgLength = is.readShort();
if(msgLength>0){//接收消息体长度
byte[] rcvMsg=new byte[msgLength];
is.read(rcvMsg);
mssageLength=is.readInt();//获取后面消息的长度
is.read(ignoreBytes);
mssageLength-=IGNOREBYTELENGTH;//剩下的信息长度减去
if(mssageLength>0){
byte[] msg=new byte[mssageLength];//存放消息体
is.read(msg);//读消息体,msg即为弹幕消息体,格式为json格式
//解析消息体
MessageDecode(new String(msg,"UTF-8"));
}
}
}
else if(receivMsgFlag[0]==HEARTBEATRESPONSE[0]
&&receivMsgFlag[1]==HEARTBEATRESPONSE[1]
&&receivMsgFlag[2]==HEARTBEATRESPONSE[2]
&&receivMsgFlag[3]==HEARTBEATRESPONSE[3]){
//连接正常
mIsSendHeartbeatPack = false; //收到服务器对心跳包的响应,标志复位
}
else{
System.out.println(Integer.toHexString(receivMsgFlag[0])+" "+Integer.toHexString(receivMsgFlag[1])+" "+
Integer.toHexString(receivMsgFlag[2])+" "+
Integer.toHexString(receivMsgFlag[3])+" ");
System.out.println((char)receivMsgFlag[0]+" "+(char)receivMsgFlag[1]+" "+
(char)receivMsgFlag[2]+" "+
(char)receivMsgFlag[3]+" ");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
System.out.println("ReceiveMessage closed after socket closed:\n"+e);
}
}
}
}
Message messageHelper=new Message();
Object messageReceied;
/*
* 解读消息体,注意!!在弹幕多的情况下,一条msg中可能有多条弹幕信息
*/
public void MessageDecode(String messageStr){
//将这个json格式的字符串进行处理,获得相应类型的对象
int indexOfStrEnd;
indexOfStrEnd = messageStr.indexOf("{");
if(!messageStr.substring(indexOfStrEnd+1, indexOfStrEnd+2).equals("\"")){//开头有问题
int index=messageStr.indexOf("{",indexOfStrEnd+1);
if(index!=-1)
messageStr = messageStr.substring(messageStr.indexOf("{", indexOfStrEnd+1));//获取可用的子串
}
indexOfStrEnd=messageStr.indexOf("}}}");
if(indexOfStrEnd!=-1){//特殊消息(内容较多的消息)
if(indexOfStrEnd+2<messageStr.length()){//存在两条消息
int index=messageStr.indexOf("{",indexOfStrEnd);
if(index!=-1)
MessageDecode(messageStr.substring(index));//生成一个子串作为新的一条json
}
}
else{
indexOfStrEnd=messageStr.indexOf("}}");
if(indexOfStrEnd==-1)//没有符合条件的json字串
return;
if(indexOfStrEnd+2<messageStr.length()){//存在两条消息
MessageDecode(messageStr.substring(messageStr.indexOf("{",indexOfStrEnd)));//生成一个子串作为新的一条json
}
}
messageReceied = messageHelper.MessageDecode(messageStr);
if(messageReceied==null)
return;
mUIFrame.UpdateDanMu(messageReceied);
}
public void Close(){
if(socket!=null&&os!=null&&is!=null&&socket.isConnected()){
try {
mIsHeartBeatThreadStop=true;//停止心跳线程
mIsReceivMsgThreadStop=true;//停止弹幕消息接收
os.close();
is.close();
socket.close();
} catch (IOException e) {
//e.printStackTrace();
System.out.println(e);
}
}
}
}