赞
踩
HP-Socket 是一套通用的高性能 TCP/UDP/HTTP 通信框架,包含服务端组件、客户端组件和 Agent 组件,广泛适用于各种不同应用场景的 TCP/UDP/HTTP 通信系统,提供 C/C++、C#、Delphi、E(易语言)、Java、Python 等编程语言接口。HP-Socket 对通信层实现完全封装,应用程序不必关注通信层的任何细节;HP-Socket 提供基于事件通知模型的 API 接口,能非常简单高效地整合到新旧应用程序中。
HP-Socket的一些应用方向HP-Socket不同于传统MVC框架,HP-Socket不仅可以用于TCP/UDP开发,同时还有更广阔的应用领域,例如即时通讯类、物联网、游戏、服务治理、其它跨平台服务器或者中间件,这无疑大大提高了程序开发者的视野。目前这些领域的程序开发者奇缺,如果想在通讯领域有自己的技术优势,不满足于每天的增删改查工作,或者想向架构师方向或者技术大牛的方向发展,HP-Socket都是非常值得学习的框架。建议开发者不仅会用,而且能基于HP-Socket开发出属于自己的开源项目,提升技能增加自己的影响力。
github: GitHub - ldcsaa/HP-Socket: High Performance TCP/UDP/HTTP Communication Component
官方DEMO提供很多应用实例,但大多是MFC程序,所以准备搭建个基础版的,方便以后扩展。demo采用HPSocket的Pack模式,这样我们只需要处理逻辑,而不需要考虑分包、组包、粘包等情况
- EnHandleResult JSocketServer::OnPrepareListen(ITcpServer* pSender, SOCKET soListen)
- {
- TCHAR szAddress[50];
- int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);
- USHORT usPort;
-
- pSender->GetListenAddress(szAddress, iAddressLen, usPort);
-
- if (m_prepare_listen != nullptr) {
- m_prepare_listen(szAddress, iAddressLen, usPort);
- }
-
- return HR_OK;
- }
Client端 Start()后,会进入OnConnectListen(),可获取Server端 ip ,port ,也可获取本地 ip ,port。 server端会进入 OnAccept() ,获取上线用户 connid , ip , port,
- Client:
- EnHandleResult JSocketClient::OnConnect(ITcpClient* pSender, CONNID dwConnID)
- {
- TCHAR szAddress[50];
- int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);
- USHORT usPort;
- pSender->GetLocalAddress(szAddress, iAddressLen, usPort);
-
- if (m_connect_listen != nullptr) {
- m_connect_listen(dwConnID, szAddress, iAddressLen, usPort);
- }
-
- return HR_OK;
- }
-
- Server:
- EnHandleResult JSocketServer::OnAccept(ITcpServer* pSender, CONNID dwConnID, SOCKET soClient)
- {
- BOOL bPass = TRUE;
- TCHAR szAddress[50];
- int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);
- USHORT usPort;
-
- pSender->GetRemoteAddress(dwConnID, szAddress, iAddressLen, usPort);
-
- if (m_accepte_listen != nullptr) {
- m_accepte_listen(dwConnID, szAddress, iAddressLen, usPort, nullptr);
- }
- return bPass ? HR_OK : HR_ERROR;
- }
这里有个问题,就是不知道 client 和 server 如何通过连接绑定额外数据(比如用户名,地址等信息),类似于 SetConnectionExtra() 和 GetConnectionExtra() 那种方式。我采用的办法是当server收到用户上线通知后,主动将用户的信息发送给client端,client端收到消息后,将绑定信息发送给server,server收到通知后,缓存到集合中,然后将在线的用户通过json的方式发送给client端。(这种方式感觉不是很合理,暂且这么做把,以后再优化,有小伙伴知道的,麻烦指导一二。)
client端: 接收 server端 用户信息和在线用户通知
- JSocketResult JSocketClientBus::OnReceiveListen(ULONG connid, const UCHAR* data, int bodyLen) {
-
- JPkgBodyByte* bodyInfo = (JPkgBodyByte*)data;
- JUserInfo srcUserInfo;
- JUserInfo desUserInfo;
- GetUserInfoByBodyInfo(bodyInfo, srcUserInfo, desUserInfo);
-
- /*
- 当客户端连接服务端成功后,无法获取服务端中客户端的connid,所以客户端需要接受服务端的用户信息,即connid。
- 服务端当接受 OnAccept 监听后 ,给客户端发送用户信息指令 ,客户端收到用户信息指令后,保存用户信息。
- 最后发送消息给服务端,服务端接受后,更新本地该用户信息.(即Ip , Port , AliasName ...)
- */
- if (bodyInfo->msg_data_type == MSG_USER_INFO_TYPE) {
- m_userInfo.usId = desUserInfo.usId;
- m_userInfo.usAddress = new char[20];
- lstrcpyA(m_userInfo.usAddress, desUserInfo.usAddress);
- m_userInfo.usPort = desUserInfo.usPort;
- m_userInfo.usName = new char[strlen(m_aliasName) * sizeof(char)];
- lstrcpyA(m_userInfo.usName, m_aliasName);
- m_userInfo.usState = ONLINE_STATUS;
- /* 给服务端发送用户指令, 更新 AliasName */
- std::wstring s = L"updateUser";
- if (!SendMsgInfo(bodyInfo->msg_data_type, &m_userInfo, const_cast<wchar_t*>(s.c_str()), s.length())) {
- #if IS_LOG
- JSocketHelper::LogMsg("Client OnReceiveListen", m_userInfo, "发送用户信息给server失败.", GetUserLastErrorCode());
- #endif // IS_TOAST
- }
- if (m_connectAPIListen != nullptr) {
- m_connectAPIListen(&m_userInfo);
- }
- return JResult_OK;
- }
-
- // 获取用户列表
- if (bodyInfo->msg_data_type == MSG_USER_LIST_TYPE) {
- if (m_receiveAPIListen != nullptr) {
- m_receiveAPIListen(bodyInfo->msg_data_type, &srcUserInfo, &desUserInfo, bodyInfo->desc, bodyInfo->desLen);
- }
- return JResult_OK;
- }
-
- if (bodyInfo->msg_data_type == MSG_INFO_TYPE) {
- if (m_receiveAPIListen != nullptr) {
- m_receiveAPIListen(bodyInfo->msg_data_type, &srcUserInfo, &desUserInfo, bodyInfo->desc, bodyInfo->desLen);
- }
- return JResult_OK;
- }
-
- if (bodyInfo->msg_data_type == MSG_USER_ONLINE_TYPE || bodyInfo->msg_data_type == MSG_USER_OFFLINE_TYPE) {
- /* 有终端上线 */
- if (bodyInfo->msg_data_type == MSG_USER_ONLINE_TYPE && m_acceptAPIListen != nullptr) {
- m_acceptAPIListen(&srcUserInfo);
- return JResult_OK;
- }
- /* 有终端下线 */
- if (bodyInfo->msg_data_type == MSG_USER_OFFLINE_TYPE && m_closeAPIListen != nullptr) {
- m_closeAPIListen(&srcUserInfo);
- return JResult_OK;
- }
- return JResult_OK;
- }
-
- return JResult_OK;
- }
server端:
收到client端发送的用户信息后,将在线用户信息通过json方式发送给client端
将用户信息发送给别的client端,通知该用户上线了。
将用户信息缓存到集合中。
- JSocketResult JSocketServerBus::OnReceiveListen(ULONG connid, const UCHAR* data, int bodyLen) {
-
- JPkgBodyByte* bodyInfo = (JPkgBodyByte*)data;
-
- JUserInfo srcUserInfo;
- JUserInfo desUserInfo;
- GetUserInfoByBodyInfo(bodyInfo, srcUserInfo, desUserInfo);
-
- /* 更新用户信息 (connid, ip, port, AliasName ..)*/
- if (bodyInfo->msg_data_type == MSG_USER_INFO_TYPE) {
- m_user_collection.push_back(srcUserInfo);
-
- #if IS_LOG
- JSocketHelper::LogMsg("Server OnReceiveListen", srcUserInfo, "用户上线.", GetUserLastErrorCode());
- #endif // IS_TOAST
-
- if (m_acceptAPIListen != nullptr) {
- m_acceptAPIListen(&srcUserInfo);
- }
- // 通知用户上线
- post_user_info_to_users(MSG_USER_ONLINE_TYPE, srcUserInfo, "online");
- // 发送用户列表
- post_users_list_to_user(srcUserInfo);
- return JResult_OK;
- }
- // 发送消息
- if (bodyInfo->msg_data_type == MSG_INFO_TYPE) {
- if (!m_pSocketServer->SendMsg(desUserInfo.usId, (BYTE*)data, bodyLen)) {
- #if IS_LOG
- JSocketHelper::LogMsg("Server OnReceiveListen", srcUserInfo, desUserInfo, bodyInfo->desc, "转发消息失败.", GetUserLastErrorCode());
- #endif // IS_TOAST
- }
- if (m_receiveAPIListen != nullptr) {
- m_receiveAPIListen(bodyInfo->msg_data_type, &srcUserInfo, &desUserInfo, bodyInfo->desc, bodyInfo->desLen);
- }
- }
- return JResult_OK;
- }
- client:
- // msg_data_type: 收发消息类型 desUser:接受者信息 msg:内容(UTF-8 byte[])
- bool JSocketBusBase::SendMsgInfo(int msg_data_type, JUserInfo *desUser, wchar_t* msg, int len) {
- int bodyLen;
- JPkgBodyByte* info = JSocketHelper::GetMsgBodyInfo(msg_data_type, &m_userInfo, desUser, msg, len, bodyLen);
- bool flag = Get()->SendMsg(desUser->usId, (BYTE*)info, bodyLen);
- if (!flag) {
- #if IS_LOG
- JSocketHelper::LogMsg("Base SendMsgInfo", m_userInfo, *desUser, msg, "转发信息失败.", GetUserLastErrorCode());
- #endif // IS_LOG
- }
- free(info);
- return flag;
- }
-
- server:
- // 根据connid,转发消息
- JSocketResult JSocketServerBus::OnReceiveListen(ULONG connid, const UCHAR* data, int bodyLen) {
-
- JPkgBodyByte* bodyInfo = (JPkgBodyByte*)data;
-
- JUserInfo srcUserInfo;
- JUserInfo desUserInfo;
- GetUserInfoByBodyInfo(bodyInfo, srcUserInfo, desUserInfo);
-
- // 发送消息
- if (bodyInfo->msg_data_type == MSG_INFO_TYPE) {
- if (!m_pSocketServer->SendMsg(desUserInfo.usId, (BYTE*)data, bodyLen)) {
- #if IS_LOG
- JSocketHelper::LogMsg("Server OnReceiveListen", srcUserInfo, desUserInfo, bodyInfo->desc, "转发消息失败.", GetUserLastErrorCode());
- #endif // IS_LOG
- }
- if (m_receiveAPIListen != nullptr) {
- m_receiveAPIListen(bodyInfo->msg_data_type, &srcUserInfo, &desUserInfo, bodyInfo->desc, bodyInfo->desLen);
- }
- }
- return JResult_OK;
- }
- JSocketResult JSocketServerBus::OnCloseListen(ULONG connid) {
- JUserInfo user;
- BOOL flag = get_user_info_with_collection(connid, true, user);
- #if IS_LOG
- JSocketHelper::LogMsg("Server OnCloseListen", user, "用户下线.", GetUserLastErrorCode());
- #endif // IS_TOAST
- // 给用户发送下线通知
- if (flag) {
- post_user_info_to_users(MSG_USER_OFFLINE_TYPE, user, "offline");
- }
- else {
- // 缓存中没有id(todo)
- user.usId = connid;
- post_user_info_to_users(MSG_USER_OFFLINE_TYPE, user, "offline");
- }
- if (m_closeAPIListen != nullptr) {
- m_closeAPIListen(&user);
- }
- return JResult_OK;
- }
server端断开连接后,会在OnShutdownListen中接受到消息,其实不光是主动断开连接,异常断线,比如网络异常等情况也会进入此回调函数中。所以我在这个回调中增加了自动重连,也就是当用户非主动断开连接,则会自动重连。同理,client端也是如此。
- client:
- JSocketResult JSocketClientBus::OnCloseListen(ULONG connid) {
-
- // 正在重连中。。
- if (m_enState == EN_RECONNECTING)
- return JResult_OK;
-
- if (m_shutDownAPIListen != nullptr) {
- m_shutDownAPIListen();
- }
- // 主动断开连接,不进行自动重连..
- if (m_enState == EN_STOPPED || m_enState == EN_STOPPING)
- return JResult_OK;
-
- clear();
- reconnect();
- return JResult_OK;
- }
-
- server:
- JSocketResult JSocketServerBus::OnShutdownListen() {
-
- // 正在重连中。。
- if (m_enState == EN_RECONNECTING)
- return JResult_OK;
-
- if (m_shutDownAPIListen != nullptr) {
- m_shutDownAPIListen();
- }
-
- if (m_enState == EN_STOPPED || m_enState == EN_STOPPING)
- return JResult_OK;
-
- clear();
- reconnect();
- return JResult_OK;
- }
- client:
- /* client端 连接成功通知 */
- typedef void(*JSocketOnConnectListener)(JUserInfo *userAPI);
- /* client端 接受用户上线通知 */
- typedef void(*JSocketOnAcceptListener)(JUserInfo *server);
- /* client端 接受消息通知 srcUser:发送者 desUser:接受者 info:信息 */
- typedef void(*JSocketOnReceiveListener)(int msg_data_type, JUserInfo *srcUser, JUserInfo *desUser, wchar_t *info, int len);
- /* client端 已发送消息通知*/
- typedef void(*JSocketOnSendListener)(JUserInfo *body, wchar_t *info, int len);
- /* client端 用户下线通知 */
- typedef void(*JSocketOnCloseListener)(JUserInfo *user);
- /* client端 断开连接通知 */
- typedef void(*JSocketOnShutDownListener)();
-
- server:
- /* server端 连接成功通知 */
- typedef void(*JSocketOnPrepareListener)(JUserInfo *server);
- /* server端 接受用户上线通知 */
- typedef void(*JSocketOnAcceptListener)(JUserInfo *server);
- /* server端 接受消息通知 srcUser:发送者 desUser:接受者 info:信息 */
- typedef void(*JSocketOnReceiveListener)(int msg_data_type, JUserInfo *srcUser, JUserInfo *desUser, wchar_t *info, int len);
- /* server端 用户下线通知 */
- typedef void(*JSocketOnCloseListener)(JUserInfo *user);
- /* server端 断开连接通知 */
- typedef void(*JSocketOnShutDownListener)();
C# demo程序 + C++ DLL
效果:
地址: https://download.csdn.net/download/haiyangyunbao813/14999630
以上内容就基本搭建好了一个基础版本的socket通讯,等以后实际落地应用在慢慢扩展,有需要的小伙伴可以参考参考。
此程序么有怎么测试过,也没有落地实际应用过,只是我简单的测试了一下,所以说肯定会有很多潜在bug,以及不足的地方,所以说各位大神,大佬们,有啥问题请及时指正,非常感谢。
最后还有一个星期就过年了,在此祝各位小伙伴们新年快乐。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。