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

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



你可能感兴趣的:(C++)