#include "netclient.h" #include "../model/datacenter.h" using namespace model; namespace network { NetClient::NetClient(model::DataCenter* dataCenter) :dataCenter(dataCenter) { initWebSocket(); } void NetClient::ping() { QNetworkRequest httpReq; httpReq.setUrl(QUrl(HTTP_URL + "/ping")); QNetworkReply* httpResp = httpClient.get(httpReq); connect(httpResp, &QNetworkReply::finished, this, [=]() { //到这里面,说明响应已经回了 if (httpResp->error() != QNetworkReply::NoError) { //请求失败 LOG() << "HTTP请求失败!!!" << httpResp->errorString(); httpResp->deleteLater(); return; } //获取到响应的body QByteArray body = httpResp->readAll(); LOG() << "响应的内容: " << body; httpResp->deleteLater(); }); } void NetClient::initWebSocket() { //准备好所需要的信号槽 connect(&websocketClient, &QWebSocket::connected, this, [=]() { LOG() << "webSocket 连接成功"; //连接成功之后,进行发送身份认证 sendAuth(); }); connect(&websocketClient, &QWebSocket::disconnected, this, [=]() { LOG() << "webSocket 连接断开"; }); connect(&websocketClient, &QWebSocket::textMessageReceived, this, [=](const QString& message) { LOG() << "webSocket 收到文本消息 " << message; }); connect(&websocketClient, &QWebSocket::binaryMessageReceived, this, [=](const QByteArray& byteArray) { LOG() << "webSocket 收到二进制的消息" << byteArray.length(); bite_im::NotifyMessage notifyMessage; notifyMessage.deserialize(&serializer, byteArray); handleWsResponse(notifyMessage); }); //和服务器真正建立连接 websocketClient.open(WEBSOCKET_URL); } void NetClient::handleWsResponse(const bite_im::NotifyMessage& notifyMessage) { if (notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY) { //收到消息 //把pb中的MessageInfo转成客户端自己的message Message message; message.load(notifyMessage.newMessageInfo().messageInfo()); //针对自己的message做进一步的处理 handleWsMessage(message); } else if (notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::CHAT_SESSION_CREATE_NOTIFY) { } else if (notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::FRIEND_ADD_APPLY_NOTIFY) { } else if (notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::FRIEND_ADD_PROCESS_NOTIFY) { } else if (notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::FRIEND_REMOVE_NOTIFY) { } } void NetClient::handleWsMessage(const model::Message& message) { // 这里要考虑两种情况 QList* messageList = dataCenter->getRecentMessageList(message.chatSessionId); if (messageList == nullptr) { //如果当前这个消息所属的会话,里面的消息列表没有在本地加载,此时需要通过网络先加载整个消息列表 connect(dataCenter, &DataCenter::getRecentMessageListDoneNoUI, this, &NetClient::receiveMessage, Qt::UniqueConnection); dataCenter->getRecnetMessageListAsync(message.chatSessionId, false); } else { //若已经在本地加载了,直接把这个消息尾插到消息列表中即可 messageList->push_back(message); this->receiveMessage(message.chatSessionId); } } void NetClient::sendAuth() { bite_im::ClientAuthenticationReq req; req.setRequestId(makeRequestId()); req.setSessionId(dataCenter->getLoginSessionId()); QByteArray body = req.serialize(&serializer); websocketClient.sendBinaryMessage(body); LOG() << "[WS身份认证] requestId= " << req.requestId() << ",loginSessionId= " << req.sessionId(); } QString NetClient::makeRequestId() { //基本的要求,确保每个请求的Id都是不重复的(唯一的) //通过UUID实现上述的效果 return "R" + QUuid::createUuid().toString().sliced(25, 12); } //通过这个函数,把发送HTTP请求的操作封装一下 QNetworkReply* NetClient::sendHttpRequest(const QString& apiPath, const QByteArray& body) { //构造出HTTP请求 QNetworkRequest httpReq; httpReq.setUrl(QUrl(HTTP_URL + apiPath)); httpReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-protobuf"); //发起HTTP请求 QNetworkReply* httpResp = httpClient.post(httpReq, body); return httpResp; } //在这个函数内部,完成具体的网络通信即可 void NetClient::getMyself(const QString& loginSessionId) { //构造出HTTP请求body部分 bite_im::GetUserInfoReq req; req.setRequestId(makeRequestId()); req.setSessionId(loginSessionId); QByteArray body = req.serialize(&serializer); LOG() << "获取个人信息->发送请求: requestId=" << req.requestId() << ", loginSessionId=" << loginSessionId; //构造出HTTP请求 QNetworkReply* httpResp = sendHttpRequest("/service/user/get_user_info", body); //通过信号槽,获取到当前的响应 connect(httpResp, &QNetworkReply::finished, this, [=]() { //if (httpResp->error() != QNetworkReply::NoError) { // //说明HTTP请求出错了, // LOG() << "HTTP error: " << httpResp->errorString(); // httpResp->deleteLater(); // return; //} ////说明拿到了body //QByteArray respBody = httpResp->readAll(); ////针对body进行反序列化,解析成对象 //bite_im::GetUserInfoRsp respObj; //respObj.deserialize(&serializer, respBody); ////判定一下业务上是否出错 //if (!respObj.success()) { // LOG() << "requestId= " << respObj.requestId() << ", errmsg=" << respObj.errmsg(); // httpResp->deleteLater(); // return; //} //继续处理后续的业务逻辑 //获取http回复 bool ok = false; QString reason; auto resp = handleHttpResponse(httpResp, &ok, &reason); //判定响应是否正确 if (!ok) { LOG() << "获取个人信息->出错 requestId= " << req.requestId() << ", reason= " << reason; return; } //把响应的数据保存到DataCenter dataCenter->resetMyself(resp); //通知调用逻辑,响应已经处理完成了,仍然通过信号槽通知 emit dataCenter->getMyselfDone(); //打印日志 LOG() << "获取个人信息->响应处理完毕 requestId" << req.requestId(); }); } void NetClient::getFriendList(const QString& loginSessionId) { //通过protobuf构造body bite_im::GetFriendListReq req; req.setRequestId(makeRequestId()); req.setSessionId(loginSessionId); QByteArray body = req.serialize(&serializer); LOG() << "获取好友列表->发送请求 requestId=" << req.requestId() << ", loginSessionId= " << loginSessionId; //发送HTTP请求 QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/get_friend_list", body); //处理响应 connect(httpResp, &QNetworkReply::finished, this, [=]() { //解析响应 bool ok = false; QString reason; auto friendListResp = this->handleHttpResponse(httpResp, &ok, &reason); if (!ok) { LOG() << "获取好友列表->失败 requestId= " << req.requestId() << ", reason= " << reason; } LOG() << "获取好友列表->成功"; //把结果保存在DataCenter中 dataCenter->resetFriendList(friendListResp); // 发送信号 emit dataCenter->getFriendListDone(); }); } void NetClient::getChatSessionList(const QString& loginSessionId) { //通过protobuf构造body bite_im::GetChatSessionListReq req; req.setRequestId(makeRequestId()); req.setSessionId(loginSessionId); QByteArray body = req.serialize(&serializer); LOG() << "获取会话列表->发送请求 requestId= " << req.requestId() << ", loginSessionId= " << loginSessionId; //发送HTTP请求 QNetworkReply* resp = this->sendHttpRequest("/service/friend/get_chat_session_list", body); //针对响应进行处理 connect(resp, &QNetworkReply::finished, this, [=]() { //解析响应 bool ok = false; QString reason; auto pbResp = this->handleHttpResponse(resp, &ok, &reason); //判断响应是否正确 if (!ok) { LOG() << "获取会话列表->失败 reason= " << reason; return; } //到这里说明没有问题,把得到的数据,写入到DataCenter中 dataCenter->resetChatSessionList(pbResp); //通知调用者,响应已经处理完毕了 emit dataCenter->getChatSessionListDone(); //打印日志 LOG() << "获取会话列表->处理响应完毕 requestId= " << pbResp->requestId(); }); } void NetClient::getApplyList(const QString& loginSessionId) { //通过protobuf构造body bite_im::GetPendingFriendEventListReq req; req.setRequestId(makeRequestId()); req.setSessionId(loginSessionId); QByteArray body = req.serialize(&serializer); LOG() << "获取好友申请列表->发送请求 reason= " << req.requestId() << ", loginSessonId= " << loginSessionId; //发送HTTP请求 QNetworkReply* resp = this->sendHttpRequest("/service/friend/get_pending_friend_events", body); //处理响应 connect(resp, &QNetworkReply::finished, this, [=]() { //解析响应 bool ok = false; QString reason; auto pbResp = this->handleHttpResponse(resp, &ok, &reason); //判定结果是否正确 if (!ok) { LOG() << "获取好友申请列表->失败 reason= " << reason; return; } //拿到的数据,写入到DataCenter中 dataCenter->resetApplyList(pbResp); //发送信号通知界面,已经处理完毕 emit dataCenter->getApplyListDone(); LOG() << "获取好友申请列表->处理响应完成 requestId= " << req.requestId(); }); } void NetClient::getRecentMessageList(const QString& loginSessionId, const QString& chatSessionId, bool updateUI) { //通过protobuf构造请求body bite_im::GetRecentMsgReq req; req.setRequestId(makeRequestId()); req.setChatSessionId(chatSessionId); req.setMsgCount(50); req.setSessionId(loginSessionId); QByteArray body = req.serialize(&serializer); LOG() << "获取最近消息->发送请求 requestId= " << req.requestId() << ", loginSessionId= " << loginSessionId; //发送http请求 QNetworkReply* resp = this->sendHttpRequest("/service/message_storage/get_recent", body); //处理响应 connect(resp, &QNetworkReply::finished, this, [=]() { //解析响应,反序列化 bool ok = false; QString reason; auto pbResp = this->handleHttpResponse(resp, &ok, &reason); //判定响应是否出错 if (!ok) { LOG() << "获取最近消息->失败 reason= " << reason; return; } //把拿到的数据,设置到DataCenter中 dataCenter->resetRecentMessageList(chatSessionId, pbResp); //发送信号,告知界面进行更新 if (updateUI) { emit dataCenter->getRecentMessageListDone(chatSessionId); } else { emit dataCenter->getRecentMessageListDoneNoUI(chatSessionId); } }); } //此处的extraInfo,可以用来传递扩展信息,尤其是对文件消息来说,通过这个字段表示“文件名” //其他类型的消息暂时不涉及,就直接设置为空,如果后续有消息类型需要,都可以给这个参数,赋予一定的特殊意义 void NetClient::sendMessage(const QString& loginSessionId, const QString& chatSessionId, model::MessageType messageType, const QByteArray& content, const QString& extraInfo) { //通过protobuf 构造 body bite_im::NewMessageReq req; req.setRequestId(makeRequestId()); req.setSessionId(loginSessionId); req.setChatSessionId(chatSessionId); //构造MessageContent bite_im::MessageContent messageContent; if (messageType == MessageType::TEXT_TYPE) { messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::STRING); bite_im::StringMessageInfo stringMessageInfo; stringMessageInfo.setContent(content); messageContent.setStringMessage(stringMessageInfo); } else if (messageType == MessageType::IMAGE_TYPE) { messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::IMAGE); bite_im::ImageMessageInfo imageMessageInfo; imageMessageInfo.setFileId(""); imageMessageInfo.setImageContent(content); messageContent.setImageMessage(imageMessageInfo); } else if (messageType == MessageType::FILE_TYPE) { messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::FILE); bite_im::FileMessageInfo fileMessageInfo; fileMessageInfo.setFileId(""); fileMessageInfo.setFileSize(content.size()); fileMessageInfo.setFileName(extraInfo); fileMessageInfo.setFileContents(content); messageContent.setFileMessage(fileMessageInfo); } else if (messageType == MessageType::SPEECH_TYPE) { messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::SPEECH); bite_im::SpeechMessageInfo speechMessageInfo; speechMessageInfo.setFileId(""); speechMessageInfo.setFileContents(content); messageContent.setSpeechMessage(speechMessageInfo); } else { LOG() << "错误的消息类型 messageType= " << messageType; } req.setMessage(messageContent); //序列化操作 QByteArray body = req.serialize(&serializer); LOG() << "发送消息->发送请求 requestId= " << req.requestId() << ", loginSessionId=" << req.sessionId() << ", chatSessionId= " << req.chatSessionId() << ", messageType=" << req.message().messageType(); //发送HTTP请求 QNetworkReply* resp = this->sendHttpRequest("/service/message_transmit/new_message", body); //处理HTTP响应 connect(resp, &QNetworkReply::finished, this, [=]() { //针对响应结果,进行解析 bool ok = false; QString reason; auto pbResp = this->handleHttpResponse(resp, &ok, &reason); //判定响应是否正确 if (!ok) { LOG() << "发送消息->处理出错 reason= " << reason; return; } //此处只是记录成功和失败,不需要把内写入到DataCenter中 //通知调用者,响应处理完毕 emit dataCenter->sendMessageDone(messageType, content, extraInfo); //打印日志 LOG() << "发送消息->响应处理完毕 requestId=" << pbResp->requestId(); }); } void NetClient::receiveMessage(const QString& chatSessionId) { //要先判定一下,这个收到消息对应的会话是否是正在被用户选中的“当前会话” //当前会话,就需要把消息显示到消息展示区,也需要更新会话列表消息预览 //若不是,只更新消息预览,并且更新“未读消息数目” if (chatSessionId == dataCenter->getCurrentSessionId()) { //说明是选中的会话 const Message& lastMessage = dataCenter->getRecentMessageList(chatSessionId)->back(); //通过信号,让NetClient模块能够通知界面 emit dataCenter->receiveMessageDone(lastMessage); } else { //说明不是 dataCenter->addUnread(chatSessionId); } //统一更新会话列表的消息预览 emit dataCenter->updateLastMessage(chatSessionId); } } //end namespace network