功能簡介
私聊,群聊,查看在線好友,檔案匯入,日志匯出 (MFC+Socket)
運行環境
vs2019
專案截圖
- 服務器端

- 客戶端

重要代碼
服務器
- 開啟、關閉服務器
void CServerDlg::OnBnClickedBtnStartStop()
{
// TODO: 在此添加控制元件通知處理程式代碼
if (m_srvrStatus=="已停止") //狀態為已停止,則要開啟服務器監聽埠
{
UpdateData();
CString log=_T("服務器已開啟,等待客戶端連接......");
m_ctrlChatRoomInfo.AddString(log);
WCHAR str[3];
m_ctrlMaxCnt.GetWindowTextW(str,3);//將最大客戶端數 那個下拉視窗當前的數,拷貝到str中
m_maxClient=_ttoi(str);//將str從String轉換到int
if (m_listenPort<1024)
{
MessageBox(_T("請選擇大于1024的埠!"),_T("提示"),MB_ICONWARNING);
return;
}
if (!m_serverSocket.Create(m_listenPort))
{
MessageBox(_T("創建套接字出錯!"),_T("錯誤"),MB_ICONERROR);
return;
}
if (!m_serverSocket.Listen())
{
MessageBox(_T("監聽失敗!"),_T("錯誤"),MB_ICONERROR);
return;
}
GetDlgItem(IDC_BTN_START_STOP)->SetWindowTextW(_T("停止"));
GetDlgItem(IDC_LISTENPORT)->EnableWindow(FALSE);
}
else //狀態為正在監聽,要停止監聽
{
CString log = _T("服務器已關閉!!");
m_ctrlChatRoomInfo.AddString(log);
GetDlgItem(IDC_BTN_START_STOP)->SetWindowTextW(_T("啟動"));
GetDlgItem(IDC_LISTENPORT)->EnableWindow(TRUE);
m_clientCnt = 0;
vector<CClientSocket *>::iterator iter;
for (iter = clientList.begin(); iter != clientList.end(); )
{
unsigned char buf[2]={0};
buf[0]=SERVERCLOSE;
(*iter)->Send(buf,1);
(*iter)->Close();
delete (*iter);
iter=clientList.erase(iter);
}
clientList.clear();
m_serverSocket.Close();//關閉服務端套接字
}
m_srvrStatus=m_srvrStatus=="正在監聽……"?"已停止":"正在監聽……";
GetDlgItem(IDC_STATUS)->SetWindowTextW(m_srvrStatus);
UpdateData(FALSE);
}
- 服務器處理客戶端發來的訊息
void CServerDlg::ProcessData(CClientSocket *clientSocket)
{
unsigned char buf[MAX_BUFSIZE] = { 0 };
wchar_t wbuf[MAX_BUFSIZE] = { 0 };
wchar_t srcName[16] = { 0 }, dstName[16] = { 0 };
int len = clientSocket->Receive(buf, MAX_BUFSIZE);
vector<CClientSocket *>::iterator iter;
vector<CString>::iterator file_iter;//檔案串列
CString log, usrName, usrNP, usrLove, usrAge, usrSex;
CTime tm;
bool undefine = true;//判斷是否注冊或是密碼正確
CString data_c_srcName;
unsigned char data_buf[MAX_BUFSIZE] = { 0 };
CString strPath;
char CH_nameLength = NULL;
int IN_namelength = 0;
unsigned int strName_length = 0;
char chName_length;
string temp;
ifstream fin("user.txt", ios::in);//在這個路徑下讀入檔案
ofstream fout("user.txt", ios::app);//在同一個路徑下,如果注冊成功則寫入檔案
switch (buf[0])
{
case NEWCLIENT: //收到用戶要登錄的訊息
memcpy(wbuf, buf + 1, 16); //獲取用戶名
usrName = wbuf;
memcpy(wbuf, buf + 1, len - 1); //獲取用戶名和密碼
usrNP = wbuf;
clientSocket->m_userName = usrName; //設定用戶名
while (std::getline(fin, temp))//每次讀一行的資料進入temp中,
{
if (usrNP == temp.c_str()) //用戶名和密碼都正確
{
undefine = false;
break;
}
}
fin.close();
if (undefine)
{
buf[0] = ERROR;
clientSocket->Send(buf, 1); //用戶未注冊或密碼不正確
return;
}
for (iter = clientList.begin(); iter < clientList.end(); iter++)
{
if (*iter != NULL)
{
(*iter)->Send(buf, 17); //轉發到每個客戶端
}
}
Sleep(100);
for (iter = clientList.begin(); iter < clientList.end() - 1; iter++)
{
if (*iter != NULL)
{
buf[0] = ONLINECLIENT;
memcpy(wbuf, (*iter)->m_userName.GetBuffer(0), (*iter)->m_userName.GetLength() * 2);
memcpy(buf + 1, wbuf, (*iter)->m_userName.GetLength() * 2);
clientSocket->Send(buf, (*iter)->m_userName.GetLength() * 2 + 1);
Sleep(100);
}
}
break;
case REGISTER://注冊
memcpy(wbuf, buf + 1, 16); //獲取用戶名
usrName = wbuf;
memcpy(wbuf, buf + 1, len - 1); //獲取用戶名和密碼
usrNP = wbuf;
clientSocket->m_usrNP = usrNP; //設定用戶名和密碼
clientSocket->m_userName = usrName; //設定用戶名
//判斷用戶是否注冊過
while (std::getline(fin, temp))//每次讀一行的資料進入temp中,
{
temp = temp.substr(0,8);
if (usrName == temp.c_str()) //用戶注冊過
{
undefine = false;
buf[0] = HADDEFINED;
clientSocket->Send(buf, 1); //用戶已注冊過
return;
}
}
if (undefine)
{
this->m_ctrlUsers.AddString(usrNP);
temp = CT2A(usrNP.GetBuffer());
fout << temp << endl;//向檔案寫入注冊者的用戶名,然后換一行
fout.close();
buf[0] = REGISTER;
clientSocket->Send(buf, 1); //注冊用戶
UpdateData(false);
return;
}
break;
case MESSAGE_ALL: //群發
for (iter = clientList.begin(); iter < clientList.end(); iter++)
{
if (*iter != NULL)
{
(*iter)->Send(buf, len);
}
}
break;
case MESSAGE_ONE: //私聊
memcpy(dstName, buf + 1, 16);
memcpy(srcName, buf + 17, 16);
for (iter = clientList.begin(); iter < clientList.end(); iter++)
{
if ((*iter)->m_userName == dstName) //發給目的用戶
{
(*iter)->Send(buf, len);
break;
}
}
break;
case EXIT: //斷開連接的代碼
tm = CTime::GetCurrentTime();
log.Format(_T("【%s : %u】與服務器斷開,"), clientSocket->m_userIP, clientSocket->m_userPort);
log = tm.Format("%Y-%m-%d %X : ") + log;
m_ctrlChatRoomInfo.AddString(log);
m_clientCnt--;
UpdateData(FALSE);
clientSocket->Close();
for (iter = clientList.begin(); iter != clientList.end();)
{
if (*iter == clientSocket) //連接斷開,洗掉套接字
{
iter = clientList.erase(iter);
}
else //給其他客戶端轉發下線的訊息
{
(*iter)->Send(buf, len);
iter++;
}
}
delete clientSocket;
break;
default:
break;
}
}
客戶端
- 客戶端連接服務器
void CClientDlg::OnBnClickedBtnConnect()
{
// TODO: 在此添加控制元件通知處理程式代碼
CString str;
GetDlgItem(IDC_BTN_CONNECT)->GetWindowTextW(str);
if (str == "連接服務器") //當前處于斷開狀態
{
UpdateData();
if (!m_clientSocket.Create()) //建立Socket
{
MessageBox(_T("創建套接字失敗!"),_T("錯誤"),MB_ICONERROR);
return;
}
if (!m_clientSocket.Connect(_T("127.0.0.1"),2537))
{
MessageBox(_T("連接服務器失敗,請稍候重試!"),_T("錯誤"),MB_ICONERROR);
m_clientSocket.Close();
return;
}
GetDlgItem(IDC_BTN_CONNECT)->SetWindowTextW(_T("斷開連接"));
m_publicChannel += _T("【系統訊息】成功連接服務器!");
m_publicChannel += _T("\r\n");
UpdateData(false);
}
else //當前處于連接狀態
{
UpdateData();
unsigned char buf[MAX_BUFSIZE];
buf[0] = EXIT; //斷開連接
memcpy(buf+1,m_usrName.GetBuffer(0),m_usrName.GetLength()*2);
m_clientSocket.Send(buf,m_usrName.GetLength()*2+1); //退出用戶的用戶名
m_clientSocket.Close();
GetDlgItem(IDC_BTN_CONNECT)->SetWindowTextW(_T("連接服務器"));
GetDlgItem(IDC_USERNAME)->EnableWindow(TRUE); //斷開后可以改用戶名
GetDlgItem(IDC_EDIT1)->EnableWindow(TRUE); //斷開后可以改密碼
m_ctrlUserOnline.ResetContent(); //清空在線用戶串列
GetDlgItem(IDC_BUTTON1)->EnableWindow(true); //斷開后可再點擊登錄
GetDlgItem(IDC_BUTTON2)->EnableWindow(true); //斷開后可以再點擊注冊
m_publicChannel += _T("【系統訊息】成功斷開服務器!");
m_publicChannel += _T("\r\n");
m_publicChannel += _T("【系統訊息】");
m_publicChannel += m_usrName;
m_publicChannel += _T("被強制下線!\r\n");
UpdateData(false);
}
}
//注冊
void CClientDlg::OnRegister()
{
// TODO: 在此添加控制元件通知處理程式代碼
if (m_clientSocket.m_hSocket == INVALID_SOCKET)
{
MessageBox(_T("未連接到服務器!"), _T("錯誤"), MB_ICONERROR);
return;
}
else
{
UpdateData();
if (m_usrName.IsEmpty()||m_usrPassword.IsEmpty())
{
MessageBox(_T("請輸入用戶名/密碼!"), _T("提示"), MB_ICONWARNING);
GetDlgItem(IDC_USERNAME)->SetFocus();
return;
}
if (m_usrName.GetLength()!=8)
{
MessageBox(_T("用戶名不等于8位"), _T("提示"), MB_ICONWARNING);
GetDlgItem(IDC_USERNAME)->SetFocus();
return;
}
else
{
unsigned char buf[MAX_BUFSIZE];
buf[0] = REGISTER;
m_usrNP = m_usrName + m_usrPassword;
memcpy(buf + 1, m_usrNP.GetBuffer(0), m_usrNP.GetLength() * 2);
m_clientSocket.Send(buf, m_usrNP.GetLength() * 2 + 1);
GetDlgItem(IDC_USERNAME)->EnableWindow(FALSE); //登陸后后不可更改用戶名
}
}
}
//登錄
void CClientDlg::OnLogoIn()
{
// TODO: 在此添加控制元件通知處理程式代碼
if (m_clientSocket.m_hSocket == INVALID_SOCKET)
{
MessageBox(_T("未連接到服務器!"), _T("錯誤"), MB_ICONERROR);
return;
}
else
{
UpdateData();
if (m_usrName.IsEmpty()||m_usrPassword.IsEmpty())
{
MessageBox(_T("請輸入用戶名/密碼!"), _T("提示"), MB_ICONWARNING);
GetDlgItem(IDC_USERNAME)->SetFocus();
return;
}
unsigned char buf[MAX_BUFSIZE];
buf[0] = NEWCLIENT;
m_usrNP = m_usrName + m_usrPassword;
//m_usrNP +=m_usrPassword;
memcpy(buf + 1, m_usrNP.GetBuffer(0), m_usrNP.GetLength() * 2);
m_clientSocket.Send(buf, m_usrNP.GetLength() * 2 + 1);
GetDlgItem(IDC_USERNAME)->EnableWindow(FALSE); //登陸后后不可更改用戶名
GetDlgItem(IDC_EDIT1)->EnableWindow(FALSE); //登陸后后不可更改密碼
GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE); //登陸后后不可再點擊登錄
GetDlgItem(IDC_BUTTON2)->EnableWindow(false); //登陸后不可以再點擊注冊
}
}
*客戶端的事件處理
void CClientDlg::ProcessRecvData()
{
CString str;
unsigned char buf[MAX_BUFSIZE]={0};
int len = m_clientSocket.Receive(buf,MAX_BUFSIZE);
wchar_t msg[MAX_BUFSIZE]={0};//訊息內容
wchar_t usrName[MAX_BUFSIZE]={0};//用戶名字
wchar_t srcName[16] = {0};
//接收檔案串列
char CH_nameLength = NULL;
int IN_namelength = 0;
srand((unsigned int)time(NULL));
if (NEWCLIENT == buf[0]) //新用戶上線訊息
{
memcpy(usrName, buf + 1, len-1); //用戶名
if (LB_ERR == m_ctrlUserOnline.FindString(-1,usrName)) //在線用戶增加
{
m_ctrlUserOnline.AddString(usrName);
}
if (LB_ERR == m_ctrlPeopleToTalk.FindString(-1,usrName)) //可以私聊的人增加
{
m_ctrlPeopleToTalk.AddString(usrName);
}
str.Empty();
str+=_T("【系統訊息】\"");
str+=usrName;
str+=_T("\"進入到聊天室\r\n");
m_publicChannel+=str;
}
else if (ONLINECLIENT == buf[0]) //當前在線用戶資訊
{
memcpy(usrName,buf+1,len-1); //用戶名
if (LB_ERR == m_ctrlUserOnline.FindString(-1,usrName))
{
m_ctrlUserOnline.AddString(usrName);
}
if (LB_ERR == m_ctrlPeopleToTalk.FindString(-1,usrName))
{
m_ctrlPeopleToTalk.AddString(usrName);
}
}
else if (MESSAGE_ALL == buf[0]) //公共頻道的訊息
{
str.Empty();
memcpy(usrName,buf+1,16);
memcpy(msg,buf+27,len-27);
str+=_T("\"");
str+=usrName;
str+=_T("\"");
//str+=style;
str+=_T("對 \"所有人\"說:");
str+=msg;
m_publicChannel+=str;
m_publicChannel+=_T("\r\n");
}
else if (MESSAGE_ONE == buf[0]) //私聊
{
wchar_t srcUsrName[16]={0},dstUsrName[16]={0};
memcpy(dstUsrName,buf+1,16); //目的用戶名
memcpy(srcUsrName,buf+17,16); //源用戶名
memcpy(msg,buf+43,len-43); //訊息內容
str.Empty();
if (m_usrName == dstUsrName)
{
str+=_T("\"");
str+=srcUsrName;
str+=_T("\"");
str+=_T("對 \"你\"說:");
str+=msg;
m_privateChannel+=str;
m_privateChannel+=_T("\r\n");
}
}
else if (EXIT == buf[0]) //離開的資訊
{
memcpy(usrName,buf+1,len-1); //用戶名
m_ctrlUserOnline.AddString(usrName);
str.Empty();
str+=_T("【系統訊息】\"");
str+=usrName;
str+=_T("\"離開聊天室!\r\n");
m_publicChannel+=str;
int n=m_ctrlUserOnline.FindString(-1,usrName);
if (LB_ERR != n)
{
m_ctrlUserOnline.DeleteString((UINT)n);
m_ctrlUserOnline.DeleteString((UINT)n);
}
n=m_ctrlPeopleToTalk.FindString(-1,usrName);
if (LB_ERR != n)
{
m_ctrlPeopleToTalk.DeleteString((UINT)n);
}
}
else if (SERVERCLOSE == buf[0]) //服務器關閉訊息
{
MessageBox(_T("服務器已關閉!"),_T("提示"),MB_ICONINFORMATION);
m_clientSocket.Close();
GetDlgItem(IDC_BTN_CONNECT)->SetWindowTextW(_T("連接服務器"));
GetDlgItem(IDC_USERNAME)->EnableWindow(TRUE); //斷開后可以改用戶名
m_ctrlUserOnline.ResetContent();
}
else if (REPEATNAME == buf[0])
{
MessageBox(_T("用戶名已存在,請重試!"),_T("提示"),MB_ICONINFORMATION);
m_clientSocket.Close();
GetDlgItem(IDC_BTN_CONNECT)->SetWindowTextW(_T("連接服務器"));
GetDlgItem(IDC_USERNAME)->EnableWindow(TRUE); //斷開后可以改用戶名
m_ctrlUserOnline.ResetContent();
GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE); //可再點擊登錄
GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE); //可以再點擊注冊
}
else if (ERROR == buf[0])
{
MessageBox(_T("用戶名或密碼不正確,請重試!"), _T("提示"), MB_ICONINFORMATION);
GetDlgItem(IDC_USERNAME)->EnableWindow(TRUE); //斷開后可以改用戶名
m_ctrlUserOnline.ResetContent();
GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE); //可再點擊登錄
GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE); //可以再點擊注冊
}
else if (HADDEFINED == buf[0])
{
MessageBox(_T("用戶名已被注冊!"), _T("提示"), MB_ICONINFORMATION);
GetDlgItem(IDC_USERNAME)->EnableWindow(TRUE); //斷開后可以改用戶名
m_ctrlUserOnline.ResetContent();
GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE); //可再點擊登錄
GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE); //可以再點擊注冊
}
else if (REGISTER == buf[0])
{
MessageBox(_T("注冊成功!"), _T("提示"), MB_ICONINFORMATION);
}
UpdateData(FALSE);
}
最后附上完整專案鏈接~
鏈接: https://pan.baidu.com/s/1ausANhj_W_Wc53GAH7IKwQ
提取碼:7vki .
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/282648.html
標籤:其他
