第1章 Visual C++网络开发基本应用
Visual C++技术功能强大,在网络领域游刃有余,可以开发出很多网络应用。在本章的内容中,将详细介绍使用Visual C++技术开发基本网络应用的知识。本章介绍的都是最基本的应用内容,目的是为进入本书后面的学习打下良好的基础。
1.1 获取网卡的类型和MAC地址
网卡的类型可以从注册表中获得。MAC是Media Access Control的缩写,MAC地址也称为硬件地址,用来定义网络设备的位置。在OSI模型中,第三层(网络层)负责IP地址,第二层(数据链路层)则负责 MAC地址。因此一个主机会有一个IP地址,而每个网络位置会有一个专属于它的MAC地址。在本节的内容中,将讲解使用Visual C++技术开发一个获取MAC地址程序的方法。在具体编程之前,先讲解与之相关的基础知识。
1.1.1 Visual C++网络编程概述(1)
Visual C++(后面简写为VC)网络编程是指用户使用MFC类库(微软基础类库)在VC编译器中编写程序,以实现网络应用。用户通过VC编程实现的网络软件可以在网络中不同的计算机之间互传文件、图像等信息。本章将向用户介绍基于Windows操作系统的网络编程基础知识,其开发环境是VC。在VC编译器中,使用Windows Socket进行网络程序开发是网络编程中非常重要的一部分。
1. 网络基础知识
如果用户要进行VC网络编程,就必须首先了解计算机网络通信的基本框架和工作原理。在两台或多台计算机之间进行网络通信时,通信的双方还必须遵循相同的通信原则和数据格式。
接下来将首先向读者介绍OSI七层网络模型、TCP/IP协议以及C/S编程模型。
(1) OSI七层网络模型
OSI网络模型是一个开放式系统互联的参考模型。通过这个参考模型,用户可以非常直观地了解网络通信的基本过程和原理。OSI参考模型如图1-1所示。
图1-1 OSI七层网络参考模型 |
从如图1-1所示的OSI网络模型中可以看到网络数据从发送方到达接收方的过程中数据的流向以及经过的通信层和相应的通信协议。
事实上,在网络通信的发送端,其通信数据每到一个通信层,都会被该层协议在数据中添加一个包头数据。而在接收方恰好相反,数据通过每一层时,都会被该层协议剥去相应的包头数据。用户也可以这样理解--即网络模型中的各层都是对等通信的。在OSI七层网络模型中,各个网络层都具有各自的功能,如表1-1所示。
表1-1 各网络层的功能
协议层名 |
功能概述 |
物理硬件层 |
表示计算机网络中的物理设备。常见的有计算机网卡等 |
数据链路层 |
将传输数据进行压缩与解压缩 |
网络层 |
将传输数据进行网络传输 |
数据传输层 |
进行信息的网络传输 |
会话层 |
建立物理网络的连接 |
表示层 |
将传输数据以某种格式进行表示 |
应用层 |
应用程序接口 |
(2) TCP/IP协议
TCP/IP协议实际上是一个协议簇,其中包括了很多协议。例如FTP(文件传输协议)、SMTP(邮件传输协议)等应用层协议。TCP/IP协议的网络模型只有4层,包括数据链路层、网络层、数据传输层和应用层,如图1-2所示。
图1-2 TCP/IP网络协议模型 |
在TCP/IP网络编程模型中,各层的功能如表1-2所示。
表1-2 TCP/IP网络协议各层的功能
协议层名 |
功能概述 |
数据链路层 |
网卡等网络硬件设备以及驱动程序 |
网络层 |
IP协议等互联协议 |
数据传输层 |
为应用程序提供通信方法,通常为TCP、UDP协议 |
应用层 |
负责处理应用程序的实际应用层协议 |
在数据传输层中,包括了TCP和UDP协议。其中,TCP协议是基于面向连接的可靠的通信协议,它具有重发机制,即当数据被破坏或者丢失时,发送方将重发该数据。而UDP协议是基于用户数据报协议,属于不可靠连接通信的协议。例如当使用UDP协议发送一条消息时,并不知道该消息是否已经到达接收方,或者在传输过程中数据是否已经丢失。但是在即时通信中,UDP协议在一些对时间要求较高的网络数据传输方面有着重要的作用。
(3) C/S编程模型
C/S编程模型是基于可靠连接的通信模型。在通信的双方必须使用各自的IP地址以及端口进行通信。否则,通信过程将无法实现。通常情况下,当用户使用C/S模型进行通信时,其通信的任意一方称为客户端,则另一方称为服务器端。
服务器端等待客户端连接请求的到来,这个过程称为监听过程。通常,服务器监听功能是在特定的IP地址和端口上进行。然后,客户端向服务器发出连接请求,服务器响应该请求则连接成功。否则,客户端的连接请求失败。C/S编程模型如图1-3所示。
图1-3 C/S编程模型 |
由于客户端连接服务器时需要使用服务器的IP地址和监听端口号才能完成连接,所以,服务器的IP地址和端口必须是固定的。在这里,向用户介绍部分协议所使用的端口号码。例如,HTTP协议(用于网页浏览服务)所使用的端口号为80,FTP协议(用于文件传输)所使用的端口号是21。
2. 网络编程基础
可以使用MFC中封装的套接字类来编写网络应用程序,也可以使用Windows API函数进行程序开发。其中MFC网络编程比较简单,使用起来也非常方便。但是,使用MFC相关类编程会使用户对网络通信中的基本原理缺乏清晰的认识。而使用Windows API函数则恰好相反,可以使用户熟悉网络通信的基本原理。在实际编程过程中,通信双方的连接以及数据通信均是基于Socket(套接字)进行的。
(1) Sockets套接字
用户在Windows中编写网络通信程序时,需要使用Windows Sockets(Windows套接字)。与Windows套接字相关的API函数称为Winsock函数。
在网络通信的双方,均有各自的套接字,并且该套接字与特定的IP地址和端口号相关联。通常,套接字主要有两种类型,分别是流式套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)。其中,流式套接字专门用于使用TCP协议通信的应用程序中,而数据报套接字则专门用于使用UDP协议进行通信的应用程序中。
(2) 网络字节顺序
网络字节顺序是指TCP/IP协议中规定的数据传输使用格式,与之相对的字节顺序是主机字节顺序。网络字节顺序表示首先将数据中最重要的字节进行存储。例如,当数据0x358457使用网络字节顺序进行存储时,该值在内存中的存放顺序将是0x35、0x84、0x57。因为通信数据可能会在不同的机器之间进行传输,所以通信数据必须以相同的格式进行整理。只有经过格式处理的通信数据,才能在不同的机器之间进行传输。
3. 网络通信基本流程
要通过互联网进行通信,用户至少需要一对套接字,其中一个运行于客户机端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。根据网络通信的特点,套接字可以分为两类:流式套接字和数据报套接字。套接字之间的连接过程可以分为三个步骤,分别是服务器监听、客户端请求和连接确认。具体说明如图1-4所示。
图1-4 套接字之间的连接过程 |
4. 搭建开发环境
在Visual C++ 6.0环境下进行Winsock的API编程开发,需要在项目中导入以下三个文件,否则会发生编译错误。
WINSOCK.h:WINSOCK API的头文件,需要包含在项目中。
WSOCK32.lib:WINSOCK API链接库文件,使用时一定要把它作为项目的非默认的链接库包含到项目文件中去。
WINSOCK.dll:WINSOCK的动态链接库,位于Windows的安装目录下。
5. 两个常用的数据结构
套接字是网络通信过程中端点的抽象表示,在实现中以句柄的形式创建,包含了进行网络通信所必需的5种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址和远程进程的协议端口。
WinSock编程中常用的数据结构有sockaddr_in和in_addr。
(1) sockaddr_in结构
WinSock通过sockaddr_in结构对有关Socket的信息进行了封装:
- struct sockaddr_in {
- short sin_family;
- unsigned short sin_port;
- IN_ADDR sin_addr;
- char sin_zero[8];
- };
上述结构中各个参数的具体说明如下。
sin_family:指网络中标识不同设备时使用的地址类型,对于IP地址,它的类型是AF_INET。
sin_port:指Socket对应的端口号。
sin_addr:是一个结构,将IP进行了封装。
sin_zero:一个用来填充结构的数组,字符全为0,这个结构对于不同地址类型可以是相同的大小。
(2) in_addr结构
in_addr结构对IP地址进行了封装,既可以用4个单字节数表示,也可以转换为两个双字节数表示或一个四字节数表示。这样定义是为了方便使用,例如在程序中初始化IP时,可以传入4个单字节整数,而在函数间传递这个值时,可以将其转换成一个四字节整数使用。in_addr结构定义如下:
- struct in_addr {
- union {
- struct { u_char s_b1, s_b2, s_b3, s_b4; } S_un_b;
- struct { u_short s_w1, s_w2; } S_un_w;
- u_long S_addr;
- } S_un;
- };
6. Windows Sockets基础
在MFC类库中,几乎封装了Windows Sockets的全部功能。在接下来的内容中,将简单介绍两个最常用的套接字相关类--CAsyncSocket类和CSocket类。
(1) CAsyncSocket类
在微软基础类库中,CAsyncSocket类封装了异步套接字的基本功能。用户使用该类进行网络数据传输的步骤如下。
① 调用构造函数创建套接字对象。
② 如果创建服务器端套接字,则调用函数Bind()绑定本地IP和端口,然后调用函数Listen()监听客户端的请求。如果请求到来,则调用函数Accept()响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可。
③ 调用Send()等功能函数进行数据传输与处理。
④ 关闭或销毁套接字对象。
(2) CSocket类
CSocket类派生于CAsyncSocket类。该类不但具有CAsyncSocket类的基本功能,还具有序列化功能。用户在实际编程中,通过将CSocket类与CSocketFile类和CArchive类一起使用,能够很好地管理数据以及发送数据。用户使用该类进行网络编程的步骤如下。
① 创建CSocket类对象。
② 如果创建服务器端套接字,则调用函数Bind()绑定本地IP和端口,然后调用函数Listen()监听客户端的请求。如果请求到来,则调用函数Accept()响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可。
③ 创建与CSocket类对象相关联的CSocketFile类对象。
④ 创建与CSocketFile类相关联的CArchive对象。
⑤ 使用CArchive类对象在客户端和服务器之间进行数据传输。
⑥ 关闭或销毁CSocket类、CSocketFile类和CArchive类的3个对象。
1.1.2 MAC地址的原理
MAC意为介质访问控制。MAC地址是烧录在网卡(Network Interface Card,NIC)里的MAC地址,也叫硬件地址,是由48比特长(6字节)十六进制的数字组成。其中0~23位叫做组织唯一标志符(Organizationally Unique Identifier),是识别LAN(局域网)节点的标识;而24~47位是由厂家自己分配的。其中第40位是组播地址标志位。网卡的物理地址通常是由网卡生产厂家烧入网卡的EPROM(一种闪存芯片)中,它存储的是传输数据时真正赖以标识发出数据的电脑和接收数据的主机的地址。
在网络底层的物理传输过程中,通过物理地址来识别主机,它一般也是全球唯一的。例如以太网卡的物理地址是48bit(比特位)的整数,如44-45-53-54-00-00格式,以机器可读的方式存入主机接口中。以太网地址管理机构(IEEE,电气和电子工程师协会)将以太网地址(也就是48比特的不同组合)分为若干独立的连续地址组,生产以太网网卡的厂家就购买其中一组,在具体生产时,逐个将这些唯一地址赋予以太网卡。
由此可见,MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。在Windows操作环境下,依次选择"开始"→"运行",然后在"运行"对话框中输入"cmd",打开命令行窗口,输入"ipconfig /all"(注意ipconfig和/之间有一个空格),即可获取我们机器的MAC地址,如图1-5所示。
图1-5 输入ipconfig /all获取MAC地址 |