Transcript Document
第7章
网络编程
学习目标
了解网络编程的方法
理解流式套接字和数据报套接字的区别
理解Windows Sockets的工作原理
了解两种不同的通信方式
掌握利用MFC CSocket类进行网络编程的方法
了解利用MFC CAsyncSocket类进行网络编程的方法
7.1 网络编程概述
7.1.1 Windows Sockets规范
Windows Sockets(即WinSock)规范是90年代初
Microsoft公司联合其他几家大公司共同制定的—套在
Windows下的二进制兼容网络编程接口规范。
Windows Sockets规范制定的本意是将基础网络抽象出来 。
遵守Windows Sockets规范的网络软件,称之为与
windows Sockets兼容。
7.1.2 VC++ 2005网络编程
利用Windows Sockets 网络编程的方法
1.利用Windows Sockets API进行网络编程
麻烦,但灵活
2.利用MFC提供的WinSock类进行网络编程
在MFC中,提供了两个类用来实现对Windows Socket
API的封装,从而实现网络编程
(1)CAsyncSocket类
CAsyncSocket,顾名思义是指采用了异步(非阻塞)套
接字的类。该类主要是提供给那些具有一定网络编程经
验,希望同时拥有Winsock API编程的灵活性和类库编程
便利性的用户的。
(2)CScoket类(阻塞)
CScoket类是CAsyncSocket类的派生类,该类对
Windows Sockets API进行了更多的封装,并且
利用CArchive类进行数据传输,从而使利用该类
进行数据传输的过程与MFC的串行化一致。
7.2 套接字
套接字(Socket)是一个通信终结点,它是Windows
Sockets应用程序用来在网络上发送或接收数据包的对象。
一个套接字有自己的类型,并有一个与之相关的应用程序,
它还可以有自己的名字。
7.2.1 流式套接字
使用流式套接字的过程:
在两个通信节点间建立连接
节点通过这个连接进行通信
通信完毕后取消连接
流式套接字提供了双向的数据流,并且保证数据流的传输是
可靠的、有序的(即:传输的数据顺序的正确性)、无重复的
(即:数据包只被传送一次)
最大特点是通信信息能保证按顺序无遗漏地到达目的地。
7.2.2 数据报套接字
数据报套接字也提供双向的数据传输,与流式套接字不同,
它用于实现无连接的通信方式。
在传输可靠性要求不高的信息或者通信量较少的信息,则可
采用数据报套接字。
7.2.3 IP地址和端口
IP地址是一台计算机在网络上的地址,是一个32位的数字
如202.204.125.5就是由十进制描述的一个IP地址
端口用于标识进程,同一机器上不同的应用程序有不同的端
口,则通过“IP地址+端口”的方式可以唯一标识机器的应
用程序。
7.3 Windows Sockets的工作原理
Windows Sockets的工作方式为客户/服务器模式
根据连接的方式分为
面向连接——流式套接字
无连接——数据报套接字
7.3.1 面向连接的通信方式
服务器
创建并初始化套接字,
指定端口(socket)
客户端
创建并初始化套接字,
指定要连接的端口(socket)
监听客户端的请求(listen)
请求建立连接(connect)
接受连接请求(accept)
发送接收数据(send、recv)
发送确认信息
发送接收数据(send、recv)
销毁套接字,关闭连接
销毁套接字,关闭连接
(closesocket)
(closesocket)
图7-1 面向连接的通信方式
7.3.2 无连接的通信方式
服务器
客户端
创建并初始化套接字(socket)
创建并初始化套接字(socket)
向服务器发送请求(sendto)
接受请求,进行处理
(recvfrom、sendto)
发送结果
接收结果(recvfrom)
销毁套接字,关闭连接
销毁套接字,关闭连接
(closesocket)
(closesocket)
图7-2 无连接的通信方式
7.5 基于MFC的Windows Sockets编程
7.5.1 编制基于流式套接字网络应用程序的步骤
1.服务器端
服务器端应用程序的设计过程如下:
(1)从CSocket类派生两个新类,假设一个新类
的名称为CServeSocket,另一个新类的名称为
CAcceptSocket。其中,CServeSocket套接字用来
监听连接请求,CAcceptSocket套接字是真正用于
与客户端进行连接、接收发送数据的套接字。
(2)创建一个CServeSocket类对象。
(3)调用CSocket::Create函数创建一个CServeSocket
套接字,并指定一个端口,端口号一般要大于1024。
(4)调用CSocket::Listen函数侦听端口。
(5)在CServeSocket中添加虚函数OnAccept。在
OnAccept函数中为每一个连接进来的客户端创建一个
新的CAcceptSocket类对象专门用于读写。
(6)在用于读写的CSocket派生类CAcceptSocket的
OnReceive函数中进行读写操作。
(7)通讯结束时,调用读写套接字的Close函数关闭
为各个客户端分配的读写套接字。
2.客户端
客户端应用程序的设计过程如下:
(1)创建一个CSocket类的派生类,用于连接和读写,假设
新类的名称为CClientSocket。
(2)调用CSocket::Create函数创建套接字。
(3)调用CSocket::Connect函数连接到服务器的指定端口。
(4)调用CSocket::Send函数发送数据,进行发送数据的操
作
(5)在CClientSocket中添加虚函数onReceive,在该函数中
进行读操作。
(6)在结束通讯时,调用CSocket::Close函数关闭套接字。
7.5.2 编制基于流式套接字的网络应用程序
【例7-1】编写两个程序,一个用于模拟服务器端的程序
Mysev,一个用于模拟客户端的程序Myclient,这个网络应
用程序的功能很简单,只是实现服务器和客户端的通信。当
客户端连接上服务器端时,给服务器发送消息:“服务器,
你好!”,服务器向客户端发送消息:“客户端,你好!”。
服务器端程序的运行结果如图7-4所示,客户端程序的运行
结果如图7-5所示。客户端与服务器进行连接后,相互发送
信息后的运行结果分别见图7-6和图7-7。
图7-4 服务器端程序Mysev的运行结果
图7-6 服务器端程序接收到的信息
图7-5 客户端程序Myclient的运行结果
图7-7 客户端程序接收到的信息
程序的实现过程如下:
1.服务器端应用程序设计
(1)利用MFC应用程序向导新建一个基于对话框的
应用程序Mysev
图7-8 为应用程序Mysev添加套接字支持
(2)添加菜单资源
(3)添加控件和关联的成员变量
(4)添加套接字类型
(5)建立套接字类与对话框类的关联
(6)在对话框中初始化套接字并监听连接请求
(7)接受连接请求
(8)接收信息
(9)关闭连接
2.客户端应用程序设计
(1)创建一个基于对话框的应用程序Myclient,在高级功能中
选择“Windows 套接字支持”,同服务器端应用程序的第一步。
(2)添加菜单资源
(3)添加控件和关联的成员变量
(4)添加套接字类型
(5)建立套接字类与对话框类的关联
(6)初始化套接字并建立连接
(7)接收信息
(8)发送消息
(9)关闭连接
(10)编译并运行程序
7.5.3 编制基于数据报套接字网络
应用程序的步骤
基于数据报套接字的网络通信应用程序的服务器和客户端
遵循同样的编程方法。
具体的编程方法如下:
(1)如果服务器(客户程序)需要读取客户的数据,则
创建一个CAsyncSocket类的派生类,并生成 该类的对
象;如果服务器(客户程序)仅发送数据,则直接生成一
个CAsyncSocket类对象。
(2)调用CAsyncSocket::Socket函数创建一个数据报
类型的套接字。
(3)调用CAsyncSocket::SetSockOpt函数设置端口可
重用。
(4)设置一个端口号,并调用CAsyncSocket::Bind函数
将该端口捆绑到本地地址。
(5)调用CAsyncSocket:: SetSockOpt函数设置端口属性,
允许传输广播信息。
(6)调用CAsyncSocket::SendTo函数发送数据
(7)在OnReceive()函数中调用ReceiveFrom函数读取客
户方发送的数据。
(8)通信结束后,调用CAsyncSocket::Close函数关闭套
接字。
7.5.4 编制基于数据报套接字的
网络应用程序
【例7-2】创建两个应用程序,实现以无连接方式发送和接
收数据。程序的运行结果如下图所示。
图7-14 无连接方式发送和结束数据的结果
1.服务器端应用程序设计
(1)利用MFC应用程序向导新建一个基于对话框的应用
程序MyUDPsever,在高级功能中选择“Windows 套接
字支持”。
(2)修改应用程序主窗口
IDD_MYUDPSEVER_DIALOG对话框的布局。删除原
有的【取消】按钮,保留原有的【确定】按钮,将【确
定】按钮的“Default Button”属性设置为false。
(3)添加一个名为IDD_PORT_DIALOG的对话框,为
该对话框添加相应的基于CDialog类的CPortDlg类,该对
话框被用于输入通讯端口号。
(4)修改主窗口类CMyUDPseverDlg,在该类中进行通
讯。
(5)在CMyUDPseverDlg类中添加WM_DESTROY类的
消息处理函数OnDestroy,在该函数中关闭套接字
2.客户端应用程序设计
(1)利用MFC应用程序向导新建一个基于对话框的
应用程序MyUDPclient,在高级功能中选择
“Windows 套接字支持”。
(2)修改应用程序主窗口的
IDD_MYUDPCLIENT_DIALOG对话框资源的布局。
删除原有的【取消】按钮,保留原有的【确定】按钮
(3)由于本示例客户端需要从服务器读取数据,因
此从CSocket类派生一个类CClientSocket,该类用于
读取数据。
(4)添加自定义消息宏。当CClientSocket类对象读取了
数据后,需要通过自定义消息将数据发送给应用程序主
窗口。
(5)对CClientSocket类进行修改
(6)创建一个ID为IDD_PORT_DIALOG的对话框,并
生成相应的基于CDialog类的派生类CPortDlg。
(7)创建一个ID为IDD_TRY_DIALOG的对话框,并生
成相应的基于CDialog类的派生类CTryDlg。
(8)修改MyUDPclientDlg类,添加成员变量和成员函数。
7.6 综合实例:聊天室
【例7-3】创建基于客户/服务器模式的聊天室应用程序。这
个应用程序包括客户端的应用程序和服务器端的应用程序。
(a)服务器端
(b)一个客户端
7.6.1 服务器端应用程序的功能介绍
等待用户连接
通知所有在线用户新登陆用户信息
中转消息
当用户离线时通知所有在线用户
7.6.2 客户端应用程序的功能介绍
获得连接参数后与服务器建立连接
发送/接收消息
断开时通知服务器
7.6.3 服务器端应用程序的编写过程
(1)利用MFC应用程序向导新建一个基于对话框的应用程序
ChatRoom,在高级功能中选择“Windows套接字支持”。
(2)修改应用程序主窗口IDD_CHATROOM_DIALOG对话框的
布局。
(3)添加一个头文件tagHeader.h
(4)添加一个基于CSocket的类CClientSocket,用于和客户端进
行数据交换。
(5)添加一个基于CSocket的类CServerSocket,用于接收客户端
的连接。
(6)修改CChatRoomDlg类的代码
(7)编译并运行程序
7.6.4 客户端应用程序的编写过程
(1)利用MFC应用程序向导新建一个基于对话框的应用程序
ChatClient,在高级功能中选择“Windows套接字支持”。
(2)修改应用程序主窗口IDD_CHATCLIENT_DIALOG对话框的布局。
(3)由于用户登录时需要添加服务器地址、自己的用户名等信息,所
以添加一个对话框资源,其ID为IDD_LOGIN_DIALOG,并为该对话框
添加基类为CDialog的派生类CLoginDlg。
(4)同服务器端类似,添加一个头文件tagHeader.h
(5)添加一个基于CSocket类的派生类CClientSocket,用于完成数据的
发送和接收。
(6)对类CChatClientDlg进行修改 。
(7)编译并运行程序 。