利用CSocket实现进程间的通信

用CSocket类编写网络程序,既可以使用如CAsyncSocket类网络程序一样的Send和Receive函数来收发信息,也可以与CSocketFile类和CArchive类一起来管理对数据的发送和接收。
首先,了解CSocket连接的理论步骤:
1、在服务器端声明一个用于监听客户端的CSocket对象,同时对应某个端口设定为监听状态;
2、在客户端声明CSocket对象,并通过IP和端口向服务器请求连接;
3、服务器端监听到请求指令后,新建一个CSocket对象,用于与客户端绑定,传送和接收数据,服务器用于监听的CSocket继续监听下一个客户端的请求。
 
一、服务器端的建立
1.界面

控件属性

控件类型 控件ID Caption属性 控件变量 变量类型
列表框 IDC_LIST_SHOW m_ListShow CListBox
编辑框 IDC_EDIT_WRITE m_EditMsg CEdit
按钮 IDC_BTN_SEND 发送 m_BtnSend CButton

2.定义CSocket类的派生类CServSocket和CRecvSocket。

从CSocket编程模型知道,服务器端需要两种套接字,一个用来侦听连接请求,一个用来与请求连接的套接字建立连接。因此,为程序添加两个CSocket派生类:CServSocket和CRecvSocket,它们与对话框类密切配合,共同完成程序所要求实现的功能。

具体做法是:选择"插入->类->MFC类",添加一个基于CSocket的子类CServSocket和CRecvSocket,点击完成。其中,CServSocket类将用于监听客户端的连接请求,CRecvSocket用来与请求连接的套接字建立连接。

3.建立套接字与对话框类的关联

在程序中,对话框类要用到套接字类,而套接字类在响应某些消息,如在函数OnAccept、OnReceive中进行处理时,也要改变对话框的某些控件状态,以反映给用户这些事情的发生。这里存在着两个类相互使用的情况,把套接字类对象定义成对话框类的成员变量,同时在套接字类中也把对话框类定义为成员变量。

具体做法如下:

首先,在DlgTestDlg.h中加入套接字类头文件的声明,语句#pragma once的后面加入如下语句:

#include "ServSocket.h"

#include "RecvSocket.h"

然后在该文件中为CDlgTestDlg类增加两个公有成员变量,语句如下:

CServSocket *m_pServSock;

CRecvSocket *m_pRecvSock;

这样在对话框类中就可以使用套接字类了。

继续在套接字类中加入对话框类信息。

首先,在ServSocket.h文件的开头,语句#pragma once的后面加入如下语句:

class CDlgTestDlg;

然后,在该文件中为CServSocket类添加一个公有成员变量和一个构造函数:

CServSocket(CDlgTestDlg *pDlg);

CDlgTestDlg *m_pDlg;

接着在ServSocket.cpp文件中添加新的构造函数的实现,并添加一条关于DlgTestDlg.h文件的预编译声明,代码如下:

#include "DlgTestDlg.h"

CServSocket::CServSocket(CDlgTestDlg *pDlg)
{
m_pDlg = pDlg;
}

这样,在套接字类中也可以通过成员变量使用对话框了。

使用同样的方法对CRecvSocket类进行设置,使其也可以通过成员变量使用对话框。

4.为套接字添加串行化读写信息的功能。
在服务器端的两个套接字中,只有CRecvSocket套接字是真正与客户端套接字建立连接,发送与接收数据的,因此,我们只为该类添加串行化读写信息功能。在RecvSocket.h文件中为类CRecvSocket添加三个公有成员变量

CSocketFile *m_pFile;
CArchive *m_pArIn;
CArchive *m_pArOut;

5.在对话框中初始化套接字并侦听连接请求。

在OnInitDialog函数中添加如下代码:

// TODO: Add extra initialization here

if(m_pServSock = new CServSocket(this))
{
if(m_pServSock->Create (9547))
{
m_ListShow.InsertString(m_ListShow.GetCount(), "等待连接......");
m_pServSock->Listen ();
}
else
{
m_ListShow.InsertString(m_ListShow.GetCount(), "初始化失败,请重新启动程序!");
delete m_pServSock;
}
}
else
{
m_ListShow.InsertString(m_ListShow.GetCount(), "初始化失败,请重新启动程序!");
}

上述代码主要是创建并初始化ServSock套接字,并开始侦听连接请求。

6.接受连接请求。

由于是CServSocket类的ServSock对象在侦听连接请求,因此由该类来接受连接请求。

首先,在ServSocket.h文件中加入如下语句:

#iinclude “RecvSocket.h”

然后,重载该类的OnAccept函数,在该函数中添加如下代码:

CRecvSocket *tempSock;

if(tempSock = new CRecvSocket(this->m_pDlg))
{
if(Accept(*tempSock))
{
tempSock->m_pFile = new CSocketFile(tempSock);
tempSock->m_pArIn = new CArchive(tempSock->m_pFile ,CArchive::load );
tempSock->m_pArOut = new CArchive(tempSock->m_pFile ,CArchive::store );
m_pDlg->m_pRecvSock = tempSock;
tempSock=NULL;
m_pDlg->m_ListShow.InsertString(m_pDlg->m_ListShow.GetCount(), "连接成功,可以开始传递消息");
m_pDlg->m_BtnSend.EnableWindow (true);
}
else
{
m_pDlg->m_ListShow.InsertString(m_pDlg->m_ListShow.GetCount(), "客户端当前的连接尝试失败");
delete tempSock;
}
}
else
{
m_pDlg->m_ListShow.InsertString(m_pDlg->m_ListShow.GetCount(), "连接套接字初始化失败");
}

上述代码首先调用Accept函数接受连接请求,然后为该连接创建一个CRecvSocket类型的套接字,并为该套接字关联CArchive对象,使其能实现串行化传输信息的功能。最后把关联好的套接字传回给对话框对象供其使用。这样,对话框对象的成员变量RecvSock套接字便与客户端套接字之间建立了一条信息通道,信息将在两个套接字之间传递。

7.接收信息。

连接建立成功后,当有信息到达服务器端时,就会引发RecvSock套接字对象的OnReceive函数,因此需要重载CRecvSocket类的OnReceive函数。添加代码如下:

CString str;
(*m_pArIn)>>str;
str = "对方 : " + str;
m_pDlg->m_ListShow.InsertString(m_pDlg->m_ListShow.GetCount(), str);
m_pDlg->m_ListShow.SetCurSel(m_pDlg->m_ListShow.GetCount() - 1);

8.发送信息。

为对话框“发送”按钮添加事件处理函数OnBtnSend(),代码如下:

void CDlgTestDlg::OnBtnSend()
{
// TODO: Add your control notification handler code here
CString strText, str;

m_EditMsg.GetWindowText (strText);
if(strText.GetLength () == 0)
AfxMessageBox("空信息,所以不发出");
else
{
*(m_pRecvSock->m_pArOut)<<strText;
m_pRecvSock->m_pArOut->Flush();
strText = "你 : " + strText;
m_ListShow.InsertString(m_ListShow.GetCount(), strText);
m_ListShow.SetCurSel(m_ListShow.GetCount()-1);
m_EditMsg.SetWindowText("");
}
}

二、客户端的建立

1.界面与控件属性可参考服务器端。

2.创建套接字类(从CSocket类派生)。客户端只需要一个套接字,命名为CClientSocket。

3.建立对话框类与套接字类的关联。

首先,在DlgTestClientDlg.h文件的开头,语句#pragma once后面加入如下语句:

#include “ClientSocket.h”

然后,在该文件中为CDlgTestClientDlg类添加一个公有成员变量,语句如下:

CClientSocket *m_pClientSock;

接着,在ClientSocket.h文件的开头,语句#pragma once后面加入如下语句:

class CDlgTestClientDlg;

然后,在该文件中为CClientSocket类添加一公有成员变量和一个构造函数,语句如下:

CClientSocket(CDlgTestClientDlg *pDlg);

CDlgTestClientDlg *m_pDlg;

接着,在ClientSocket.cpp文件中添加新的构造函数的实现代码,并添加一条关于DlgTestClientDlg.h文件的预编译声明,代码如下:

#include "DlgTestClientDlg.h"

CClientSocket::CClientSocket(CDlgTestClientDlg *pDlg)
{
m_pDlg = pDlg;
}

这样,便完成了对话框和套接字之间的连接了。

4.为套接字添加串行化读写信息的功能。

在ClientSocket.h文件中,为类CClientSocket添加三个公有成员变量,代码如下:

CSocketFile *m_pFile;
CArchive *m_pArIn;
CArchive *m_pArOut;

5.在对话框中初始化套接字并建立连接。

在对话框类的OnInitDialog函数中添加如下代码:

// TODO: Add extra initialization here
m_ListShow.InsertString(m_ListShow.GetCount(), "正在连接......");
if(m_pClientSock = new CClientSocket(this))
{
if(m_pClientSock->Create())
{
if(m_pClientSock->Connect ("localhost",9547))
{
m_pClientSock->m_pFile = new CSocketFile(m_pClientSock);
m_pClientSock->m_pArIn = new CArchive(m_pClientSock->m_pFile ,CArchive::load );
m_pClientSock->m_pArOut = new CArchive(m_pClientSock->m_pFile,CArchive::store );
m_ListShow.InsertString(m_ListShow.GetCount(), "连接成功,可以开始传递消息");
m_BtnSend.EnableWindow (true);
}
else
{
m_ListShow.InsertString(m_ListShow.GetCount(), "连接不成功");
delete m_pClientSock;
}
}
else
{
m_ListShow.InsertString(m_ListShow.GetCount(), "初始化失败,请重新启动程序");
delete m_pClientSock;
}
}
else
{
m_ListShow.InsertString(m_ListShow.GetCount(), "初始化失败,请重新启动程序");
}

6.接收消息。

消息到来时,会引发套接字的OnReceive消息,因此要重载CClientSocket类的OnReceive函数,在其中添加代码如下:

CString str;

*m_pArIn>>str;  // 接收信息 Receive(str, str.GetLength()+1, 0);
str = "对方 : " + str;
m_pDlg->m_ListShow .InsertString(m_pDlg->m_ListShow.GetCount(), str);
m_pDlg->m_ListShow .SetCurSel(m_pDlg->m_ListShow .GetCount()-1);

7.发送信息。

为对话框“发送”按钮添加事件处理函数OnBtnSend(),代码如下:

void CDlgTestClientDlg::OnBtnSend()
{
// TODO: Add your control notification handler code here
CString str;

m_EditMsg.GetWindowText(str);
if(str.GetLength () == 0)
AfxMessageBox("空信息,所以不发出");
else
{
*(m_pClientSock->m_pArOut )<<str;
m_pClientSock->m_pArOut ->Flush();
str = "你 : " + str;
m_ListShow.InsertString(m_ListShow.GetCount(), str);
m_ListShow.SetCurSel(m_ListShow.GetCount()-1);
m_EditMsg.SetWindowText("");
}
}

原文地址:https://www.cnblogs.com/zerotoinfinity/p/6357759.html