# 基于TCP SOCKET实现的局域网聊天室
# 一、需求分析
局域网聊天室是单服务器端,多个客户端的聊天程序,多个客户端可以通过配置IP和端口号来连接服务器。
利用socket编程,服务器端建立线程套接字,通过绑定监听。
等待客户端的请求,一旦有客户端请求连接则建立连接,双方进行通信,而客户端则主动去连接服务器,基本步骤相同。
# 二、详细设计
## 2.1 主要流程图

### 服务器端
- 初始化建立线程
- 开始运行主线程
- 创建套接字
- 绑定套接字
- 监听端口
### 客户端
- 初始化建立线程
- 开始运行主线程
- 创建套接字
- 绑定套接字
- 连接请求
- 连接成功开始通信
# 三、主要数据结构
```c++
class CClientItem {
public:
CString m_strIp;
SOCKET m_Socket;
HANDLE hThread;
CChatRoomDlg *m_pMainWnd;
CClientItem(){
m_pMainWnd = NULL;
m_Socket = INVALID_SOCKET;
hThread = NULL;
}
};
```
# 四、详细设计过程
主要线程函数处理过程如下:
- 建立套接字
- 绑定端口
- 设计套接口模式
- 循环监听客户端是否在运行
- 进行连接
- 进行通信
# 五、服务器端代码
```c++
# include "stdafx.h"
# include "ChatRoomDlg.h"
BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut, BOOL bRead)
{
fd_set fdset;
timeval tv;
FD_ZERO(&fdset);
FD_SET(hSocket, &fdset);
nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut;
tv.tv_sec = 0;
tv.tv_usec = nTimeOut;
int iRet = 0;
if ( bRead ) {
iRet = select(0, &fdset, NULL , NULL, &tv);
}else{
iRet = select(0, NULL , &fdset, NULL, &tv);
}
if(iRet <= 0) {
return FALSE;
} else if (FD_ISSET(hSocket, &fdset)){
return TRUE;
}
return FALSE;
}
DWORD WINAPI ListenThreadFunc(LPVOID pParam)
{
CChatRoomDlg *pChatRoom = (CChatRoomDlg *)pParam;
ASSERT(pChatRoom != NULL);
pChatRoom->m_ListenSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);
if ( pChatRoom->m_ListenSock == INVALID_SOCKET ) {
AfxMessageBox(_T("新建Socket失败!"));
return FALSE;
}
int iPort = pChatRoom->GetDlgItemInt(IDC_LISTEN_PORT);
if ( iPort <= 0 || iPort > 65535 ) {
AfxMessageBox(_T("请输入合适的端口:- 65535"));
goto __Error_End;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = INADDR_ANY;
service.sin_port = htons(iPort);
if ( bind(pChatRoom->m_ListenSock, (sockaddr*)&service, sizeof(sockaddr_in)) == SOCKET_ERROR ) {
AfxMessageBox(_T("绑定端口失败!"));
goto __Error_End;
}
if( listen(pChatRoom->m_ListenSock, 5) == SOCKET_ERROR ) {
AfxMessageBox(_T("监听失败!"));
goto __Error_End;
}
pChatRoom->ShowMsg(_T("系统信息:启动服务器成功!"));
pChatRoom->m_bIsServer = TRUE;
pChatRoom->EnableWindow(IDC_START_SERVER, FALSE);
pChatRoom->EnableWindow(IDC_STOP_SERVER);
while( TRUE && !(pChatRoom->bShutDown)) {
if ( SOCKET_Select(pChatRoom->m_ListenSock, 100, TRUE) ) {
sockaddr_in clientAddr;
int iLen = sizeof(sockaddr_in);
SOCKET accSock = accept(pChatRoom->m_ListenSock, (struct sockaddr *)&clientAddr , &iLen);
if (accSock == INVALID_SOCKET) {
continue;
}
CClientItem tItem;
tItem.m_Socket = accSock;
tItem.m_pMainWnd = pChatRoom;
tItem.m_strIp = inet_ntoa(clientAddr.sin_addr);
INT_PTR idx = pChatRoom->m_ClientArray.Add(tItem);
tItem.hThread = CreateThread(NULL, 0, ClientThreadProc, &(pChatRoom->m_ClientArray.GetAt(idx)), CREATE_SUSPENDED, NULL);
pChatRoom->m_ClientArray.GetAt(idx).hThread = tItem.hThread;
ResumeThread(tItem.hThread);
CString strMsg;
strMsg = _T("客户端:") + tItem.m_strIp + _T(" 进入聊天室!");
pChatRoom->ShowMsg(strMsg);
pChatRoom->SendClientsMsg(strMsg, &tItem);
Sleep(100);
}
}
__Error_End:
closesocket(pChatRoom->m_ListenSock);
return TRUE;
}
DWORD WINAPI ClientThreadProc(LPVOID lpParameter)
{
CString strMsg;
CClientItem m_ClientItem = *(CClientItem *)lpParameter;
while( TRUE && !(m_ClientItem.m_pMainWnd->bShutDown)) {
if ( SOCKET_Select(m_ClientItem.m_Socket, 100, TRUE) ) {
TCHAR szBuf[MAX_BUF_SIZE] = {0};
int iRet = recv(m_ClientItem.m_Socket, (char *)szBuf, MAX_BUF_SIZE, 0);
if ( iRet > 0 ) {
//right;
strMsg = szBuf;
strMsg = _T("客户端:") + m_ClientItem.m_strIp + _T(">") + strMsg;
m_ClientItem.m_pMainWnd->ShowMsg(strMsg);
m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem);
}else{
//close socket;
strMsg = _T("客户端:") + m_ClientItem.m_strIp + _T(" 离开了聊天室!");
m_ClientItem.m_pMainWnd->ShowMsg(strMsg);
m_ClientItem.m_pMainWnd->RemoveClientFromArray(m_ClientItem);
m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem);
break;
}
}
Sleep(500);
}
return TRUE;
}
```
# 六、客户端代码
```c++
# include "stdafx.h"
# include "ChatRoomDlg.h"
# include "Inc.h"
DWORD WINAPI ConnectThreadFunc(LPVOID pParam)
{
CChatRoomDlg *pChatRoom = (CChatRoomDlg *)pParam;
ASSERT(pChatRoom != NULL);
pChatRoom->m_ConnectSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);
if ( pChatRoom->m_ConnectSock == INVALID_SOCKET ) {
AfxMessageBox(_T("新建Socket失败!"));
return FALSE;
}
CString strServIp;
pChatRoom->GetDlgItemText(IDC_IP_ADDR, strServIp);
int iPort = pChatRoom->GetDlgItemInt(IDC_CONNECT_PORT);
if ( iPort <= 0 || iPort > 65535 ) {
AfxMessageBox(_T("请输入合适的端口:- 65535"));
goto __Error_End;
}
char szIpAddr[16] = {0};
USES_CONVERSION;
strcpy_s(szIpAddr, 16, T2A(strServIp));
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(iPort);
server.sin_addr.s_addr = inet_addr(szIpAddr);
if ( connect(pChatRoom->m_ConnectSock, (struct sockaddr *)&server, sizeof(struct sockaddr)) == SOCKET_ERROR ) {
AfxMessageBox(_T("连接失败,请重试!"));
goto __Error_End;
}
pChatRoom->m_bIsServer = FALSE;
pChatRoom->ShowMsg(_T("系统信息:连接服务器成功!"));
while( TRUE && !(pChatRoom->bShutDown)) {
if ( SOCKET_Select(pChatRoom->m_ConnectSock) ) {
TCHAR szBuf[MAX_BUF_SIZE] = {0};
int iRet = recv(pChatRoom->m_ConnectSock, (char *)szBuf, MAX_BUF_SIZE, 0);
if ( iRet > 0 ) {
//right;
pChatRoom->ShowMsg(szBuf);
}else{
//close socket;
pChatRoom->ShowMsg(_T("聊天室服务器已停止,请重新进行连接!"));
break;
}
}
Sleep(500);
}
__Error_End:
closesocket(pChatRoom->m_ConnectSock);
return TRUE;
}
```
#### 主线程代码
```c++
// ChatRoomDlg.cpp : implementation file
//
# include "stdafx.h"
# include "ChatRoom.h"
# include "ChatRoomDlg.h"
# include "Inc.h"
# ifdef _DEBUG
# define new DEBUG_NEW
# endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CChatRoomDlg dialog
CChatRoomDlg::CChatRoomDlg(CWnd* pParent /*=NULL*/)
: CDialog(CChatRoomDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
bShowAll = FALSE;
m_ListenSock = INVALID_SOCKET;
m_ConnectSock = INVALID_SOCKET;
m_hListenThread = NULL;
m_hConnectThred = NULL;
m_bIsServer = -1;
bShutDown = FALSE;
}
void CChatRoomDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STOP_CLIENT, m_StopClientBtn);
DDX_Control(pDX, IDC_SHOW_MSG, m_MsgEdit);
}
BEGIN_MESSAGE_MAP(CChatRoomDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_NETSET, &CChatRoomDlg::OnBnClickedNetset)
ON_BN_CLICKED(IDC_

工具盒子
- 粉丝: 85
最新资源
- GOAT(山羊)是基于 LlaMa 进行 SFT 的中英文大语言模型
- 借助 ChatGPT 大语言模型通过聊天机器人自动搭建 vulhub 漏洞靶机环境
- 一个 JavaScript 的简单范例程序-创建一个简单的待办事项列表(Todo List)
- 第二届广州・琶洲算法大赛智能交通 CV 模型赛题第四名方案
- 第二届广州・琶洲算法大赛智能交通 CV 模型赛题第 4 名解决方案
- 基于ChatGPT大语言模型,通过聊天机器人自动创建vulhub的漏洞靶机环境
- Python 的排序算法范例程序-实现快速排序算法
- 从零开始编写大语言模型相关所有代码用于学习
- kindeditor多图上传H5版 ,替换到原来的plugins\multiimage目录下就可用,无须修改原来的调用代码,要记得刷新缓存
- CID解码最新300-CD软件
- CID解码最新300-CD软件
- 结合大模型强大的自然语言处理能力,自动化地生成全面、高质量的测试用例
- CID解码最新300-CD软件
- MATLAB实现NMEA 0183数据可视化工具
- MATLAB实现NMEA 0183数据可视化工具
- aspmkr7_1.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈


