《VC程序设计第9讲Winsock网络编程》由会员分享,可在线阅读,更多相关《VC程序设计第9讲Winsock网络编程(81页珍藏版)》请在金锄头文库上搜索。
1、VC+VC+程序设计第程序设计第9 9讲讲- -WinsockWinsock网络编程网络编程第第9讲讲 WinSock网络编程网络编程n9.1网络通信基础网络通信基础n9.2WinSock概述概述n9.3MFC WinSock编程编程n9.4MFC WinSock网络编程实例网络编程实例9.1网络通信基础网络通信基础n网络编程是指:应用程序需要与网络中其网络编程是指:应用程序需要与网络中其它系统上的应用程序之间进行通信。它系统上的应用程序之间进行通信。n以下介绍一下有关网络通信的基础知识。以下介绍一下有关网络通信的基础知识。计算机网络计算机网络n计算机网络是相互连接的独立自主的计算计算机网络是
2、相互连接的独立自主的计算机的集合,最简单的网络形式由两台计算机的集合,最简单的网络形式由两台计算机组成。机组成。两台计算机通过网络进行通信两台计算机通过网络进行通信AB网络网络192.168.0.118192.168.0.10协议协议协议协议端端口口号号端端口口号号IP地址地址nIP网络中每台主机都必须有一个惟一的网络中每台主机都必须有一个惟一的IP地地址;址;nIP地址是一个逻辑地址;地址是一个逻辑地址;n因特网上的因特网上的IP地址具有全球唯一性;地址具有全球唯一性;n32位,位,4个字节,常用点分十进制的格式表个字节,常用点分十进制的格式表示,例如:示,例如:192.168.0.16协议
3、协议n为进行网络中的数据交换(通信)而建立为进行网络中的数据交换(通信)而建立的规则、标准或约定。的规则、标准或约定。(=语义语义+语法语法+规则规则) n不同层具有各自不同的协议。不同层具有各自不同的协议。网络的状况网络的状况多种通信媒介多种通信媒介有线、无线有线、无线不同种类的设备不同种类的设备通用、专用通用、专用不同的操作系统不同的操作系统Unix、Windows 不同的应用环境不同的应用环境固定、移动固定、移动 它们互相交织,形成了非常复杂的系它们互相交织,形成了非常复杂的系统应用环境。统应用环境。网络异质性问题的解决网络异质性问题的解决n网络体系结构网络体系结构就是使这些用不同媒介连
4、接起来的就是使这些用不同媒介连接起来的不同设备和网络系统在不同的应用环境下实现互不同设备和网络系统在不同的应用环境下实现互操作性,并满足各种业务需求的一种粘合剂,它操作性,并满足各种业务需求的一种粘合剂,它营造了一种营造了一种“生存空间生存空间” 任何厂商的任何产品、任何厂商的任何产品、以及任何技术只要遵守这个空间的行为规则,就以及任何技术只要遵守这个空间的行为规则,就能够在其中生存并发展。能够在其中生存并发展。n网络体系结构网络体系结构解决异质性问题采用的是分层方法解决异质性问题采用的是分层方法 把复杂的网络互联问题划分为若干个较小的、把复杂的网络互联问题划分为若干个较小的、单一的问题,在不
5、同层上予以解决。单一的问题,在不同层上予以解决。就像我们在编程时把问题分解为很多小的模块来就像我们在编程时把问题分解为很多小的模块来解决一样。解决一样。ISO/OSI七层参考模型七层参考模型nOSI(Open System Interconnection)参考参考模型将网络的不同功能划分为模型将网络的不同功能划分为7层。层。应用层应用层表示层表示层物理层物理层会话层会话层传输层传输层网络层网络层数据链路层数据链路层处理网络应用处理网络应用数据表示数据表示主机间通信主机间通信端到端的连接端到端的连接寻址和最短路径寻址和最短路径介质访问介质访问(接入接入)二进制传输二进制传输ISO/OSI七层参考
6、模型七层参考模型n通信实体的对等层之间不允许直接通信。通信实体的对等层之间不允许直接通信。n各层之间是严格单向依赖。各层之间是严格单向依赖。n上层使用下层提供的服务上层使用下层提供的服务 Service user;n下层向上层提供服务下层向上层提供服务 Service provider。对等通信示例对等通信示例“你好你好”“Hello”传真传真中国教师中国教师翻译翻译秘书秘书“Hallo”“Hello”传真传真德国教师德国教师翻译翻译秘书秘书对交谈内容的共识对交谈内容的共识用英语对话用英语对话使用传真通信使用传真通信物理通信线路物理通信线路对等层通信的实质对等层通信的实质n对等层实体之间虚拟通
7、信。对等层实体之间虚拟通信。n下层向上层提供服务,实际通信在最底层下层向上层提供服务,实际通信在最底层完成。完成。OSI各层所使用的协议各层所使用的协议n应用层:应用层:远程登录协议远程登录协议Telnet、文件传输协议、文件传输协议FTP、 超文本传输协议超文本传输协议HTTP、域名服务、域名服务DNS、简单邮件传输协议简单邮件传输协议SMTP、邮局协议、邮局协议POP3等。等。n传输层:传输控制协议传输层:传输控制协议TCP、用户数据报协议、用户数据报协议UDP。 TCP:面向连接的可靠的传输协议。:面向连接的可靠的传输协议。 UDP:是无连接的,不可靠的传输协议。:是无连接的,不可靠的传
8、输协议。n网络层:网际协议网络层:网际协议IP、Internet互联网控制报文协互联网控制报文协议议ICMP、Internet组管理协议组管理协议IGMP。数据封装数据封装n一台计算机要发送数据到另一台计算机,数据首一台计算机要发送数据到另一台计算机,数据首先必须打包,打包的过程称为先必须打包,打包的过程称为封装封装。n封装就是在数据前面加上特定的协议头部。封装就是在数据前面加上特定的协议头部。数据数据数据数据协议头协议头数据封装数据封装nOSI参考模型中,对等层协议之间交换的信息单元统称为参考模型中,对等层协议之间交换的信息单元统称为协议数据单元协议数据单元(PDU,Protocol Dat
9、a Unit)。nOSI参考模型中每一层都要依靠下一层提供的服务。参考模型中每一层都要依靠下一层提供的服务。n为了提供服务,下层把上层的为了提供服务,下层把上层的PDU作为本层的数据封装,作为本层的数据封装,然后加入本层的头部(和尾部)。头部中含有完成数据传然后加入本层的头部(和尾部)。头部中含有完成数据传输所需的控制信息。输所需的控制信息。n这样,数据自上而下递交的过程实际上就是不断封装的过这样,数据自上而下递交的过程实际上就是不断封装的过程。到达目的地后自下而上递交的过程就是不断拆封的过程。到达目的地后自下而上递交的过程就是不断拆封的过程。由此可知,在物理线路上传输的数据,其外面实际上程。
10、由此可知,在物理线路上传输的数据,其外面实际上被包封了多层被包封了多层“信封信封”。n但是,某一层只能识别由对等层封装的但是,某一层只能识别由对等层封装的“信封信封”,而对于被,而对于被封装在封装在“信封信封”内部的数据仅仅是拆封后将其提交给上层,内部的数据仅仅是拆封后将其提交给上层,本层不作任何处理。本层不作任何处理。TCP/IP模型模型nTCP/IP起源于美国国防部高级研究规划署起源于美国国防部高级研究规划署(DARPA)的一项研究计划的一项研究计划实现若干台主机的实现若干台主机的相互通信。相互通信。n现在现在TCP/IP已成为已成为Internet上通信的工业标准。上通信的工业标准。nT
11、CP/IP模型包括模型包括4个层次:个层次:应用层应用层传输层传输层网络层网络层网络接口网络接口TCP/IP与与OSI参考模型的对应关系参考模型的对应关系应用层应用层表示层表示层会话层会话层传输层传输层物理层物理层数据链路层数据链路层网络层网络层7654321OSI参考模型参考模型应用层应用层传输层传输层网络接口网络接口网络层网络层TCP/IP模型模型端口端口n按照按照OSI七层模型的描述,传输层提供进程(应用程序)通七层模型的描述,传输层提供进程(应用程序)通信的能力。为信的能力。为 了标识通信实体中进行通信的进程(应用程序)了标识通信实体中进行通信的进程(应用程序),TCP/IP协议提出了
12、协议端口(协议提出了协议端口(protocol port,简称端口),简称端口)的概念。的概念。n端口是一种抽象的软件结构(包括一些数据结构和端口是一种抽象的软件结构(包括一些数据结构和I/O缓冲区)缓冲区)。应用程序通过系统调用与某端口建立连接(。应用程序通过系统调用与某端口建立连接(binding)后,)后,传输层传给该端口的数据都被相应的进程所接收,相应进程传输层传给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。发给传输层的数据都通过该端口输出。n端口用一个整数型标识符来表示,即端口号。端口号跟协议端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,相
13、关,TCP/IP传输层的两个协议传输层的两个协议TCP和和UDP是完全独立的两是完全独立的两个软件模块,因此各自的端口号也相互独立。个软件模块,因此各自的端口号也相互独立。n端口使用一个端口使用一个16位的数字来表示,它的范围是位的数字来表示,它的范围是065535,1024以下的端口号保留给预定义的服务。例如:以下的端口号保留给预定义的服务。例如:http使用使用80端口。端口。9.2WinSock概述概述nWindows Sock(Windows套接字,简称套接字,简称WinSock)是微软根据)是微软根据UNIX操作系统中流操作系统中流行的行的Berkeley(伯克利伯克利)套接字规范,
14、而实现套接字规范,而实现的一套的一套Microsoft Windows下的网络编程下的网络编程接口。接口。n在在ISO的的OSI网络七层协议中,网络七层协议中,WinSock主要负责控制数据的输入和输主要负责控制数据的输入和输出,也就是传输层和网络层。出,也就是传输层和网络层。n它屏蔽了数据链路层和物理层,给它屏蔽了数据链路层和物理层,给Windows下的网络编程带来了巨大的下的网络编程带来了巨大的变化。变化。Windows Sockets通信机制通信机制nWindows Sockets通信的基础是套接字通信的基础是套接字(Socket)。nSocket就是在应用程序之间用于读(接收就是在应用
15、程序之间用于读(接收信息)或写(发送信息)的一个网络对象。信息)或写(发送信息)的一个网络对象。客户机服务器模式客户机服务器模式n在在TCP/IP网络应用中,通信的两个进程网络应用中,通信的两个进程间相互作用的主要模式是客户机间相互作用的主要模式是客户机/服务器服务器模式模式(client/server),即客户向服务器提,即客户向服务器提出请求,服务器接收到请求后,提供相出请求,服务器接收到请求后,提供相应的服务。应的服务。客户机服务器模式客户机服务器模式n客户机客户机/服务器模式在操作过程中采取的是主动请求的方式。服务器模式在操作过程中采取的是主动请求的方式。首先服务器方要先启动,并根据请
16、求提供相应的服务:首先服务器方要先启动,并根据请求提供相应的服务:打开一个通信通道并告知本地主机,它愿意在某一地址和端口打开一个通信通道并告知本地主机,它愿意在某一地址和端口上接收客户请求。上接收客户请求。等待客户请求到达该端口。等待客户请求到达该端口。接收到服务请求,处理该请求并发送应答信号,同时要激活一接收到服务请求,处理该请求并发送应答信号,同时要激活一个新的进程个新的进程(或线程或线程)来处理这个客户请求。新进程来处理这个客户请求。新进程(或线程或线程) 处处理此客户请求,并不需要对其它请求作出应答。服务完成后,理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的
17、通信链路,并终止。关闭此新进程与客户的通信链路,并终止。返回第二步,等待另一客户请求。返回第二步,等待另一客户请求。关闭服务器。关闭服务器。客户方:客户方:打开一个通信通道,并连接到服务器所在主机的特定端口。打开一个通信通道,并连接到服务器所在主机的特定端口。向服务器发服务请求报文,等待并接收应答;继续提出请求。向服务器发服务请求报文,等待并接收应答;继续提出请求。请求结束后关闭通信通道并终止。请求结束后关闭通信通道并终止。基于基于TCP(面向连接面向连接)的的socket编程编程服务器端程序:服务器端程序:1、创建套接字(、创建套接字(socket)。)。2、将套接字绑定到一个本地地址、将套
18、接字绑定到一个本地地址和端口上(和端口上(bind)。)。3、将套接字设为监听模式,准备、将套接字设为监听模式,准备接收客户请求(接收客户请求(listen)。)。4、等待客户请求到来;当请求到、等待客户请求到来;当请求到来后,接受连接请求,返回一个来后,接受连接请求,返回一个新的对应于此次连接的套接字新的对应于此次连接的套接字(accept)。)。5、用返回的套接字和客户端进行、用返回的套接字和客户端进行通信(通信(send/receive)。)。6、返回第、返回第4步,等待另一客户请步,等待另一客户请求。求。7、关闭套接字。、关闭套接字。客户端程序客户端程序:1、创建套接字(、创建套接字(
19、socket)。)。2、向服务器发出连接请求、向服务器发出连接请求(connect)。)。3、和服务器端进行通信、和服务器端进行通信(send/receive)。)。4、关闭套接字。、关闭套接字。9.3MFC WinSock编程编程nMFC为套接字提供了相应的类为套接字提供了相应的类CAsynSocket和和CSocket,它们封装了,它们封装了Windows Sockets的的API,从而程序员可,从而程序员可以用面向对象的方法调用以用面向对象的方法调用Socket。n基本类基本类CAsySocket提供了全面的事件驱动提供了全面的事件驱动的的Socket通信能力,用户可以创建自己派通信能力
20、,用户可以创建自己派生的生的Socket类,来捕获和响应每个事件。类,来捕获和响应每个事件。nCSocket类是类是CAsySocket的派生类,封装的派生类,封装和简化了基本类的某些功能。和简化了基本类的某些功能。9.3MFC WinSock编程编程n9.3.1MFC WinSock类类n9.3.2WinSock初始化初始化n9.3.3Socket的创建、连接与关闭的创建、连接与关闭n9.3.4数据的发送与接收数据的发送与接收9.3.1MFC WinSock类类nCAsyncSocket类是在一个很低的层次上类是在一个很低的层次上封装了封装了Windows Sockets API,只提供了,
21、只提供了一些基本的操作。一些基本的操作。nCSocket则是由则是由CAsyncSocket派生,是派生,是Windows Sockets API的高层抽象,提供的高层抽象,提供了更高层次的功能。了更高层次的功能。n通过通过MFC WinSock类,开发者可以不考虑类,开发者可以不考虑网络字节顺序和忽略掉更多的通信细节。网络字节顺序和忽略掉更多的通信细节。CAsyncSocket类主要成员函数及其功能(一)类主要成员函数及其功能(一)n构造函数构造函数CAsyncSocket:构造构造CAsyncSocket对象。对象。Create:创建套接字。创建套接字。n属性:属性:Attach:对对CA
22、syncSocket对象附加套接字句柄。对象附加套接字句柄。Detach:从从CAsyncSocket对象除去套接字句柄。对象除去套接字句柄。FromHandle:返回返回CAsyncSocket对象的指针,给出套对象的指针,给出套接字句柄。接字句柄。GetLastError:获得上一次运行失败的状态。获得上一次运行失败的状态。GetPeerName:获得与套接字连接的对等套接字的地址。获得与套接字连接的对等套接字的地址。GetSockName:获得套接字的本地名。获得套接字的本地名。GetSockOpt:获得套接字选项。获得套接字选项。SetSockOpt:设置套接字选项。设置套接字选项。C
23、AsyncSocket类主要成员函数及其功能(二)类主要成员函数及其功能(二)n操作函数:操作函数:Accept: 接受套接字上的连接请求。接受套接字上的连接请求。CAsyncSelect:请求对于套接字的事件通知。请求对于套接字的事件通知。Bind:与套接字有关的本地地址。与套接字有关的本地地址。Close:关闭套接字。关闭套接字。Connect:发出连接请求。发出连接请求。IOCtr:控制套接字模式。控制套接字模式。Listen:建立套接字,监听即将到来的连接请求。建立套接字,监听即将到来的连接请求。Receive:从套接字上接收数据。从套接字上接收数据。Send:给连接套接字发送数据。给
24、连接套接字发送数据。SendTo:给特定目的地发送数据。给特定目的地发送数据。ShutDown:使套接字上的使套接字上的send或或receive调用无效。调用无效。CAsyncSocket类主要成员函数及其功能(三)类主要成员函数及其功能(三)n可重载函数:可重载函数:OnAccept:通知侦听套接字,通过通知侦听套接字,通过Accept接受连接请求。接受连接请求。OnClose:通知套接字,关闭对它的套接字连接。通知套接字,关闭对它的套接字连接。OnConnect:通知连接套接字,连接尝试已经完成,无论成通知连接套接字,连接尝试已经完成,无论成功或失败。功或失败。OnReceive:通知侦
25、听套接字,通过调用通知侦听套接字,通过调用Receive接收数据。接收数据。OnSend:通知套接字,通过调用通知套接字,通过调用Send,它可以发送数据。,它可以发送数据。n数据成员:数据成员:m_hSocket:指定附加在此指定附加在此AsyncSocket对象上的对象上的SOCKET句柄。句柄。CSocket类主要成员及其功能类主要成员及其功能n构造函数:构造函数:CSocket:构造一个构造一个CSocket对象。对象。Create:创建一个套接字。创建一个套接字。n属性:属性:IsBlocking:确定一个阻塞调用是否在进行中。确定一个阻塞调用是否在进行中。FromHandle:返回
26、一个指向返回一个指向CSocket对象的指针,给出对象的指针,给出一个套接字句柄。一个套接字句柄。n操作函数:操作函数:CancelBlockingCall:取消一个当前在进行中的套接字调取消一个当前在进行中的套接字调用。用。n可重载函数可重载函数OnMessagePending:当等待完成一个阻塞调用时调用此当等待完成一个阻塞调用时调用此函数来处理悬而未决的消息。函数来处理悬而未决的消息。9.3.2WinSock初始化初始化n在使用在使用WinSock MFC类之前,必须为应用程序初始化类之前,必须为应用程序初始化WinSock环境。环境。n这只需要调用实例初始化函数这只需要调用实例初始化函
27、数AfxSocketInit()即可。如下即可。如下面的代码:面的代码:nBOOL CChatRoomServerApp:InitInstance()nnif (!AfxSocketInit()n n AfxMessageBox(IDP_SOCKETS_INIT_FAILED); return FALSE;n nn同时,在同时,在”stdafx.h”文件中添加如下代码:文件中添加如下代码:n#include n如果在使用如果在使用MFC AppWizardEXE创建创建MFC工程时,在工程时,在MFC AppWizard step 2对对话框选择话框选择”Windows Sockets”选项,
28、则程选项,则程序会自动添加上面的代码,实现序会自动添加上面的代码,实现WinSock的初始化。的初始化。9.3.3Socket的创建、连接与关闭的创建、连接与关闭n初始化初始化Winsock环境后,即可以实现环境后,即可以实现socket的创建、连接与关闭。的创建、连接与关闭。n1. 创建创建Socketn为了使应用程序可以使用为了使应用程序可以使用Socket,首先需,首先需要声明要声明CAsyncSocket、CSocket或者这两个类或者这两个类派生类的一个对象成员,如:派生类的一个对象成员,如:nCAsyncSocket m_sock;n使用使用Socket对象之前,必须调用它的对象之
29、前,必须调用它的Create函数创建函数创建Socket。n如果准备用如果准备用Socket连接另一个应用程序,作为客户程序,连接另一个应用程序,作为客户程序,不必给不必给Create函数传送任何参数。函数传送任何参数。n而如果而如果Socket作为服务程序,准备监听另一个应用程序的作为服务程序,准备监听另一个应用程序的连接请求,则至少需要传送一个端口给连接请求,则至少需要传送一个端口给Create函数函数:nif(m_sock.Create(5800)nn /Socket创建成功后的代码创建成功后的代码nnelsen /错误处理代码错误处理代码nCreate函数的其他参数还有很多,可以包括函
30、数的其他参数还有很多,可以包括Socket类型、类型、准备响应的事件以及准备响应的事件以及Socket的本地的的本地的IP地址等,一般情况地址等,一般情况下取默认值即可。下取默认值即可。n2. Socket连接连接n建立建立Socket对象后,就可以创建一个连接,对象后,就可以创建一个连接,共需要以下共需要以下3个步骤:个步骤:(1)在服务器端让在服务器端让Socket调用调用listen()函数,监听函数,监听对方的连接请求。对方的连接请求。(2)在客户端通过调用在客户端通过调用Connect()函数连接服务器。函数连接服务器。 (3)在服务器通过调用在服务器通过调用Accept()函数接受
31、连接请函数接受连接请求。求。n(1)在服务器端让在服务器端让Socket调用调用listen()函数,函数,监听对方的连接请求。监听对方的连接请求。nlisten()函数的调用方式如下:函数的调用方式如下:nif(m_sock.listen()nn /Socket监听成功后的代码监听成功后的代码nnelsen /错误处理代码错误处理代码n(2)在客户端通过调用在客户端通过调用Connect()函数连接服务器。函数连接服务器。n使用使用Connect()函数连接服务器必须传达两个参数:函数连接服务器必须传达两个参数:连接的计算机名或连接的计算机名或IP地址和端口号。地址和端口号。nConnect
32、()函数的调用方式如下:函数的调用方式如下:nif(m_sock.Connect(“127.0.0.1”,5802);nn /Socket连接成功后的代码连接成功后的代码nnelsen /错误处理代码错误处理代码n(3) 一旦服务器监听到连接请求,一旦服务器监听到连接请求,CAsycnSocket类就激活类就激活OnAccept()事件,让应用程序知道已有连接请求,则服务器端必须事件,让应用程序知道已有连接请求,则服务器端必须调用调用Accept()函数接受连接请求。函数接受连接请求。Accept()函数调用成功,函数调用成功,将创建另一个将创建另一个Socket,通过它与对方应用程序连接。,
33、通过它与对方应用程序连接。nAccept()函数的调用方式如下:函数的调用方式如下:nif(m_sock.Accept(m_sock2); /等待连接请求等待连接请求n /连接请求成功后的代码连接请求成功后的代码nnelsen /连接请求失败后的代码连接请求失败后的代码nm_sock2和客户方建立了连接,以后就通过这个和客户方建立了连接,以后就通过这个m_sock2对象去和客户方进行通信,而监听对象去和客户方进行通信,而监听Socket m_sock仍然继续在监听,一旦又有一个客户方要连接服仍然继续在监听,一旦又有一个客户方要连接服务方,务方,OnAccept()又会被调用一次。又会被调用一次
34、。m_sock2是和客户是和客户方通信的服务方,它不会触发方通信的服务方,它不会触发OnAccept()事件,因为它不事件,因为它不是监听是监听Socket。n3. 关闭关闭Socket连接连接n在应用程序之间的通信完成之后,就可以在应用程序之间的通信完成之后,就可以调用调用Close()函数关闭这个连接。如下:函数关闭这个连接。如下:nm_sock.close();9.3.4数据的发送与接收数据的发送与接收n通过通过Socket提供的提供的send()和和Receive()函函数可以实现任何类型数据的发送和接收。数可以实现任何类型数据的发送和接收。n1. 发送发送n通过通过Socket连接发
35、送请求,可以使用连接发送请求,可以使用Send()函数,该函数,该函数的原型如下:函数的原型如下:nint CAsynSocket:Send(const void* lpBuf,nint nBufLen,int nFlags=0);n各参数含义如下:各参数含义如下:nlpBuf:指向发送数据缓冲区的指针。数据为指向发送数据缓冲区的指针。数据为CString变变量时,可使用量时,可使用LPCTSTR操作符把操作符把CString变量作为缓变量作为缓冲区传送。冲区传送。nnBuflen:指明缓冲区要发送数据的长度。指明缓冲区要发送数据的长度。nnFlags:该参数是可选的,用于控制消息的发送方式。
36、该参数是可选的,用于控制消息的发送方式。n函数执行成功,返回发送到对方应用程序的数据总量。函数执行成功,返回发送到对方应用程序的数据总量。如果有错误产生,函数返回如果有错误产生,函数返回SOCKET_ERROR。n典型的典型的Send()函数调用方式如下:函数调用方式如下:nchar str1000;nint ilen;nint iSend;niLen=str.GetLength();niSend=m_sock.Send(LPCTSTR(str),iLen);nif(iSend=SOCKET_ERROR)nn /发送不成功错误处理代码发送不成功错误处理代码nnelsenn /发送成功处理代码发
37、送成功处理代码nn2. 接收数据接收数据n发送数据后,在另一端应用程序就可通过发送数据后,在另一端应用程序就可通过Receive()函数接收数据了。函数接收数据了。n其函数原型如下:其函数原型如下:nint CAsyncSocket:Receive ( nvoid* lpBuf, int nBufLen, int nFlags = 0 );n各参数含义如下:各参数含义如下:lpBuf:指向接收数据缓冲区的指针。指向接收数据缓冲区的指针。nBufLen:接收数据缓冲区的长度。接收数据缓冲区的长度。nFlags:该参数是可选的,用于控制消息的发送方式。该参数是可选的,用于控制消息的发送方式。n通过
38、执行成功后,通过执行成功后,Receive()函数也返回接收到的函数也返回接收到的数据的数据量。如果有错误产生,函数返回数据的数据量。如果有错误产生,函数返回SOCKET_ERROR。n典型的典型的Receive()的调用方式如下:的调用方式如下:nchar Buf1024;nint iRecv;niRecv=m_sock.Receive(Buff,1024);nif(iRecv=SOCKET_ERROR)nn /接收不成功错误处理代码接收不成功错误处理代码nnelsen BufiRecv=0;n 接收成功时处理代码接收成功时处理代码n n9.4MFC WinSock网络编程实例网络编程实例n
39、为了说明为了说明WinSock的基本功能,本节将采的基本功能,本节将采用用MFC提供的提供的CSocket类实现一个采用类实现一个采用Client/Server结构的可以多人同时在线的结构的可以多人同时在线的聊天室。聊天室。n正如大多数聊天室一样,需要一个聊天室正如大多数聊天室一样,需要一个聊天室服务器,它可以和很多客户端进行通信,服务器,它可以和很多客户端进行通信,从而实现把来自不同的客户的聊天信息转从而实现把来自不同的客户的聊天信息转发给所有其他的客户端。发给所有其他的客户端。9.4MFC WinSock网络编程实例网络编程实例n9.4.1实例说明实例说明n9.4.2服务器端程序创建服务器
40、端程序创建n9.4.3客户端程序创建客户端程序创建9.4.1实例说明实例说明n创建一个多人在线的服务器,首先必须有一个服创建一个多人在线的服务器,首先必须有一个服务器端口程序,各客户端消息均发送至服务器,务器端口程序,各客户端消息均发送至服务器,而服务器则将收到的消息分发给所有的客户程序。而服务器则将收到的消息分发给所有的客户程序。n对于本例,服务器和客户端均在本机运行,因此对于本例,服务器和客户端均在本机运行,因此服务器的服务器的IP地址为地址为127.0.0.1。n服务器程序为一个简单的对话框程序,只有服务器程序为一个简单的对话框程序,只有3个按个按钮:钮:“启动启动”、“停止停止”、“退
41、出退出”,分别实现启动、,分别实现启动、停止网络服务和退出窗口的功能。停止网络服务和退出窗口的功能。n如下图所示:如下图所示:n客户端启动时,首先弹出窗口实现用户的连接,客户端启动时,首先弹出窗口实现用户的连接,就可以向服务器发送消息,并在列表框中显示服就可以向服务器发送消息,并在列表框中显示服务器会传的其所有接收到的消息。务器会传的其所有接收到的消息。n其运行结果如下图所示:其运行结果如下图所示:分析:分析:n为了实现多人在线聊天,本程序中设计了为了实现多人在线聊天,本程序中设计了客户套接字列表来保存所有的客户端套接客户套接字列表来保存所有的客户端套接字。字。9.4.2服务器端程序创建服务器
42、端程序创建n服务器端程序为基于对话框的服务器端程序为基于对话框的MFC应用程序。其应用程序。其创建过程如下:创建过程如下:n1. 创建工程:创建一个名为创建工程:创建一个名为”ChatRoomServer”的基于对话框的的基于对话框的MFC工程,在步骤工程,在步骤2时选中时选中“Windows Sockets”选项,其他步骤使用默认值。选项,其他步骤使用默认值。n2. 添加控件资源添加控件资源n服务器端对话框只有服务器端对话框只有3个按钮控件:个按钮控件:“启动启动”、“停停止止”和和“退出退出”,利用,利用ClassWizard为为“启动启动”和和“停停止止”按钮添加按钮添加BN_CLICK
43、ED消息响应函数消息响应函数OnButtonStart()和和OnButtonStop()。n3.派生派生CSocket类类n程序从程序从CSocket类派生了两个类派生了两个Socket类类CListenSocket和和ClientSocket,用于创建监,用于创建监听套接字和客户套接字。其中,听套接字和客户套接字。其中,ClientSocket类主要用于创建服务方的客户端套接类主要用于创建服务方的客户端套接字列表,实现将服务器接收的消息分发给所有客户字列表,实现将服务器接收的消息分发给所有客户端端Socket。ClistenSocket类主要是重载了类主要是重载了CSocket类的类的On
44、Accept()函数,用于创建客户端套接字和维护客户端套接函数,用于创建客户端套接字和维护客户端套接字列表。字列表。(1)CClientSocket类类n该类主要用于创建服务器客户该类主要用于创建服务器客户Socket,实,实现接收消息并分发给所有连接至服务器的现接收消息并分发给所有连接至服务器的客户客户Socket。nCClientSocket类的声明如下类的声明如下:nclass CClientSocket : public CSocketnpublic:nCClientSocket(CClientSocketList *);nvirtual CClientSocket();nCClien
45、tSocketList *List; /所在列表首地址所在列表首地址 CClientSocket * Front; /指向前一个套接字指向前一个套接字 nCClientSocket * Next; /指向后一个套接字指向后一个套接字nvirtual void OnReceive(int nErrorCode);nvirtual void OnClose(int nErrorCode);n;n相应的函数实现代码如下:相应的函数实现代码如下:nCClientSocket:CClientSocket(CClientSocketList *tmp)nFront=0;nNext=0;nList=tmp;
46、nn/此处此处OnReceice是接收时激活的。是接收时激活的。nvoid CClientSocket:OnReceive(int nErrorCode) nn / 通过通过this指针指向的客户套接字接收数据指针指向的客户套接字接收数据n /并把接收到的数据再发送给所有客户端并把接收到的数据再发送给所有客户端nList-Sends(this);n(2)CClientSocketList类类n该类用于接收数据和向各客户套接字发送数据。其声明该类用于接收数据和向各客户套接字发送数据。其声明如下如下:nclass CClientSocketListnpublic:n CClientSocketLi
47、st();nvirtual CClientSocketList();n /接收并向所有客户套接字发送数据接收并向所有客户套接字发送数据nBOOL Sends(CClientSocket *);n /向客户套接字列表添加一个套接字向客户套接字列表添加一个套接字n BOOL Add(CClientSocket *); nCClientSocket * Head; /客户套接字列表元素指针客户套接字列表元素指针n;n其主要函数的实现如下所示:其主要函数的实现如下所示:Add方法方法n/功能:将功能:将add元素添加在列表最后。元素添加在列表最后。nBOOL CClientSocketList:Add
48、(CClientSocket *add)n/tmp暂存暂存Head元素元素n CClientSocket *tmp=Head;nif (!Head) /如果列表为空,则添加头元素后返回如果列表为空,则添加头元素后返回nHead=add;nreturn true;nn /若列表不为空,则若列表不为空,则add元素添加在列表最后,元素添加在列表最后,n/并把原来的末尾元素指向该元素并把原来的末尾元素指向该元素n while (tmp-Next) tmp=tmp-Next;n tmp-Next=add; nreturn true;nsends方法方法n/功能:功能: 通过通过tmp指针指向的客户套接
49、字接收数据指针指向的客户套接字接收数据n/并把接收的数据再发送给所有客户端并把接收的数据再发送给所有客户端nBOOL CClientSocketList:Sends(CClientSocket *tmp)nchar buff1000; /分配缓存分配缓存nint n;n /将当前套接字指针置为客户套接字列表的头指针将当前套接字指针置为客户套接字列表的头指针nCClientSocket *curr=Head; n /把把tmp客户套接字中的内容接收到缓存客户套接字中的内容接收到缓存buff中。中。nn=tmp-Receive(buff,1000);nbuffn=0;n /把缓存中把缓存中buff
50、中的内容依次发送给列表中的所有套接字中的内容依次发送给列表中的所有套接字nwhile (curr)n curr-Send(buff,n); /向各个客户端发所有聊天记录向各个客户端发所有聊天记录n curr=curr-Next;nnreturn true;n(3)CListenSocket类类nCListenSocket类派生自类派生自CSocket类,用于监类,用于监听套接字接受连接请求,创建客户端套接字,听套接字接受连接请求,创建客户端套接字,并把客户端套接字添加到客户套接字列表中。并把客户端套接字添加到客户套接字列表中。n其类的声明如下:其类的声明如下:nclass CListenSoc
51、ket : public CSocketnpublic:nCListenSocket();nvirtual CListenSocket();nvirtual void OnAccept(int nErrorCode); nCClientSocketList CCSL; n;n在在CListenSocket类中,重载了类中,重载了CSocket: OnAccept函数,用于接受客户端连接请求,创建与该客户端通函数,用于接受客户端连接请求,创建与该客户端通信的套接字,并把该套接字添加到客户端套接字列表信的套接字,并把该套接字添加到客户端套接字列表中。中。nOnAccept函数重新定义如下:函数重新
52、定义如下:nvoid CListenSocket:OnAccept(int nErrorCode) n/创建客户套接字并放在客户端套接字列表中创建客户套接字并放在客户端套接字列表中n ClientSocket *tmp=new CClientSocket(&CCSL);n /Accept()函数一旦调用成功,将指定函数一旦调用成功,将指定tmp为为n /新的客户端套接字,与新客户端建立通信连接。新的客户端套接字,与新客户端建立通信连接。 nAccept(*tmp); n /向客户端套接字列表添加该套接字向客户端套接字列表添加该套接字nCCSL.Add(tmp); n4. 添加按钮响应函数的实现
53、代码添加按钮响应函数的实现代码n“开始开始”按钮响应函数的实现代码如下按钮响应函数的实现代码如下:nvoid CChatRoomServerDlg:OnButtonStart() nn/使启动按钮无效使启动按钮无效nm_IDC_BUTTON_START.EnableWindow(FALSE);n /创建监听套接字的端口为创建监听套接字的端口为6767n ListenSocket.Create(6767); nListenSocket.Listen(); /开始监听开始监听n /将停止按钮激活将停止按钮激活 nm_IDC_BUTTON_STOP.EnableWindow(TRUE);nn“停止停
54、止”按钮响应函数的实现代码如下按钮响应函数的实现代码如下:nvoid CChatRoomServerDlg:OnButtonStop() nn/ TODO: Add your control notification handler code heren /使停止按钮无效使停止按钮无效nm_IDC_BUTTON_STOP.EnableWindow(FALSE); n ListenSocket.Close();/关闭监听套接字关闭监听套接字n/将启动按钮激活将启动按钮激活 n m_IDC_BUTTON_START.EnableWindow(TRUE);n9.4.3客户端程序创建客户端程序创建n客
55、户端同样为基于对话框的客户端同样为基于对话框的MFC应用程序,应用程序,其创建过程如下:其创建过程如下:n1.创建工程创建工程n创建一个名为创建一个名为“ChatRoomClient”的基于对的基于对话框的话框的MFC 工程,同样在步骤工程,同样在步骤2时选中时选中“Windows Sockets”选项,其他步骤使用选项,其他步骤使用默认值。默认值。n2. 创建并设计一个派生自创建并设计一个派生自CSocket类的类的CClientSocket类,管理创建客户端的套接字,以发送连接请求,和接类,管理创建客户端的套接字,以发送连接请求,和接收数据。收数据。nCClientSocket类的类声明如
56、下:类的类声明如下:nclass CClientSocket : public CSocketnpublic:nCChatRoomClientDlg * myDlg;nBOOL SetDlg(CChatRoomClientDlg *tmp);nCString NikeName;nCClientSocket();nvirtual CClientSocket();nvirtual void OnReceive(int nErrorCode);n;n类实现代码如下:类实现代码如下:nCClientSocket:CClientSocket()nNikeName=;nmyDlg=0;nn/设置套接字的当
57、前窗口设置套接字的当前窗口nBOOL CClientSocket:SetDlg(CChatRoomClientDlg *tmp)nmyDlg=tmp;nreturn true;nnvoid CClientSocket:OnReceive(int nErrorCode) nmyDlg-GetMessage(); n3.添加设计对话框资源。添加设计对话框资源。n客户端程序有两个窗口,一个是登录窗口,客户端程序有两个窗口,一个是登录窗口,需要输入一些登录信息,另外就是聊天主需要输入一些登录信息,另外就是聊天主窗口。窗口。n聊天主窗口布局如下图所示:聊天主窗口布局如下图所示:控件类型及各自控件类型及各
58、自IDtab顺序顺序控件类型控件类型控件控件ID1ButtonIDOK2List BoxIDC_LIST_CHATBOX3EditIDC_EDIT_MESSAGE4ButtonIDC_BUTTON_SEND5StaticIDC_STATIC_NIKENAME6StaticIDC_STATICn利用利用ClassWizard,为列表控件为列表控件CDC_LIST_CHATBOX添加控制变添加控制变量量m_IDC_LIST_CHARBOX,用于显示聊天信,用于显示聊天信息。息。为静态文本控件为静态文本控件IDC_STAIC_NIKENAME增加增加控制变量控制变量m_IDC_STAIC_NIKEN
59、AME ,用于动,用于动态显示用户别名。态显示用户别名。为为IDC_EDIT_MESSAGE控件增加成员变量控件增加成员变量m_IDC_EDIT_MESSAGE,记录用户的发言。,记录用户的发言。n登录窗口是通过对话框资源添加进去的,其布局登录窗口是通过对话框资源添加进去的,其布局如下图所示。如下图所示。125436登录窗口各控件类型及各自登录窗口各控件类型及各自IDtab顺序顺序控件类型控件类型控件控件ID1ButtonIDOK2ButtonIDCCANCEL3StaticIDC_STATIC_NIKENAME4StaticIDC_STATIC_ADDRESS5EditIDC_EDIT_NI
60、KENAME6EditIDC_EDIT_ADDRESSn登录窗口的类名为登录窗口的类名为CConectDlg,利用,利用“建立建立类向导类向导”,为昵称编辑框控件添加为昵称编辑框控件添加CString类型的成员变量类型的成员变量m_IDC_EDIT_NIKENAME。为服务器地址编辑框控件为服务器地址编辑框控件IDC_EDIT_ADDRESS添加添加CString类型的成员变量类型的成员变量m_IDC_ADDRESS。4.登录对话框的支持类登录对话框的支持类CConectDlgn登录对话框类中要实现与服务器的连接。登录对话框类中要实现与服务器的连接。n由于要完成与服务器的连接操作必然离不开由于
61、要完成与服务器的连接操作必然离不开Socket,而为了,而为了在登录对话框销毁后,能够继续使用与服务器连接的在登录对话框销毁后,能够继续使用与服务器连接的Socket,因此在,因此在CConectDlg类的声明里直接定义了类的声明里直接定义了Socket指针形参,指针形参,以把以把Socket传回。类的声明如下:传回。类的声明如下:n类的声明如下:类的声明如下:nclass CConnectedDlg : public CDialognpublic:n CConnectedDlg(CClientSocket * tmp,CWnd* pParent = NULL); n CClientSocke
62、t * myServerSocket;n n;n类的实现如下:类的实现如下:nCConnectedDlg:CConnectedDlg(CClientSocket * tmp,CWnd* pParent /*=NULL*/): CDialog(CConnectedDlg:IDD, pParent)nnnmyServerSocket=tmp;nn而而“登录登录”按钮的响应函数按钮的响应函数OnOK()的实现代码如下:的实现代码如下:nvoid CConnectedDlg:OnOK() n/ TODO: Add extra validation herenUpdateData(TRUE); /控件控
63、件-数据成员数据成员nchar *nikename,*address;nint n;nif (!myServerSocket-Create()n myServerSocket-Close();n AfxMessageBox(网络创建错误!网络创建错误!);n return;nn n n=m_IDC_EDIT_ADDRESS.GetLength();naddress=new char(n+1);n /sprintf将内容格式化为字符串保存在将内容格式化为字符串保存在address中中nsprintf(address,%s,m_IDC_EDIT_ADDRESS.GetBuffer(n);naddr
64、essn=0;nn=m_IDC_EDIT_NIKENAME.GetLength();nnikename=new char(n+1);nsprintf(nikename,%s,m_IDC_EDIT_NIKENAME.GetBuffer(n);nnikenamen=0;n /向服务器向服务器6767端口请求连接。端口请求连接。nif (!myServerSocket-Connect(address,6767)n myServerSocket-Close();n AfxMessageBox(网络连接错误!网络连接错误!);n return;nnmyServerSocket-NikeName=nike
65、name;nCDialog:OnOK();n5. 实现在主对话框启动登录对话框实现在主对话框启动登录对话框n为了在主窗口出现之前,先启动登录对话框窗口,需要在为了在主窗口出现之前,先启动登录对话框窗口,需要在CChatRoomClientApp:InitInstance中添加代码,即在主对中添加代码,即在主对话框窗口构造之前先启动,如下所示:话框窗口构造之前先启动,如下所示:nCClientSocket curSocket; /事先定义了客户端套接字变量事先定义了客户端套接字变量nBOOL CChatRoomClientApp:InitInstance()nnCConnectedDlg cdl
66、g(&curSocket);nif (cdlg.DoModal()=IDCANCEL) return FALSE;n / curSocket中保存了在登录窗口中建立的通信套接字。中保存了在登录窗口中建立的通信套接字。nCChatRoomClientDlg dlg(&curSocket);nm_pMainWnd = &dlg;ncurSocket.SetDlg(&dlg);nint nResponse = dlg.DoModal();nif (nResponse = IDOK) nelse if (nResponse = IDCANCEL) n n5.在主对话框实现消息的发送在主对话框实现消息的
67、发送n在主对话框的在主对话框的“发送发送”按钮响应函数中,添加代码实现向服务器发送用按钮响应函数中,添加代码实现向服务器发送用户消息,代码如下:户消息,代码如下:nvoid CChatRoomClientDlg:OnButtonSend() nint n;nchar message1000;nUpdateData(TRUE); /控件控件-数据成员数据成员nm_IDC_EDIT_MESSAGE=myServerSocket-NikeName+: +m_IDC_EDIT_MESSAGE;nn=m_IDC_EDIT_MESSAGE.GetLength();nsprintf(message,%s,m
68、_IDC_EDIT_MESSAGE.GetBuffer(n);nmessagen=0;nif (myServerSocket-Send(message,n+1)n m_IDC_EDIT_MESSAGE=; n UpdateData(FALSE); /数据成员数据成员-控件控件nnelse AfxMessageBox(网络传输错误网络传输错误!); n6. 接收服务器消息的实现接收服务器消息的实现n本例中并没有直接采用本例中并没有直接采用CSocket类而是采用了一个类而是采用了一个CClientSocket类,它是自己重新定义的一个类,它是自己重新定义的一个CSocket类的派生类,其中有一个
69、很重要的成员函数,类的派生类,其中有一个很重要的成员函数,它实现了立即显示从服务器获得的聊天信息到聊天对它实现了立即显示从服务器获得的聊天信息到聊天对话框中的功能。核心代码如下:话框中的功能。核心代码如下:nvoid CClientSocket:OnReceive(int nErrorCode) nnmyDlg-GetMessage();nn它通过调用主对话框的成员函数它通过调用主对话框的成员函数GetMessage,显示,显示聊天信息,代码如下:聊天信息,代码如下:nBOOL CChatRoomClientDlg:GetMessage()nchar buff1000;nint count;n /接收服务器发送来的聊天信息。接收服务器发送来的聊天信息。 ncount=myServerSocket-Receive(buff,1000);nbuffcount=0;nm_IDC_LIST_CHATBOX_CONTROL.AddString(buff);nreturn true;n结束!结束!