可实现简易的对话,登录状态和对话内容会在后台Service显示,由后台Service进行转发
协议部分,针对客户传来的消息,进行解包分析,并返回相应数据。
#pragma once
#define LOGIN_USER 1
#define USER_LIST 2
#define CHAT 3
#define ONLINE 4
struct PACK //协议公有访问,所以不能设置为class
{
int nProtoID; //协议号
int nPackSize; //数据包的大小 (>0 为大小 || <0 为错误)
};
struct LOGIN_PACK
{
PACK Proto;
char szName[52]; //可变长数组,动态内存分配
};
struct USER_LIST_PACK
{
PACK Proto;
char aUser[][52];
};
struct CHAT_PACK
{
PACK Proto;
char szName[52];
char szText[];
};
Service.cpp
使用网络编程和C++封装,多线程开发,避免服务接收消息出现时锁
#include "stdafx.h"
#include "RecvClass.h"
#include "Method.h"
void Login(char* szPack, LPVOID lpvParam, int nLen);
void getList(char* szPack, LPVOID lpvParam, int nLen);
void Chat(char* szPack, LPVOID lpvParam, int nLen);
SERVICE_MAP Protocol[] = { {LOGIN_USER,"用户协议",&Login},
{USER_LIST,"列表协议",&getList},
{CHAT,"通讯协议",&Chat},
{NULL,NULL,NULL} };
DWORD WINAPI RecvThread(LPVOID lpvParam);
USER_LIST_MAP* g_pMap;
RecvClass Rev;
MethodClass Mdc;
int main()
{
WSADATA wsData;
if (0 != WSAStartup(MAKEWORD(2, 2), &wsData))
{
return -1;
}
//创建侦听套接字
//创立套接字
SOCKET sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (INVALID_SOCKET == sockListen)
{
return -1;
}
//填写服务地址和端口号
SOCKADDR_IN ListenAddr;
ListenAddr.sin_family = AF_INET;
ListenAddr.sin_port = htons(5051);
ListenAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//绑定
if (SOCKET_ERROR == bind(sockListen, (sockaddr*)&ListenAddr, sizeof(ListenAddr)))
{
closesocket(sockListen);
return -1;
}
//设置侦听
if (SOCKET_ERROR == listen(sockListen, SOMAXCONN))
{
closesocket(sockListen);
return -1;
}
//接受
USER_LIST_MAP UserList[50] = { 0,"" };
g_pMap = UserList;
while (true)
{
SOCKET ChatSock = accept(sockListen, NULL, NULL);
if (INVALID_SOCKET == ChatSock)
{
continue;
}
int i = 0;
for (; i < 50; i++)
{
if (UserList[i].sClient == 0)
{
break;
}
}
UserList[i].sClient = ChatSock;
DWORD dwThreadID = 0;
CloseHandle(CreateThread(NULL, 0, RecvThread, (LPVOID)&UserList[i], 0, &dwThreadID));
}
WSACleanup();
return 0;
}
DWORD WINAPI RecvThread(LPVOID lpvParam)
{
Rev.CRecv(lpvParam);
return 0;
}
void Login(char* szPack, LPVOID lpvParam, int nLen)
{
Mdc.Login(szPack, lpvParam, nLen);
}
void getList(char* szPack, LPVOID lpvParam, int nLen)
{
Mdc.getList(szPack, lpvParam, nLen);
}
void Chat(char* szPack, LPVOID lpvParam, int nLen)
{
Mdc.Chat(szPack, lpvParam, nLen);
}
服务接收到消息进行消息处理
#include "Method.h"
void MethodClass::Login(char* szPack, LPVOID lpvParam, int nLen)
{
USER_LIST_MAP* pUser = (USER_LIST_MAP*)lpvParam;
pPack = (LOGIN_PACK*)szPack;
bool bFlag = true;
PACK pack = { LOGIN_USER , 0 };
//cmp 比较有没有重复的名字
for (int i = 0; i < 50; i++)
{
if (0 == strcmp(g_pMap[i].szName, pPack->szName))
{
bFlag = false;
}
}
if (bFlag)
{
strcpy(pUser->szName, pPack->szName);
pack.nPackSize = 1;
cout << pUser->szName << " Login......." << endl;
for (int i = 0; i < 50; i++)
{
if (0 != g_pMap[i].sClient)
{
if (0 == strcmp(g_pMap[i].szName, pUser->szName))
{
continue;
}
LOGIN_PACK LoginUser;
LoginUser.Proto.nProtoID = ONLINE;
LoginUser.Proto.nPackSize = 1;
strcpy(LoginUser.szName, pPack->szName);
send(g_pMap[i].sClient, (char*)&LoginUser, sizeof(LoginUser), 0);
}
}
}
send(pUser->sClient, (char*)&pack, sizeof(pack), 0);
}
void MethodClass::getList(char* szPack, LPVOID lpvParam, int nLen)
{
USER_LIST_MAP* pUser = (USER_LIST_MAP*)lpvParam;
int nCount = -1;
for (int i = 0; i < 50; i++)
{
if (0 != g_pMap[i].sClient)
{
nCount++;
}
}
int nSize = sizeof(PACK) * nCount * 52;
pUserList = (USER_LIST_PACK*)new char[nSize];
pUserList->Proto.nProtoID = USER_LIST;
pUserList->Proto.nPackSize = nCount;
for (int i = 0, j = 0; i < 50 && j < nCount; i++)
{
if (0 != g_pMap[i].sClient)
{
if (0 == strcmp(g_pMap[i].szName, pUser->szName))
{
continue;
}
strcpy(pUserList->aUser[j], g_pMap[i].szName);
j++;
}
}
send(pUser->sClient, (char*)pUserList, nSize, 0);
}
void MethodClass::Chat(char* szPack, LPVOID lpvParam, int nLen)
{
USER_LIST_MAP* pUser = (USER_LIST_MAP*)lpvParam;
ppPack = (CHAT_PACK*)szPack;
cout << "收到来自 " << pUser->szName << "的信息, 正在转发给 " << ppPack->szName << "内容为:" << ppPack->szText << endl;
int i = 0;
for (; i < 50; i++)
{
if (0 == strcmp(g_pMap[i].szName, ppPack->szName))
{
break;
}
}
if (i < 50)
{
//找到了
strcpy(pPack->szName, pUser->szName);
send(g_pMap[i].sClient, szPack, nLen, 0);
cout << "转发成功" << endl;
}
else
{
cout << "转发失败" << endl;
}
}
结果演示
登录界面
登录成功
列表框显示的是当前在线的人,点击可弹出聊天窗口,进行聊天