做瞹瞹爱视频网站,江西天亿建设有限公司网站,营销型企业网站建设板块设置,网站开发需要多少钱文章目录 前言一、补充内容#xff0c;设置显示框换行二、客户端编程三、封装消息发送函数四、所处的身份状态总结 前言
C打造局域网聊天室第十课#xff1a; 客户端编程及数据发送 一、补充内容#xff0c;设置显示框换行
编辑框的显示内容默认是不会换行的#xff0c;这… 文章目录 前言一、补充内容设置显示框换行二、客户端编程三、封装消息发送函数四、所处的身份状态总结 前言
C打造局域网聊天室第十课 客户端编程及数据发送 一、补充内容设置显示框换行
编辑框的显示内容默认是不会换行的这样会使得显示的客户端发送内容很乱在显示编辑框的属性中找到如下两个内容将默认的FALSE设置为TRUE则显示框的内容会自动换行。
二、客户端编程
回顾客户端编程流程 TCP服务端WSASartup, socket, bind, listen, accept, read, write, closesocket, WSACleanup TCP客户端WSASartup, socket, connect, read, write, closesocket, WSACleanup
与服务器端类似当点击连接服务器按钮后才是客户端的身份。同样添加时间处理程序 在chartroom.h头文件中会自动声明 在chartroom.cpp源文件中会自动出现函数实现框架
同样为了避免阻塞现象的发生利用异步I/O模型和多线程来处理。在chartroom.h头文件中声明客户端连接服务端线程的返回句柄。 并在chartroom.cpp源文件中的构造函数处初始化并创建客户端连接服务端线程。
void CchartroomDlg::OnBnClickedButton1() // 单击连接服务器的MFC消息映射机制
{// TODO: 在此添加控件通知处理程序代码m_hConnectThread CreateThread(NULL, 0, ConnectThreadFunc, this, 0, NULL); // 创建新线程函数客户端连接服务端线程
}与服务端类似创建新的头文件和源文件实现客户端的编程并在相应的源文件中#includeClient.h。 下面在Client.cpp中实现函数ConnectThreadFunc()。首先需要添加一些CchartroomDlg类的成员变量在chartroom.h头文件中声明。 同样在chartroom.cpp源文件中的构造函数处初始化 由于在服务端已经写过SOCKET_Select函数这里直接在Client.h中声明即可 在Client.cpp源文件实现函数DWORD WINAPI ConnectThreadFunc(LPVOID pParam)
DWORD WINAPI ConnectThreadFunc(LPVOID pParam)
{CchartroomDlg* pChartRoom (CchartroomDlg*)pParam; // 将参数强制转化为主对话框类,以便使用主对话框类的一些成员变量ASSERT(pChartRoom ! NULL); // 如果pChartRoom为空指针则程序中断// 新建pChartRoom-m_ConnectSock socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (pChartRoom-m_ListenSock INVALID_SOCKET) // 如果新建失败{AfxMessageBox(_T(新建SOCKET失败!));return FALSE;}// 连接服务端CString strServIp; // 由于使用Unicode编码程序将CString视为宽字节pChartRoom-GetDlgItemText(IDC_IPADDRESS1, strServIp); // 获取界面上的IP地址int iPort pChartRoom-GetDlgItemInt(IDC_EDIT6); // 获取界面上的端口if (iPort 0 || iPort 65535) // 对端口值进行判断{AfxMessageBox(_T(请输入合适的端口1-65535));goto __Error_End;}// 进行本机字节顺序与网络字节顺序的转换char szIpAddr[16] { 0 }; // 定义窄字节数组是为了让inet_addr()函数使用该函数输入只能为窄字节USES_CONVERSION; // 与T2A配套使用strcpy_s(szIpAddr, 16, T2A(strServIp)); //将宽字节转化为窄字节T2A将工程所用的编码格式转化为窄字节。strcpy_s函数做窄字节字符串的拷贝//将端口和IP地址等信息放入sockaddr_in结构中sockaddr_in service;service.sin_family AF_INET; //与新建socket第一个参数的值一样service.sin_addr.s_addr inet_addr(szIpAddr); // 将IP地址传递给sin_addr.s_addr成员service.sin_port htons(iPort); //将端口传递给sin_port成员htons为字节顺序转换函数利用该函数是因为常用的CUP字节顺序与网络字节顺序相反// 例如地址0x12345678,host:0x78 0x56 0x34 0x12; net:0x12 0x34 0x56 0x78。h为host(主机)n为network。htons即为将主机的字节顺序转化为net顺序// connect函数第一个参数为一个socket第二个参数为一个sockaddr*结构(WinSock1版本中等同于Winsock2版本中的sockaddr_in)第三个参数为第二个参数的长度if (connect(pChartRoom-m_ConnectSock, (struct sockaddr*)service, sizeof(struct sockaddr)) SOCKET_ERROR){AfxMessageBox(_T(连接失败请重试));goto __Error_End;}pChartRoom-ShowMsg(_T(系统信息连接服务器成功));while (1){if (SOCKET_Select(pChartRoom-m_ConnectSock, 100, TRUE)) // 异步I/O模型{TCHAR szBuf[MAX_BUF_SIZE] { 0 };int iRet recv(pChartRoom-m_ConnectSock, (char*)szBuf, MAX_BUF_SIZE, 0);if (iRet 0){//正确,接收数据成功pChartRoom-ShowMsg(szBuf); //利用在chartroom.cpp中实现的ShowMsg方法将信息显示}else // 接收数据失败有错误或者服务器端关闭了{// 关闭socketpChartRoom-ShowMsg(_T(聊天室服务器已停止请重新进行连接)); //利用在chartroom.cpp中实现的ShowMsg方法将信息显示break;// 跳出循环客户端已下线退出线程}}Sleep(500);}__Error_End:closesocket(pChartRoom-m_ConnectSock);return TRUE;
}三、封装消息发送函数
发送消息通过点击发送消息按键实现 同样添加该控件的单击MFC消息映射机制这里不再赘述
为了实现消息的发送功能封装一个CchartroomDlg类的成员函数SendClientMsg()具体实现如下注意同样需要先在chartroomDlg.h头文件中声明然后再在源文件中实现
// 说明某一个客户端发送消息将信息发送给队列中除了发消息客户端外的所有客户端第一个参数为发送的内容第二个参数为发消息的客户端
void CchartroomDlg::SendClientMsg(CString strMsg, CClientitem *pNotSend) // 实现发送消息函数.发消息给客户端
{TCHAR szBuf[MAX_BUF_SIZE] { 0 };_tcscpy_s(szBuf, MAX_BUF_SIZE, strMsg);for (INT_PTR idx 0; idx m_ClientArray.GetCount(); idx){if (!pNotSend || pNotSend-m_Socket ! m_ClientArray.GetAt(idx).m_Socket || pNotSend-hThread ! m_ClientArray.GetAt(idx).hThread ||pNotSend-m_surlp ! m_ClientArray.GetAt(idx).m_surlp){// 第一个参数为要发送给哪个客户端的socket第二个参数为发送内容第三个参数为发送内容的长度*每个字符占几个字节send(m_ClientArray.GetAt(idx).m_Socket, (char*)szBuf, _tcslen(szBuf)*sizeof(TCHAR), 0);}}
}四、所处的身份状态
服务端和客户端对应的功能是不一样的因此我们需要一个功能来区分客户端和服务端以便采取正确的处理过程
注意该程序有三种状态刚启动时既不是客户端也不是服务端客户端服务端
在chartroomDlg.h头文件中声明一个整形变量来区分三种状态。 同样在构造函数中进行初始化 监听成功后证明此时该程序为服务端身份 调用connect连接成功后证明该程序此时为客户端身份 之后完成点击发送消息的MFC消息映射机制函数实现
void CchartroomDlg::OnBnClickedButton5() // 单击发送消息的MFC消息映射机制
{// TODO: 在此添加控件通知处理程序代码CString strMsg;GetDlgItemText(IDC_EDIT4, strMsg); // 获取输入信息编辑框内的输入信息if (m_bIsServer TRUE) // 若本程序状态为服务器{strMsg _T(服务器) strMsg;ShowMsg(strMsg);SendClientMsg(strMsg, NULL); // 将信息发送给所有队列中的客户端}else if (m_bIsServer FALSE) // 若本程序状态为客户端{CString strTmp _T(本地客户端 ) strMsg;ShowMsg(strTmp);int iSend send(m_ConnectSock, (char*)strMsg.GetBuffer(), strMsg.GetLength() * sizeof(TCHAR), 0);strMsg.ReleaseBuffer();}SetDlgItemText(IDC_EDIT4, _T()); // 将信息发送给服务端后清空发送内容编辑框}上述代码实现了服务端将信息发送给所有客户端以及客户端将信息发送给服务端的功能。此外当一个客户端将信息发送给服务端后服务端还需要将该信息转发给其他所有客户端。这部分功能需要在服务端程序Server.cpp中添加。 此外还需要实现一个功能只有当聊天信息输入框里面有信息时发送信息按键才是可点击状态否则为不可点击。具体操作按图即可。 EN_CHANGE为当编辑框中内容发生改变才触发相应函数
void CchartroomDlg::OnEnChangeEdit4() // // 实现当输入聊天信息编辑框内容发生变化时调用的函数
{// TODO: 如果该控件是 RICHEDIT 控件它将不// 发送此通知除非重写 CDialogEx::OnInitDialog()// 函数并调用 CRichEditCtrl().SetEventMask()// 同时将 ENM_CHANGE 标志“或”运算到掩码中。// TODO: 在此添加控件通知处理程序代码CString strMsg;GetDlgItemText(IDC_EDIT4, strMsg); // 获取输入聊天信息编辑框内容if (strMsg.IsEmpty()) // 如果没有聊天信息{EnableWindow(IDC_BUTTON5, 0); // 禁用发送信息按键}else // // 如果有聊天信息{EnableWindow(IDC_BUTTON5, 1); // 启用发送信息按键}
}
同时在初始化时要设置发送信息按钮为不可用 总结
C打造局域网聊天室第十课 客户端编程及数据发送