第1章 网络编程和开发环境
1.1 Visual C++ 2010开发平台
Microsoft Visual C++(简称Visual C++、MSVC、VC++或VC),是微软公司的C++开发工具,具有集成开发环境,可编辑编译C、C++及C++/CLI等语言。VC++整合了便利的排错工具,特别是整合了视窗应用编程接口(Windows API)、三维动画DirectX API及Microsoft .NET框架。目前最新的版本是Visual C++ 2010。
1.1.1 Visual Studio 2010安装
Visual C++一直以来都是Visual Studio系列开发工具套件的重要成员。Visual Studio是微软公司推出的著名产品,是目前最流行的Windows平台应用程序开发环境。Visual Studio 2010(简称VS 2010)于2010年4月12日上市,其集成开发环境(IDE)的界面被重新设计和组织,变得更加简单明了。Visual Studio 2010集成了Visual C++ 2010。
本书使用网上下载的Visual Studio 2010安装包:
cn_visual_studio_2010_ultimate_x86_dvd_532347.iso
这是个镜像(.iso)文件,需要虚拟光驱才能运行。用虚拟光驱软件DAEMON Tools Lite载入镜像,如图1.1所示。
图1.1 载入VS 2010安装.iso文件
载入后弹出如图1.2所示的启动窗口,单击“安装Microsoft Visual Studio 2010”,进入如图1.3所示的安装向导界面,单击“下一步”按钮继续。
图1.2 启动窗口
图1.3 Visual Studio 2010旗舰版安装向导
在图1.4所示窗口中选择“我已阅读并接受许可条款”,在图1.5所示窗口中选择“完全”,产品安装路径为默认的“C:\Program Files\Microsoft Visual Studio 10.0\”,单击“安装”按钮开始安装进程。
图1.4 接受安装许可条款
图1.5 选择安装路径
Visual Studio 2010包含Microsoft .NET平台的许多组件,如图1.6所示,安装程序会逐个安装它们,用户要等待一段时间。
图1.6 安装进行中
安装成功后,选择→“所有程序”→“Microsoft Visual Studio 2010”→,即可启动VS 2010。初次启动会弹出如图1.7所示的“选择默认环境设置”对话框,本书是Visual C++ 编程,故选择“Visual C++开发设置”,单击“启动Visual Studio”按钮进入开发环境。
图1.7 设置为Visual C++的开发环境
Visual Studio 2010的Visual C++集成开发环境的起始界面如图1.8所示。
图1.8 Visual C++ 2010集成开发环境
读者也可从网络获得Visual Studio 2010的可执行(非镜像)安装程序或者直接从光盘安装。
下面以编写一个简单的演示程序为例,使读者初步熟悉Visual Studio 2010环境下Visual C++开发的基本操作。
1.1.2 创建Visual C++项目工程
选择菜单命令“文件”→“新建”→“项目”,如图1.9所示。
图1.9 新建项目
系统弹出“新建项目”对话框(如图1.10所示),左边“项目类型”树中默认选项为“Visual C++”→“MFC”,对应右边“模板”选择“MFC应用程序”,给项目命名为“GetIPAndPort”(我们即将做的这个软件是用来演示程序如何获得用户输入的IP和端口号的——这也是几乎所有网络程序都具有的功能,所以取这个名字)。
图1.10 项目命名
单击“确定”按钮,弹出“MFC 应用程序向导”对话框(如图1.11所示),接下来我们将在这个对话框的指引下轻松完成创建VC工程的工作,单击“下一步”按钮继续。
图1.11 MFC应用程序向导
在“应用程序类型”界面(如图1.12所示)选中“基于对话框”单选按钮(这个程序很简单,用不着文档和视图),取消选择界面下方的“使用Unicode库”复选框(在本书所有程序建立工程的时候都要记得这一步,为程序兼容性考虑,避免字符串处理的麻烦),单击“下一步”按钮。
图1.12 选择应用程序类型
接下来的“用户界面功能”和“高级功能”界面(如图1.13所示)都采用系统默认设置,连续单击“下一步”按钮跳过。
图1.13 “用户界面功能”和“高级功能”默认设置
最后一步出现的是“生成的类”,稍留意下可以看到,系统已经自动为程序建立了两个类——CGetIPAndPortApp和CGetIPAndPortDlg(如图1.14所示),其中CGetIPAndPortApp类代表应用程序本身,CGetIPAndPortDlg类代表程序的主界面对话框。细心的读者可能会发现,这两个看似冗长的类名其中间部分“GetIPAndPort”就是我们刚才取的工程名!没错,以后大家就会发现一个规律:VC在一开始创建工程时都会默认生成两个类,名称形如CXXXApp和CXXXDlg(其中“XXX”部分就是用户指定的工程名),这样的命名法则是为了方便用户理清程序的类结构。
图1.14 生成的类
单击“完成”按钮,至此一个VC工程就创建完成了。
1.1.3 Visual C++可视化设计
开发环境工作区主界面将呈现的样子如图1.15所示。
图1.15 开发环境工作区主界面
主工作区大致分为三部分,最左边是供用户浏览程序结构的,包括好几个选项卡界面,常用的是解决方案资源管理器、类视图和资源视图,如图1.16所示。
图1.16 三个常用的视图
解决方案资源管理器以树形目录结构列出了程序中包含的所有代码文件,VC将它们分成三大类:头文件(.h)、源文件(.cpp)和资源文件。头文件主要对程序中用到的各种变量、常量、函数和类进行定义和声明;源文件是程序的主体部分,是各个函数、类的具体实现代码;资源文件定义程序运行要用到的各种资源,如图片、动画、声音、视频等,这对于一些功能丰富的软件(如多媒体类应用)是必不可少的。
类视图用树状结构展示了工程中所有 C++ 类及其层次结构,单击类名可在 Visual Studio 2010 环境界面右下角的“属性”窗口中设置对应的类,包括为其添加新的事件消息,重写某些方法的实现代码等。
资源视图分类列出了程序的所有资源,其中常用的是 Dialog 资源,这种资源是每一个具有GUI的程序都有的,双击资源ID号可以打开对应的界面设计工作区,就可以设计程序的图形界面了。
VC在创建工程时默认会生成两个类(本例是CGetIPAndPortApp和CGetIPAndPortDlg),图 1.16 解决方案资源管理器中的头文件 GetIPAndPort.h 和源文件 GetIPAndPort.cpp 对应CGetIPAndPortApp 类,它们一同构成了该类的源代码实现;同理,GetIPAndPortDlg.h 和GetIPAndPortDlg.cpp对应CGetIPAndPortDlg类,从中间类视图中也可以看到这两个类。
除此之外,读者会发现类视图里多了一个CAboutDlg类,它是VC自动创建的,叫做“关于GetIPAndPort”对话框,用来显示程序的版本信息。
在本例的三个类中,CAboutDlg类和CGetIPAndPortDlg类都有各自的对话框界面资源。资源视图中 Dialog 目录下有它们的 ID 号(对应的分别是 IDD_ABOUTBOX 和 IDD_GETIPANDPORT_DIALOG),双击ID号可以打开其对话框的界面设计工作区,如图1.17所示。
图1.17 “关于GetIPAndPort”对话框的设计工作区
工作区中显示的是“关于GetIPAndPort”对话框的默认界面,可以在此基础上自己设计或重新布局。
解决方案资源管理器、类视图和资源视图三者密切配合,将程序代码有机地组织成一个结构精巧的 C++项目,通过这三者可以清楚地看到程序中每个类的对应代码实体,用 C++写的类不再是抽象的代码,而是成为看得见、摸得着的现实存在,这就是可视化设计的魅力!
对话框界面的设计布局如Visual Basic一样方便:开发环境界面中央是主工作区,在这里可以打开任意多个程序文件(源文件或头文件)及对话框界面设计工作区;只要将右边工具箱中的控件直接拖曳到工作区中,就可以设计出自己想要的程序界面。VC环境下的工具箱和VB的一样,都包含了一般 Windows 程序通用的界面元素(按钮、文本框、静态标签、列表框、滚动条等),用过VB的人对这些常用控件肯定再熟悉不过了,故这里不做过多介绍。只是针对本书所介绍的网络编程,有一个控件需要特别提一下,那就是 IP 地址控件(如图1.18所示)。它在界面上的显示效果如图1.19所示。
图1.18 IP地址控件
图1.19 地址控件的显示效果
稍后将会介绍这个控件的具体用法,读者会看到它是个很实用的控件。
选择工具箱中的控件设计程序界面,可以看到VC界面设计环境的使用极其方便,丝毫不比VB逊色。在布局界面时可以使用工具栏中提供的功能调整各个控件的大小、对齐方式。如图1.20所示,先选择“关于”按钮控件,再选择“退出”按钮控件,然后单击工具栏中的“使大小相同”按钮,就可以使先选择的按钮与后选择的按钮大小一样。
图1.20 调整控件的大小
最终设计出的程序界面效果如图1.21所示。
图1.21 程序界面
在网络编程中,程序使用IP和端口来标识网络上的其他程序,以实现程序之间的通信(进程通信),因此获取并正确处理对方进程的IP和端口(通常由用户通过界面输入指定)就成为网络程序的通用功能。下面的小程序就用来演示这个最基本的功能,不过,它只负责获取和处理IP、端口号,并不实际发起网络连接。
1.1.4 一个简单的Visual C++小程序
这个程序的界面已经设计好了,但要让程序完成一定的功能,还必须为其编写代码。写代码之前,首先要定义程序中需要用到的一些变量。在VC中,很多变量都不是孤立的,而是与某个界面元素(即控件)绑定的,这样用户在界面上的输入就可以很容易地传递给程序中相应的变量进行处理。例如,为了在程序代码中获得用户输入的IP,需要给IP地址控件关联一个变量。如图1.22所示,右击该控件,在弹出的菜单中选择“添加变量”命令。
图1.22 给控件关联变量
出现“添加成员变量向导”对话框(如图1.23所示),将变量命名为“m_ip”,变量类别为“Control”。
图1.23 添加Control型变量
“Control”表示控件变量,它是微软对实现Windows程序的图形用户界面元素的一些类(类名如CEdit、CButton)的总称。这些类大多是由同一个叫做窗口(CWnd)的C++类衍生过来的, CWnd及其庞大的衍生类家族封装了Windows的GUI,作为MFC的重要组成部分,是微软对基础 C++语言的扩充,使其具有了强大的图形化界面功能。与 CWnd 家族中的类关联的变量即控件变量,可以通过这样的变量获取和控制它所关联控件的几乎一切行为,从而对程序界面进行灵活的编程和定制。
而那些仅仅只是获取用户输入值的变量称为值类型(Value)变量,这种变量与我们以前上C++语言课时接触的那些变量差不多,所不同的只是它们都有各自关联的控件,用于获取和保存特定控件接收的用户输入。
除了Control和Value这两大类变量外,用户在编程时也可以根据需要自定义临时变量,这种变量就是普通C++里用的那些变量(整型、实型、字符型之类)。
下面接着添加变量,给用于接收端口号的文本框关联Value变量strport(如图1.24所示)。
图1.24 添加Value型变量
再设置该文本框的Number属性为True(如图1.25所示),之所以这样设置,是为了限定用户只能在这个文本框中输入数字形式的端口号。这样设置之后,后面运行程序时读者会发现:如果试图输入非数字字符(中文、英文字母),则文本框一概不响应,也就杜绝了用户的非法输入。
图1.25 设置文本框Number属性
本程序还有一个文本框是用于显示程序获取的 IP 和端口的,给它关联 Control 型变量m_showIpAndPort,并且设置Read Only属性为True(作为显示信息窗口的文本框一般都设为只读模式),如图1.26所示。
图1.26 设置显示框Read Only属性
至于给控件关联的变量究竟是设成Control还是Value型,应视具体需要而定。一般来说,如果希望对控件的行为进行某种控制,设置变量为Control型较好;如果仅仅想获得控件接收的用户输入值,设置为Value型就足够了;有时候对控件的行为有较高的控制要求,同时又希望能够非常方便地获取控件的输入值,也可以对一个控件同时关联定义这两类变量。
现在,所有的变量都关联好了,界面控件也都进行了恰当的设置和布局,只剩下编码工作了。程序界面上的“获取”按钮是实现本程序功能的关键,编程工作主要就是给这个按钮添加事件处理程序。右击“获取”按钮,在弹出的功能菜单中选择“添加事件处理程序”命令,如图1.27所示。
图1.27 给“获取”按钮添加事件处理程序
在“事件处理程序向导”对话框中将这个处理程序命名为“OnShowIpAndPort”,如图1.28所示。
图1.28 给事件处理程序命名
单击“添加编辑”按钮,进入代码编辑窗口(如图1.29所示),VC自动打开需要编辑的程序代码文件 GetIPAndPortDlg.cpp 并定位到源文件中相应的位置,供用户添加自己的代码。以后在编译、调试程序时,也可随时先选中程序界面上的“获取”按钮,然后再在开发环境右下角“获取”按钮的属性设置窗口中选择这个事件处理过程。这样就可以随时随地定位到这个事件过程的源码编辑处,修改完善程序。需要指出的是,事件过程 OnShowIpAndPort 的代码必须填写在如图1.29中VC为我们指定的地方,写在其他任何地方或本工程的其他代码文件中都是无法运行的。
图1.29 编辑事件处理过程代码
为“获取”按钮编写的事件过程代码如下:
BYTE nFild[4]; //分别存放IP地址的四个字段 CString sip; //IP地址的字符串形式(可以直接显示在界面上的) UpdateData(); //刷新对话框界面,获取用户输入 //验证输入是否合法 if(m_ip.IsBlank()) //若用户没有填写IP地址,则提示填写 { AfxMessageBox("请填写IP地址!"); return; } if(strport.IsEmpty()) //若用户忘了指定端口号,则提醒其指定 { AfxMessageBox("请指定进程端口!"); return; } //获取用户输入的IP地址值 m_ip.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]); //将IP地址格式化为可以在计算机屏幕上显示的字符串 sip.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]); //在界面上显示用户输入的网络进程地址(包括所在主机的IP和端口) m_showIpAndPort.SetWindowTextA(sip + " :" + strport); m_ip.SetFocus(); //焦点回到IP地址栏
双击“关于”按钮,为其添加事件过程(如图1.30所示)。VC也会自动定位到需要添加代码处,事件处理过程函数自动命名为OnBnClickedButton2,表示在单击Button2(即“关于”按钮)时执行这个事件过程。
图1.30 添加“关于”按钮事件过程
“关于”按钮的Click事件代码如下:
CAboutDlg dlg; dlg.DoModal(); //显示“关于”对话框
由此可见,添加代码有两种方式:一种方式是右击控件后选择“添加事件处理程序”菜单项,另一种方式是直接双击该控件。所不同的是第一种方式可以自定义事件处理程序的函数名,如本例命名“获取”按钮的单击处理过程函数名为OnShowIpAndPort,这是个有意义的名字,表示单击“获取”按钮后,程序就会显示(Show)指定的“IP”和“端口(Port)”;而采用第二种方式添加的函数使用系统自动生成的函数名,如OnBnClickedButton2,虽然这个名称也有一定意义,表示事件过程是在单击(BnClicked)第二个按钮(Button2)时发生的,但并没有直观地表示出它要完成的功能。
对于小程序的开发,这两种方式都是可行的,并没有本质区别。但是如果程序规模稍大,事件处理过程较多的时候,就要注意了:采用第一种方式添加的函数由于其名称更有意义,将使得最终整个源程序的可读性很强,便于修改、维护,因此对于程序的重要功能函数尽量使用第一种方式添加;而第二种方式也不是完全没有用——对于一些不重要的函数(并非程序必要的核心功能),若都采用第一种方式添加,都要给它取一个有意义的名字,势必给编程工作造成不便,而且过多地增加有意义的函数名也容易造成命名混乱,反而降低了程序的可读性。在实际编程中,要根据具体情况灵活运用这两种方式。
至此,这个简单的VC小程序就编写完成了,下面来运行。
单击工具栏上的“启动调试”()按钮,程序界面如图1.31所示。
图1.31 启动程序界面
我们先不填写IP地址而直接单击“获取”按钮,看看会发生什么。程序弹出了消息框,提醒填写IP地址(如图1.32所示)。
图1.32 提醒填写IP地址
接下来填写IP地址时,我们故意将最后一个字段填成300(或任何其他大于255的十进制数),这时你会发现:IP地址控件会自动将它重置为255(因为值大于255的IP地址字段是不合法的),而且始终无法在IP地址控件中输入其他非数字字符(这就是使用IP地址控件的好处)。如果使用其他控件接收用户输入,用户就要自己编写代码来检查输入的合法性,IP 地址控件将这种烦琐的验证过程封装起来自动完成,给编写程序带来了极大的方便。
填写完合法 IP,再故意不填端口号,单击“获取”按钮后,程序同样也会弹出消息框,提醒输入端口号(如图1.33所示)。
图1.33 提醒填写端口
在IP和端口都合法填写的情况下,单击“获取”按钮,程序就会将用户输入的网络进程地址(IP+端口)显示在下方的输出文本框中,如图1.34所示。
图1.34 显示运行结果
这样,程序就完成了对用户输入网络进程地址的获取工作,在本书以后的程序示例中,这样的操作会经常用到。在获取了进程地址后,程序会进一步用这个地址作为参数传递给不同的网络编程接口函数,从而完成丰富的网络功能。
单击“关于”按钮,程序弹出“关于GetIPAndPort”对话框(如图1.35所示),这是一个版本声明对话框,每一个 Windows 程序几乎都有这样一个对话框,用于声明版本号和版权信息。对话框上显示的内容可以根据需要进行修改和自定义,这个小程序暂时保持默认内容。
图1.35 “关于GetIPAndPort”对话框
通过以上这个简单的小程序,读者已经初步熟悉了本书要使用的开发平台,但要进行网络编程还必须对一些基本概念有所了解,下面就来介绍。