1.程序整体框架:主程序监听一端口,等待客户接入;同时构造一个线程类,准备接管会话。当一个Socket会话产生后,将这个会话交给线程处理,然后主程序继续监听。2.客户端(Client)
客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个端口。
3.服务器端(Server)
服务器端,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
4.用户图形界面
用户图形界面方便程序与用户的交互,多个用户参加,完成会话功能,具体的设计要方便用户的使用,直观清晰,简洁明了,友好美观。
四、实验内容
一个最简单的点对点聊天程序
客户机/服务器模式是socket点对点网络程序典型的模式,以下这个实验就是实现一个简单的点对点通信的聊天程序。它用到的方法也是面向连接TCP连接的套接字MFC典型方式。其工作过程是:服务器首先启动,创建套接字后等待客户的连接;客户启动以后,创建套接字,然后和服务器建立连接;连接建立后,客户机和服务器可以通过建立的套接字连接进行信息通信。
先建立一个MFC,选dialogBased,设置工程名。
出现Dialog以后,编辑界面,使其如图所示并且对控件点击右键,选择属性选项,把每个控件的ID改掉(控件ID就是每个控件的名字,要改成有意义的,以便将来管理)。
各个控件的ID如下,并且在对话框画面点击右键,选Class Wizard选项,用这个工具对控件添加变量,使其图所示:
重新再VC中建立客户机工程,新建工程名
首先,在BOOL CLx1Dlg::OnInitDialog()和BOOL CLx2Dlg::OnInitDialog()末尾添加语句,使其如下所示:
m_send.EnableWindow(FALSE); //使发送按钮变灰
return TRUE; // return TRUE unless you set the focus to a control
为了在自己程序里面更自由地处理CSocket得到的消息,必须新建CSocket的派生类:
在第一个工程里工作区类视图里点右键,添加新类:CServer,父类为CSocket NewClass对话框如图所示:
在第一个工程里的Dlg.h添加 头文件#include \"Server.h\"
private变量:CServer m_server; CServer m_recv
在对话框的图象上双击“侦听”按钮,在里面添加如下代码,使其如下所示:
void CLx2Dlg::OnListen() {
m_server.Create(1000); // 使用1000号端口 m_server.Listen(); // 侦听 }
在对话框图象上再双击“发送”按钮,添加代码,如下所示: void CLx2Dlg::OnSend() {
UpdateData(TRUE); //更新数据,使m_msg得到当前框中文本 m_recv.Send(m_msg, 255); //发送数据
m_ctrl.SetSel(0, -1); //全选发送框文字 m_ctrl.ReplaceSel(\"\将发送框置空 }
同样地,在第一个工程里工作区类视图里右键,添加新类:CClient。继承自CSocket
在CLx1Dlg类里添加private变量:CClient m_client; 双击对话框图象上的“连接”按钮,添加代码: void CLx1Dlg::OnConnect() {
UpdateData(TRUE);
m_client.Create(1001); //使用1001号端口
if(m_client.Connect(m_ip, 1000)) //连接目标地址,1000端口 {
AfxMessageBox(\"Client端连接成功\");
m_send.EnableWindow(TRUE); //连接成功,可以发送 m_connect.EnableWindow(FALSE); //同时禁止连接按钮 } else {
m_client.Close(); //如果连接失败就关闭 AfxMessageBox(\"连接失败\"); } }
双击发送按钮,添加代码: void CLx1Dlg::OnSend() {
UpdateData(TRUE); //更新数据,使m_msg得到当前框中文本 m_client.Send(m_msg, 255); //发送数据,长度255字节 }
以上这些操作,已经将CSocket的建立,以及主机,客户机建立连接后的消息发送代码添加完成了,但是还缺少使其工作的消息机制。
下面的步骤就是利用OnAccept和OnReceive函数处理socket消息。
首先,在第一个工程的编辑界面点右键,选Class Wizard,在classname栏目里面找到CServer类,添加OnAccept和OnReceive函数并且双击下面的Member function栏目,分别为两个函数添加代码。
void CServer::OnAccept(int nErrorCode) {
// TODO: Add your specialized code here and/or call the base class CSocket::OnAccept(nErrorCode);
((CLx2Dlg*)(AfxGetApp()->m_pMainWnd))->ShowAccept();
//在这里仅仅添加了这一句,因为CLx2Dlg类是发送接收消息的主窗口,
//而且应用程序发送接收的消息也在CLx2Dlg对象实例中进行, //所以当CServer类的对象收到客户机的Connect消息时, //便可调用CLx2Dlg对象中的ShowAccept() 函数处理。 }
这步以后,可以在第一个工程里的Dlg类里添加public成员函数ShowAccept()。 void CLx2Dlg::ShowAccept() {
m_server.Accept(m_recv);
AfxMessageBox(\"Server端连接成功\");
m_send.EnableWindow(TRUE); // 连接成功,可以发送 m_listen.EnableWindow(FALSE); // 同时禁止侦听按钮 }
于是,当客户机调用m_client.Connect(m_ip, 1000);这句时,主机server端发现,并调用ShowAccept函数来建立连接。执行完以后,Socket连接便被建立。
接下来的工作便是添加发送聊天信息的函数了。
注意到前面点击发送按钮的OnSend() 函数已经添加好了,在Lx2工程中只要添加Server端的接收消息和显示消息功能就可以进行消息的传送。
在CServer类里像添加OnAccept() 一样添加成员函数OnReceive()。 void CServer::OnReceive(int nErrorCode) {
// TODO: Add your specialized code here and/or call the base class CSocket::OnReceive(nErrorCode);
((CLx2Dlg*)(AfxGetApp()->m_pMainWnd))->ShowMsg(); }
建立连接后,一方一旦发送数据,另一方的CSocket派生类便调用这个函数。其中代码可以参考前面OnAccept() 进行理解。
在CLx2Dlg里添加成员函数ShowMsg() void CLx2Dlg::ShowMsg() {
char buf[255];
m_recv.Receive(buf, 255); //接收消息到buf里面,长度255字节。
CString msg;
msg.Format(\"%s\", buf); //用AfxMessageBox函数显示接收到的字符窜。
AfxMessageBox(msg); //这里注意CString类的用法 }
同样在第二个工程中也如此这般添加消息接收函数 void CClient::OnReceive(int nErrorCode) {
// TODO: Add your specialized code here and/or call the base class ((CLx1Dlg*)(AfxGetApp()->m_pMainWnd))->ShowMsg(); CSocket::OnReceive(nErrorCode); }
void CLx1Dlg::ShowMsg() {
char buf[255];
m_client.Receive(buf, 255); //接收消息到buf里面,长度255字节。
CString msg;
msg.Format(\"%s\
AfxMessageBox(msg); //用AfxMessageBox函数显示接收到的字符窜。 }
销毁对话框,关闭Socket连接,释放资源。
以下为第二个工程
图1-11
void CLx1Dlg::OnDestroy() {
CDialog::OnDestroy(); m_client.Close(); //关闭套接字 }
在两个vc中分别按下F5键,编译执行两个程序。 程序运行过程如下:
1、在第一个工程中按下侦听。
2、在第二个工程中输入地址后按下连接。
3、在第二个工程的文本框内输入字符,按下发送,则第一个工程便会得到来自第二个工程的消息。
从而实现两个工程之间的通信对话,如图所示。