目录
1、通过Socket建立服务端:
(1)、创建一个Socket对象并绑定一个端口:CListenSocket 监听Socket
(2)、通过Listen()函数,监听来自客户端的连接请求
(3)、连接请求到来之后,建立一个通信用的Socket,CConnectSocket,并且加入数组中
(4)、使用Send()、Receive()函数,接收客户端发送的数据,并遍历数组,一次转发消息
2、UI设计:
3、代码的实现:
(1)、CListenSocket类
CListenSocket的目的是等待客户端的连接请求,继承Socket类,重写Socket的OnAccept函数,创建Socket对象,连接的初始化和等待客户端连接。
CListenSocket.h中
CListenSocket(CTcpServerDlg * pdlg);//传递对话框的指针
~CListenSocket();
virtual void OnAccept(int nErrorCode);//重写父类
CTcpServerDlg *m_pMainDlg;//接收传递的对话框指针
CListenSocket.cpp中
CListenSocket::CListenSocket(CTcpServerDlg * pdlg)
{
m_pMainDlg = pdlg; //初始化接收的对话框指针
}
void CListenSocket::OnAccept(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
CSocket::OnAccept(nErrorCode);
m_pMainDlg->ProcessPendingAccept();//对于连接函数的处理放在对话框函数中处理
}
(2)、CConnectSocket类
CConnectSocket的目的是通讯用的Socket,继承Socket类,重写Socket的OnReceive函数,数据的接收和处理以及数据的解析。
CConnectSocket.h中
CConnectSocket(CTcpServerDlg *pdlg);
~CConnectSocket();
virtual void OnReceive(int nErrorCode);
CTcpServerDlg *m_pMainDlg;
CConnectSocket.cpp中
CConnectSocket::CConnectSocket(CTcpServerDlg *pdlg)
{
m_pMainDlg = pdlg;
}
void CConnectSocket::OnReceive(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
CSocket::OnReceive(nErrorCode);
m_pMainDlg->ProcessPendingRead(this);//接收消息必须要传递指针进去,调用接收函数
}
(3)、CTcpServerDlg类
1)、声明服务端用于监听的socket
//服务端声明的用于监听的socket
CListenSocket *m_pListenSocket;
2)、声明一个CArray的对象,里面存放的CConectSocket指针
CArray<CConnectSocket*, CConnectSocket*&>m_ConnectSocketList;
//m_ConnectSocketList是CArray的一个对象,里面放着CConnectSocket的指针,并且可以对里面的数据进行访问和修改。
// CConnectSocket*&当数组元素被传递给函数时,使用引用方式传递元素,可以有效避免不必要的拷贝和内存开销。
3)、定义一个结构体,保存客户端的IP和Port
struct ClientAddr
{
CString strIp;
UINT uiPort;
};
4)、框架类的入口函数初始化监听Socket和端口号
m_pListenSocket = new CListenSocket(this);
//m_pListenSocket用来监听服务器的连接所以要分配内存
m_pListenSocket->Create(8080);
//初始化端口号
if (!m_pListenSocket->Listen())
{
MessageBox(L"监听失败!");
return FALSE;
}
5)、实现ProcessPendingAccept()函数
主要实现监听服务器的连接,监听到了就加到列表中
CConnectSocket *pConnectSocket = new CConnectSocket(this);
if (m_pListenSocket->Accept(*pConnectSocket))
{
m_ConnectSocketList.Add(pConnectSocket);
}
else {
delete pConnectSocket;
}
6)、实现ProcessPendingRead()函数
数据的接收,并且存放在buff中
TCHAR buff[4096];//存放接收到的数据
//接收数据
int nRead = connectSocket->Receive(buff, 4096);
if (nRead == SOCKET_ERROR)
{
return;
}
buff[nRead] = L'\0';//接收到的数据末尾加上结束符
CString strTemp(buff);//格式化buff
把连接到的客户端IP和Port存放在结构体中
ClientAddr clientAddr;//用来存放客户端的IP和Port
//获取连接上来的客户端的IP和端口
connectSocket->GetPeerName(clientAddr.strIp, clientAddr.uiPort);
解析数据:加入房间enter
int i = 0;
//解析数据
if (strTemp.CompareNoCase(L"enter") == 0)
{
//通知其他客户端有人进入房间
CString strEnterMsg;
strEnterMsg.Format(L"系统消息:%s(%d)进入了房间", clientAddr.strIp, clientAddr.uiPort);
for ( i;i<m_ConnectSocketList.GetSize();i++)
{//遍历每一个客户端,并且发送消息给每个客户端。
CConnectSocket *& tempClient = m_ConnectSocketList.ElementAt(i);
tempClient->Send(strEnterMsg, strEnterMsg.GetLength() + 100);
}
显示到界面
//更新界面
SetDlgItemInt(IDC_EDIT_NUMBER, m_ConnectSocketList.GetSize());//显示当前客户端人数
CString ALmSG;
GetDlgItemText(IDC_EDIT_MESSAGE, ALmSG);//EDIT上显示的之前的消息
SetDlgItemText(IDC_EDIT_MESSAGE, ALmSG + L"\r\n" + strEnterMsg);
//显示的现在的消息加上之前的消息
解析数据:离开房间leave
//从列表中移除
for (i;i<m_ConnectSocketList.GetSize();i++)
{
CConnectSocket *&temClient = m_ConnectSocketList.ElementAt(i);
ClientAddr temClientAddr;
temClient->GetPeerName(temClientAddr.strIp, temClientAddr.uiPort);
if (temClientAddr.uiPort == clientAddr.uiPort && temClientAddr.strIp.Compare(clientAddr.strIp) == 0)
{//当前移除的IP和Port如果和加入的时候存放在结构体的一样直接跳出循环删除
break;
}
if (i<m_ConnectSocketList.GetSize())
{
m_ConnectSocketList.RemoveAt(i);
}
发送给每一个客户端消息
//通知所有人
CString strleaveMsg;
strleaveMsg.Format(L"系统消息:%s(%d)进入了房间", clientAddr.strIp, clientAddr.uiPort);
for (i; i < m_ConnectSocketList.GetSize(); i++)
{
CConnectSocket *& tempClient = m_ConnectSocketList.ElementAt(i);
tempClient->Send(strleaveMsg, strleaveMsg.GetLength() + 100);
}
更新界面
//更新界面
SetDlgItemInt(IDC_EDIT_NUMBER, m_ConnectSocketList.GetSize());
CString ALmSG;
GetDlgItemText(IDC_EDIT_MESSAGE, ALmSG);
SetDlgItemText(IDC_EDIT_MESSAGE, ALmSG + L"\r\n" + strleaveMsg);
发送的是普通消息
//遍历每一个客户端发出,然后更新界面即可
CString strMsg;
strMsg.Format(L"%s(%d):%s", clientAddr.strIp, clientAddr.uiPort,strTemp);
for (i; i < m_ConnectSocketList.GetSize(); i++)
{
CConnectSocket *& tempClient = m_ConnectSocketList.ElementAt(i);
tempClient->Send(strMsg, strMsg.GetLength() + 100);
}
//更新界面
SetDlgItemInt(IDC_EDIT_NUMBER, m_ConnectSocketList.GetSize());
CString ALmSG;
GetDlgItemText(IDC_EDIT_MESSAGE, ALmSG);
SetDlgItemText(IDC_EDIT_MESSAGE, ALmSG + L"\r\n" + strMsg);