您的位置:首页 > 理论基础 > 计算机网络

基于socket通信的,利用MFC实现TCP通信的C/S架构程序

2017-06-13 14:29 627 查看
1.程序说明

开发环境为VS2012,基于TCP连接的客户端与服务端的通信程序,服务端IP为本地网卡ip地址或127.0.0.1,默认端口为1234(在程序编写过程中连接端口要大于1000,否则容易与计算机中某些程序端口冲突导致无法通信)。

2.socket简介

MFC类库中,几乎封装了Windows Sockets的全部功能,在微软基础类库中有两个基础类:CAsyncSocket类封装了异步套接字(非阻塞模式)的基本功能;CSocket类派生于CAsyncSocket类,具有串行化功能(阻塞模式)。

CAsyncSocket类实现数据传输步骤如下:

(1)调用构造函数创建套接字对象;

(2)如果创建服务器套接字,则调用Bind()函数绑定本地IP和端口,然后调用Listen()函数监听客户端请求。如果请求到来,则调用Accept()函数响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可;

(3)调用Send()等功能函数进行数据传输与处理;

(4)关闭或销毁套接字对象。

CSocket类实现数据传输步骤如下:

(1)创建CSocket类对象;

(2)如果创建服务器端套接字,则调用Bind()函数绑定本地IP和端口,然后调用Listen()函数监听客户端请求。如果请求到来,则调用Accept()函数响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可;

(3)创建与CSocket类对象相关联的CSocketFile类对象;

(4)创建与CSocket类对象相关联的CArchive类对象;

(5)使用CArchive类对象在客户端和服务器之间进行数据传输;

(6)关闭或销毁CSocket类,CSocketFile类,CArchive类三个对象。

上述两种步骤均为使用微软类库进行开发流程,使用MFC通信开发调用MFC封装类库即可。

3.相关函数简介

(1)struct sockaddr_in



short sin_family;//指定地址家族即地址格式,默认为AF_INET,就是TCP/IP协议

unsigned short sin_port;//端口号

struct in_addr sin_addr;//ip地址

char sin_zero[8];//需要指定为0



(2)BOOL Bind(const SOCKADDR* lpSockAddr,int nSockAddrLen);

lpSockAddr指定将要绑定的服务器地址结构

nSockAddrLen地址结构的长度

(3)BOOL Listen(int nConnectionBacklog=5);

最大监听客户端数,默认为5

(4)BOOL Connect(const SOCKADDR* lpSockAddr,int nSockAddrLen);

lpSockAddr 服务器地址结构

nSockAddrLen 地址结构长度

(5)virtual int Send(const void* lpBuf,int nBufLen,int nFlags=0);

lpBuf 待发送数据地址

nBufLen 待发送数据大小

nFlags 发送标志

(6)virtual int Receive(const void* lpBuf,int nBufLen,int nFlags=0);

lpBuf 接收数据地址

nBufLen 接收数据大小

nFlags 标志

4.关键代码
(1)服务器端代码

#define WM_SOCKET_SERVER WM_USER+1000
// CSocket_Connect_ServerDlg 对话框
class CSocket_Connect_ServerDlg : public CDialogEx
{
// 构造
public:
CSocket_Connect_ServerDlg(CWnd* pParent = NULL);	// 标准构造函数

// 对话框数据
enum { IDD = IDD_SOCKET_CONNECT_SERVER_DIALOG };

SOCKET s,s1;//定义套接字句柄
sockaddr_in addr,add1;//定义套接字地址结构变量
int n;

protected:
virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持

// 实现
protected:
HICON m_hIcon;

// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
afx_msg LRESULT OnSocketServer(WPARAM wParam, LPARAM lParam);
public:
afx_msg void OnClickedButton1();
};





BOOL CSocket_Connect_ServerDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();

// 将“关于...”菜单项添加到系统菜单中。

// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
//  执行此操作
SetIcon(m_hIcon, TRUE);			// 设置大图标
SetIcon(m_hIcon, FALSE);		// 设置小图标

// TODO: 在此添加额外的初始化代码
n=0;
addr.sin_family=AF_INET;
addr.sin_port=htons(1234);
addr.sin_addr.S_un.S_addr=INADDR_ANY;
s=::socket(AF_INET,SOCK_STREAM,0);
::bind(s,(sockaddr*)&addr,sizeof(addr));
::listen(s,5);
GetDlgItem(IDC_EDIT1)->EnableWindow(FALSE);
GetDlgItem(IDC_STATIC)->SetWindowTextA("服务器监听已经启动!");

::WSAAsyncSelect(s,this->m_hWnd,WM_SOCKET_SERVER,FD_ACCEPT|FD_READ|FD_CONNECT);//设置异步套接字

return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}


afx_msg LRESULT CSocket_Connect_ServerDlg::OnSocketServer(WPARAM wParam, LPARAM lParam)
{
CString str13;
char cs[100] = {0};
//int err_log=listen(s,10);
//if (err_log != 0)
//{
//	perror("listen");
//	//close(s);
//	exit(-1);
//}
switch (lParam)
{
case FD_ACCEPT:  //连接事件
{
int lenth=sizeof(add1);
s1=::accept(s,(sockaddr*)&add1,&lenth);
n=n+1;
str13.Format("有%d客户已经连接上了\n",n);
str13+=::inet_ntoa(add1.sin_addr);
str13+="\r\n登录\r\n";
GetDlgItem(IDC_EDIT1)->SetWindowTextA(str13);
}
case FD_READ:  //读取事件
{
CString num="";
::recv(s1,cs,100,0);
GetDlgItem(IDC_EDIT1)->GetWindowTextA(num);
num += "\r\n";
num += (LPTSTR)::inet_ntoa(add1.sin_addr);
num += "对您说:";
num += (LPTSTR)cs;
GetDlgItem(IDC_EDIT1)->SetWindowTextA(num);
}
default:
break;
}
return 0;
}

void CSocket_Connect_ServerDlg::OnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString str="";
GetDlgItem(IDC_EDIT2)->GetWindowTextA(str);
if (str=="")
{
MessageBox("消息不能为空!");
}
else
{
if (::send(s1,str.GetBuffer(1),str.GetLength(),0)!= SOCKET_ERROR)
{
GetDlgItem(IDC_EDIT1)->GetWindowTextA(str);
str += "\r\n消息已经发送到客户端!\r\n";
GetDlgItem(IDC_EDIT1)->SetWindowTextA(str);

}
else
{
GetDlgItem(IDC_EDIT1)->SetWindowTextA("消息发送失败!\r\n");
}
}
}



(2)客户端代码

#define WM_SOCKET WM_USER+100

// CSocket_ConnectDlg 对话框
class CSocket_ConnectDlg : public CDialogEx
{
// 构造
public:
CSocket_ConnectDlg(CWnd* pParent = NULL);	// 标准构造函数

// 对话框数据
enum { IDD = IDD_SOCKET_CONNECT_DIALOG };
SOCKET s;
sockaddr_in addr;

protected:
virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持

// 实现
protected:
HICON m_hIcon;

// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg LRESULT OnSocket(WPARAM wParam,LPARAM lParam);
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnClickedButton1();
afx_msg void OnClickedButton2();

};
BOOL CSocket_ConnectDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();

// 将“关于...”菜单项添加到系统菜单中。

// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
//  执行此操作
SetIcon(m_hIcon, TRUE);			// 设置大图标
SetIcon(m_hIcon, FALSE);		// 设置小图标

// TODO: 在此添加额外的初始化代码
this->GetDlgItem(IDC_EDIT3)->EnableWindow(FALSE);
this->GetDlgItem(IDC_EDIT4)->EnableWindow(FALSE);
this->GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);

s=::socket(AF_INET,SOCK_STREAM,0);   //创建套接字
::WSAAsyncSelect(s,this->m_hWnd,WM_SOCKET,FD_READ);//将套接字设置为异步模式

return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CSocket_ConnectDlg::OnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString str,str1;
int port;
GetDlgItem(IDC_EDIT1)->GetWindowTextA(str);
GetDlgItem(IDC_EDIT2)->GetWindowTextA(str1);
if (str==""||str1=="")
{
MessageBox("服务器地址或端口号不能为空");
}
else
{
port=atoi(str1.GetBuffer(1));//将端口字符串转换为数字
addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=inet_addr(str.GetBuffer(1));
//转换服务器IP地址
addr.sin_port=ntohs(port);
GetDlgItem(IDC_EDIT3)->SetWindowTextA("正在连接服务器......\r\n");
//提示用户正在连接服务器
if (::connect(s,(sockaddr *)&addr,sizeof(addr)))
{
GetDlgItem(IDC_EDIT3)->GetWindowTextA(str);
str +="连接服务器成功!\r\n";
GetDlgItem(IDC_EDIT3)->SetWindowTextA(str);
GetDlgItem(IDC_EDIT4)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);
GetDlgItem(IDC_EDIT1)->EnableWindow(FALSE);
GetDlgItem(IDC_EDIT2)->EnableWindow(FALSE);
}
else
{
GetDlgItem(IDC_EDIT3)->GetWindowTextA(str);
str += "连接服务器失败!请重试\r\n";
GetDlgItem(IDC_EDIT3)->SetWindowTextA(str);
}
}
}

void CSocket_ConnectDlg::OnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
CString str,str1;
GetDlgItem(IDC_EDIT4)->GetWindowTextA(str);
if (str=="")
{
GetDlgItem(IDC_EDIT3)->GetWindowTextA(str1);
str1 += "\r\n";
str1 += "消息不能为空\r\n";
GetDlgItem(IDC_EDIT3)->SetWindowTextA(str1);
}
else
{
::send(s,str.GetBuffer(1),str.GetLength(),0);
//发送消息到指定服务器
GetDlgItem(IDC_EDIT3)->GetWindowTextA(str1);
str1 += "\r\n";
str1 += str;
GetDlgItem(IDC_EDIT3)->SetWindowTextA(str1);//将发送内容输出到消息框
}
}

LRESULT CSocket_ConnectDlg::OnSocket(WPARAM wParam,LPARAM lParam)
{
char cs[100]=""; //定义数据缓冲区
if (lParam==FD_READ) //如果是套接字读取时间
{
CString num=""; //定义字符串变量
recv(s,cs,100,NULL); //接受数据
GetDlgItem(IDC_EDIT3)->GetWindowText(num);
num += "\r\n";
num += (LPTSTR)cs; //将接收到的数据转换为字符串
GetDlgItem(IDC_EDIT3)->SetWindowText(num);//设置输出消息内容
}
return 0;
}


5.代码下载地址:http://download.csdn.net/detail/u011028345/9869271





                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  MFC tcp cs socket 通信