上一篇
1、制作一个服务端和客户端应用程序
2、实现从客户端发送信息到服务端,并在服务端列表框(List Control)控件上显示
今天的代码文件Github的链接我也会放在博客的底部哟
日常偷个懒,使用上一篇博客的项目作为服务端,并进行一些修改。
再新建一个基于对话框的MFC项目作为客户端
以下表格是服务端和客户端交互的步骤
服务端 | 客户端 |
---|---|
AfxSocketIint() | AfxSocketIint() |
CSocket sockSrvr; | CSocket sockClient; |
sockSrvr.Create(nPort);1,2 | sockClient.Create( );2 |
sockSrvr.Listen( ); | |
sockClient.Connect(strAddr, nPort);3,4 | |
CSocket sockRecv;sockSrvr.Accept( sockRecv ); 5 |
CAsyncSocket类构造函数:
CAsyncSocket 构造CAsyncSocket对象
Create 创建套接字
CAsyncSocket类成员函数:
Attach 对CAsyncSocket对象附加套接字句柄
Detach 从CAsyncSocket对象除去套接字句柄
FromHandle 返回CAsyncSocket对象的指针,给出套接字句柄
GetLastError 获得上一次运行失败的状态
GetPeerName 获得与套接字连接的对等套接字的地址
GetSockName 获得套接字的本地名
GetSockOpt 获得套接字选项
SetSockOpt 设置套接字选项
Accept 接受套接字上的连接
AsyncSelect 请求对于套接字的事件通知
Bind 与套接字有关的本地地址
Close 关闭套接字
Connect 对对等套接字建立连接
IOCtl 控制套接字模式
Listen 建立套接字,侦听即将到来的连接请求
Receive 从套接字接收数据
ReceiveFrom 恢复数据报并且存储资源地址
Send 给连接套接字发送数据
SendTo 给特定目的地发送数据
ShutDown 使套接字上的Send和/或Receive调用无效
CAsyncSocket类覆盖通知函数 :
OnAccept 通知侦听套接字,它可以通过调用Accept,接受挂起连接请求
OnClose 通知套接字,关闭对它的套接字连接
OnConnect 通知连接套接字,连接尝试已经完成,无论成功或失败
OnOutOfBandData 通知接收套接字,在套接字上有带外数据读入,通常是忙消息
OnReceive 通知侦听套接字,通过调用Receive恢复数据
OnSend 通知套接字,通过调用Send,它可以发送数据
TcpSever.h的代码:
其中typedef struct MyMsg 为客户端传到服务端的消息的结构。
#pragma once
#include
#include "pch.h"
#include "resource.h"
typedef struct MyMsg
{
char name[20]="";
char position[10]="";
char Style[128]="";
};
class TcpSever:public CAsyncSocket
{
public:
CListCtrl *mList1=new CListCtrl();
void UpdataListCtrl(MyMsg* Msg);
void setListCtrl(CListCtrl* mList);
TcpSever();
virtual void OnAccept(int nErrorCode);
virtual void OnReceive(int nErrorCode);
//
};
TcpSever.cpp的代码:
#include "pch.h"
#include "TcpSever.h"
void TcpSever::UpdataListCtrl(MyMsg* Msg)
{
int count = -1;
/*转换格数*/
CString name;
CString position;
CString Style;
name.Format(_T("%s"), Msg->name);
position.Format(_T("%s"), Msg->position);
Style.Format(_T("%s"), Msg->Style);
//把数据写到List 控件
count = mList1->GetItemCount();
mList1->InsertItem(count, name);
mList1->SetItemText(count, 1, position);
mList1->SetItemText(count, 2, Style);
}
void TcpSever::setListCtrl(CListCtrl* mList)
{
this->mList1 = mList;
}
TcpSever::TcpSever()
{
}
/*OnAccept(int nErrorCode)由框架调用,通知监听套接字现在可以调用Accept成员函数
来接收悬挂的(pending)连接请求。
*/
void TcpSever::OnAccept(int nErrorCode)
{
//newTcp = new TcpSever();
TcpSever* newTcp = new TcpSever();
if (this->Accept(*newTcp)) {
newTcp->setListCtrl(this->mList1);//将this->mList1传给newTcp
CString str;
UINT port;
newTcp->GetPeerName(str, port);//获得与套接字连接的对等套接字的地址
newTcp->GetSockName(str, port);//获得套接字的本地名
}
CAsyncSocket::OnAccept(nErrorCode);
}
/*本函数由框架调用,通知套接字缓冲中有数据,
可以调用Receive成员函数取出。*/
void TcpSever::OnReceive(int nErrorCode)
{
MyMsg Msg = {
"","",""};
this->Receive(&Msg,sizeof(Msg));
UpdataListCtrl(&Msg);
//this->Close();
}
在CMFCDialogDlg类中引用TcpSever.h
CMFCDialogDlg类中添加公开的TcpSever server成员变量并实例化*
TcpSever* server = new TcpSever();
BOOL CMFCDialogDlg::OnInitDialog()
{
。。。。。。//代表省略其他代码
// TODO: 在此添加额外的初始化代码
}
在上面OnInitDialog()(初始化函数)中添加
AfxSocketInit();//初始套接字//这是自带的不需要定义,直接调用
void CMFCDialogDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
server = new TcpSever();
if (server->Create(8967, 1, 63L, NULL)) {
//端口为8967
server->setListCtrl(&(this->mList1));//传入List控件的指针
server->Listen();
}
}
BOOL CAsyncSocket::Create(
UINT nSocketPort = 0,
int nSocketType = SOCK_STREAM,
long lEvent = FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,
LPCTSTR lpszSocketAddress = NULL
);
nSocketPort 套接字使用的已知端口。如果想让Windows套接字选择端口,则此值为0。
nSocketType SOCK_STREAM或SOCK_DGRAM。
lEvent 位掩码,它指定了应用感兴趣的网络事件的组合。 · FD_READ 想要接收用于读取的读通知。
· FD_WRITE 当数据读取有效时,想要接收通知。
· FD_OOB 想要恢复带外的数据到达的通知。
· FD_ACCEPT 想要接收将要连接的通知。
· FD_CONNECT 想要接收连接结果的通知。
· FD_CLOSE 当套接字被测试关闭时,想要接收通知。
lpszSockAddress 指向字符串的指针。此字符串包含了被连接的套接字的网络地址,例如以句点分隔的数字:“128.56.22.8”,为NULL,则不指定网络地址。
其实可以不需要建这个类,直接使用CAsyncSocket类
TcpClient.h的代码:
#pragma once
#include
class TcpClient :
public CAsyncSocket
{
public:
virtual void OnConnect(int nErrorCode);
virtual void OnReceive(int nErrorCode);
};
TcpClient.cpp的代码:
#include "pch.h"
#include "TcpClient.h"
void TcpClient::OnConnect(int nErrorCode)
{
CAsyncSocket::OnConnect(nErrorCode);
}
void TcpClient::OnReceive(int nErrorCode)
{
CAsyncSocket::OnReceive(nErrorCode);
}
在CClienttcpDlg类中引用TcpClient.h
*CClienttcpDlg类中添加公开的TcpClient Client成员变量并实例化
TcpClient *Client=new TcpClient();
BOOL CMFCDialogDlg::OnInitDialog()
{
。。。。。。//代表省略其他代码
// TODO: 在此添加额外的初始化代码
}
在上面OnInitDialog()(初始化函数)中添加
AfxSocketInit();//初始套接字//这是自带的不需要定义,直接调用
Client->Create();//创建套接字
控件与控件变量绑定(具体操作看我这个系列之前的博客)
// 名字
CEdit NameEdit;
// 职务
CComboBox PositionBox;
// 自我介绍
CEdit StyleEdit;
void CClienttcpDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString test=CString("");
if (Client->Connect(CString("192.168.3.15"), 8967)) {
}
else
{
//GetLastError() 获取错误代码
if (Client->GetLastError() == WSAEWOULDBLOCK) {
/*如果确定了WSAEWOULDBLOCK的错误代码,
并且应用正在使用函数覆盖的调用,
则当连接操作完成时,应用将接收OnConnect消息。*/
CString name;
CString position;
CString Style;
NameEdit.GetWindowTextW(name);
StyleEdit.GetWindowTextW(Style);
PositionBox.GetLBText(PositionBox.GetCurSel(), position);
MyMsg msg = {
"","",""};
memcpy(msg.name, name, name.GetLength()*2);
memcpy(msg.position, position, position.GetLength() * 2);
memcpy(msg.Style, Style, Style.GetLength() * 2);
Client->Send(&msg, sizeof(msg));
return;
}
}
}
感谢大家的翻阅,如有错误欢迎指正
本篇博客的项目源代码文件的GitHub地址