1、客户端通过用户名和密码登录业务层服务器,获取到userId,appid,token等参数
2、通过http请求入口服务,拿到消息服务器的IP地址和端口号
public void login(String userId, String password, final GIMCallback callback) {
//首先获取入口服务
GIMGetEnterService task = new GIMGetEnterService(mContext);
task.setOnGetEnterService(new GIMOnGetEnterService() {
@Override
public void onSuccess(final EnterServiceInfos enterServiceInfos) {
//使用新的IP和端口号
updateNewIpAndPort();
//设置连接监听
initNetConnectListener();
//启动socket通讯层
GIMNetApi.InitSendQueue();
}
@Override
public void onFailed() {
}
});
task.execute();
}
1)updateNewIpAndPort()函数用来调用InitNetConfig配置服务器IP和端口号
Java_com_vkansee_jnidemo_jniapi_GIMNetApi_InitNetConfig(JNIEnv *_evn, jclass _thiz, jstring _ip, jint _port,
jstring _sendId, jint _version,
jlong _appId, jboolean _model) {
GSKCNet::sharedInstance()->ChangeIpAndPort(JniHelper::jstring2stringNew(_evn, _ip), (int)_port);
GSKCNet::sharedInstance()->SetSendIdAndAppVer(JniHelper::jstring2stringNew(_evn, _sendId), (int)_version);
GSKCNet::sharedInstance()->SetAppId((long long)_appId);
}
2)initNetConnectListener()函数用来设置连接成功或失败的回调
3)InitSendQueue用来初始化网络连接
bool GSKCNet::Init()
{
//初始化网络对象
if (!m_pGSKSocket)
{
m_pGSKSocket = new GSKSocket( m_strIp , m_iPort );
}
//连接网络,会根据策略最多尝试5次
//联网线程
std::thread t = std::thread(std::bind(&GSKCNet::initNetwork, this));
t.detach();
return true;
}
initNetwork的流程如下:
1) 停掉所有的线程(发送线程、接收线程、回调线程)
2) 等待所有的线程都已经正常退出,最多等5秒
3) 尝试连接5次,5次连接不成功调用连接失败回调
4) 连接成功启动所有的线程
代码逻辑如下:
void GSKCNet::initNetwork()
{
SetInitNetwork(true);
StopAllThread();
//等待线程退出,最多5秒
int num = 0;
while (!GetExitSendLoop() || !GetExitRecvLoop() || !GetExitCallbckLoop())
{
glodon::sleep(50);
m_sleepStrategy.notify_all(); //重连需要设置睡眠间隔,重连可能在发送线程中进行
++num;
if (num >100)
{
break;
}
}
//失败5次就不重连了
if (TryConnectServer(10, INIT_CNETWORK))
{
//开始线程
Start();
}
SetInitNetwork(false);
}
连接过程的逻辑如下:
bool GSKCNet::TryConnectServer(int num, enum CONNECTNETWORKSRC type)
{
if (GetTryReconnected()) //如果其他线程已经正在重连,啥都不做
{
return false;
}
SetTryReconnected(true);
if (GetIsLogged()) //如果已经登录,提示连接断开
{
if (g_netReConnectCallback)
{
g_netReConnectCallback(msg);
}
}
SetLastConnectTime(time(NULL));
int i = 0;
while (i < num)
{
if (m_pGSKSocket)
{
m_pGSKSocket->Close(); //关闭之前的socket
}
m_recvBuffer.retrieveAll();
if (!SleepTimeStrategy(i)) //设置睡眠间隔,如果是被主动唤醒的,直接退出循环,不需要再继续连接了
{
break;
}
if (m_pGSKSocket && m_pGSKSocket->Connect(1) > 0)
{ //连接成功
if(g_netConnectCallback)
{
g_netConnectCallback(true);
}
SetTryReconnected(false);
return true;
}else{
//连接失败
if(g_netConnectCallback)
{
g_netConnectCallback(false);
}
}
++i;
}
SetTryReconnected(false);
return false;
}
SleepTimeStrategy睡眠的策略如下:
bool GSKCNet::SleepTimeStrategy(int num)
{
int timeOut = 500;
if (0 == num)
timeOut = 500;
else if (1 == num)
timeOut = 2000;
else if (2 == num)
timeOut = 4000;
else if (3 == num)
timeOut = 6000;
else if (4 == num)
timeOut = 8000;
else
timeOut = 10000;
std::mutex conlock;
std::unique_lock < std::mutex > lck(conlock);
//超时解除等待
if (m_sleepStrategy.wait_for(lck, std::chrono::milliseconds(timeOut)) == std::cv_status::timeout)
{
return true;
}
else
{
//被主动唤醒
return false;
}
}
Connect代码的逻辑如下:
int GSKSocket::Connect( int iTimeout)
{
if( IsConnected() )
{
return m_iSocket;
}
//配置想要的地址信息兼容ipv4,ipv6
struct addrinfo addrCriteria;
memset(&addrCriteria,0,sizeof(addrCriteria));
addrCriteria.ai_family=AF_UNSPEC;
addrCriteria.ai_socktype=SOCK_STREAM;
addrCriteria.ai_protocol=IPPROTO_TCP;
struct addrinfo *server_addr;
//获取地址信息
char buf[25] = {0};
sprintf(buf,"%d",m_iPort);
int retVal=getaddrinfo(m_strIP.c_str(),buf,&addrCriteria,&server_addr);
if(retVal!=0)
{
return -11;
}
struct addrinfo *addr=server_addr;
int iRet = -1;
while(addr!=NULL)
{
//建立socket
m_iSocket = socket(addr->ai_family,addr->ai_socktype,addr->ai_protocol);
if(m_iSocket < 0) //Create socket failed
{
addr=addr->ai_next;
if(!addr)
{
iRet = -12;
}
continue;
}
int iNoDelay = 0;
if (setsockopt(m_iSocket, IPPROTO_TCP, TCP_NODELAY, //使用setsockopt TCP_NODELAY禁用 Nagle算法
reinterpret_cast<const char*>(&iNoDelay), (socklen_t)sizeof(iNoDelay)) != 0)
{
//set socket TCP_NODELAY opt failed
addr=addr->ai_next;
if(!addr)
{
Close();
iRet = -13;
}
continue;
}
auto result = glodon::setSocketNBlocked( m_iSocket );
if ( result != 0 )
{
//set socket NBLOCK failed
addr=addr->ai_next;
if(!addr)
{
Close();
iRet = -14;
}
continue;
}
if (!glodon::connect( m_iSocket, addr->ai_addr ,addr->ai_addrlen) )
{
//connect to server failed;
addr=addr->ai_next;
if(!addr)
{
Close();
iRet = -15;
}
continue;
}
fd_set stFdSet;
FD_ZERO(&stFdSet);
FD_SET(m_iSocket, &stFdSet);
struct timeval stTimeout;
stTimeout.tv_sec = iTimeout;
stTimeout.tv_usec = 0;
if ((iRet = select(m_iSocket + 1, NULL, &stFdSet, NULL, &stTimeout)) == -1)
{
//select check read failed
addr=addr->ai_next;
if(!addr)
{
Close();
iRet = -16;
}
continue;
}
else if (iRet == 0)
{
//select check read timeout
errno = ETIMEDOUT;
addr=addr->ai_next;
if(!addr)
{
Close();
iRet = -17;
}
continue;
}
int iSockErr;
socklen_t iSockErrLen = sizeof(iSockErr);
if (getsockopt(m_iSocket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&iSockErr), &iSockErrLen) == -1)
{
//get socket so_error opt failed
addr=addr->ai_next;
if(!addr)
{
Close();
iRet = -18;
}
continue;
}
if (iSockErr)
{
LOGI_GSKCNET_INFO("get socket so_error opt socket = %d, error=%d", m_iSocket, iSockErr);
errno = iSockErr;
addr=addr->ai_next;
if(!addr)
{
Close();
iRet = -19;
}
continue;
}
if(iRet > 0)
{
break;
}
}
freeaddrinfo(server_addr);
if(m_iSocket != INVALID_SOCKET)
{
LOGI_GSKCNET_INFO("connect to server succeed, socket=%d", m_iSocket);
return m_iSocket;
}
LOGI_GSKCNET_INFO("connect to server failed, errorCode=%d", iRet);
return iRet;
}
连接成功后会启动所有的线程,过程如下:
void GSKCNet::Start()
{
//开启发送线程
m_workThread = std::thread(std::bind( &GSKCNet::Loop, this));
m_workThread.detach();
//开启接收线程
m_recvThread = std::thread(std::bind(&GSKCNet::recvLoop, this));
m_recvThread.detach();
//开启回调处理线程
m_callbackThread = std::thread(std::bind(&GSKCNet::callbackLoop, this));
m_callbackThread.detach();
}
停止所有线程的逻辑如下:
void GSKCNet::StopAllThread()
{
m_waitExitLock.lock();
SetIsLogged(false);
SetWorking(false);
m_sendcv.notify_all();
SetRecvLoop(false);
m_sleepStrategy.notify_all();
SetCallbckLoop(false);
m_cv.notify_all();
m_waitExitLock.unlock();
}
注意,线程可能会阻塞在某些条件变量上,停止线程的时候,一定要把它们唤醒,否则线程无法正常退出 。
网友评论