📄 14.4.2 客户端程序 ..txt
字号:
14.4.2 客户端程序 .
在VC++开发环境中,一个工作区可以包含多个工程。下面我们在已有的TcpSrv工作区(如图 14.7
所示)中添加客户端工程。
添加方法是在工作区名称上单击鼠标右键,选择【Add New Project to Workspace... >菜单命
令,这时将显示新工程创建向导,与上面创建的服务器端程序: TCPSrv一样,我们也创建一个
Win32 Console Apptication类型的应用程序,并调整这个新工程的目录,将默认路径中的最后
一级目录: TcpSrv去掉,使新创建的这个客户端应用程序与上面的TcpSrv工程在同一个目录下。
本次创建的客户端向用程序的名称为TcpClient.并在工程创建向导的第 l步选择An empty
project选项,以创建一个空的工程。
这时,在VC++集成开发环境中,在FileView选项卡就可以看到在TCPSrv这个工作区中有两个工程,
如图 14.8所示。
那么如何区分当前处于活动状态的是哪个工程呢?这可以通过 VC++开发环境提供的
Build工具条中提供的工程名称下拉列表来选择当前活动工程。在VC++开发环境的初始界
面中是看不到这个工具条的,读者可以在 VC++开发环境的工具栏空白处单击鼠标右键,
将看到初始选择的是Build MiniBar工具条,如图 14.9所示。可以将这个选项取消,然后选中
Build选项,此时在VC++开发环境中就可以看到如图 14.10所示的Build工具条上的工程下拉列表
了,通过这个列表,就可以很方便地切换工作区中的当前活动工程了。
同样,为客户端程序增加一个C++源文件: TcpClient.cpp.并添加如例14-2所示内容。 例14-2
#include <Winsock2.h>
#include <stdio.h>
void main() {
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1 , 1 );
err WSAStartup( wVersionRequested, &wsaData );
i f ( err ! = 0 ) {
return;
if ( LOBYTE( wsaData.wVersion ) != 1 II
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
//创建套接字
SOCKET sockClient=socket(AF_INET, SOCK_STREAM, O);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1") ;
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
//向服务器发出连接请求
connect(sockClient , (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//接收数据
char recvBuf[100);
recv(sockClient , recvBuf , 100 , O);
printf("%S\n" , recvBuf) ;
//发送数据
send(sockClient , "This is lisi" , strlen("This is lisi")+1, 0);
//关闭套接字
closesocket(sockClient) ;
WSACleanup();
因为在利用套接字编写网络应用程序时,都需要使用套接字库提供支持,所以加载套
接字库这部分代码可以重用。读者可以直接复制 TCPSrv工程中已有的这部分代码,然后按照上
面介绍的基于 TCP(面向连接)的 socket编程的客户端程序流程完成该程序。
①创建套接宇 (socket>。
同样,利用 socket函数创建套接字,并且该函数的第一个参数只能用 AF_INET(或 PF_INET);
因为本例是基于 TCP的网络应用程序,所以第二个参数是 SOCK_STREAM;第三个参数设置为 0.
让其自动选择一个合适的协议。
CI1向服务器发出连接请求 (connect)。
对于客户端来说,它不需要绑定,可以直接连接服务器端。这可以通过调用 connect函数与服务
器端建立一个连接,于是首先定义一个地址结构体 (SOCKADDR_IN)变量: addrS凹,并对其成员
进行赋值,设定服务器端的 IP地址和端口。因为本例中服务器端和客户端都是本地的,所以可
以使用一个特殊的 E地址: 127.0.0.1,这是本地回路地址,无论机器上是否有网卡,都可以使
用这个 IP地址。如果编写的网络程序就是在本地机器上运行的,就可以采用这个 IP地址进行一
些测试。如果编写的程序需要在两台机器上进行通信,此时就应设置服务器端程序所在机器上的
IP地址。要注意. addrSrv.sin_addr.S_ un.S_addr成员需要 u_long类型的数值,因此应该使用
inet_addr函数将点分十进制表示的 IP地址转换为 u_long类型,接着将 addrSrv的 sin_famiú
ly成员设置为 AF_INET;最后将端口成员 (addrSrv.sin_port)设定为服务器程序等待接收客户
端连接请求的那个端口,注意,这里的端口需要与服务器端使用的端口号保持一致,而且需要使
用网络字节顺序。设置完 addSrv变量的各个成员之后,就可以调用 connect函数建立连接了。
③和服务器端进行通信( send/recv )。
当建立连接之后,就可以接收服务器端发送的数据了,本例定义一个有 100个字节的字符数组:
recvBuf.用来接收服务器发送的数据,然后调用 recv函数接收数据。之后调用 p由ltf函数将接
收到的数据打印出来,并且在打印时,在接收到的数据后面加上一个"\0"字符,即打印一个回车
符。
然后,客户端可以调用 send函数向服务器端程序发送数据,注意,这时使用的套接字与上面 recv
函数使用的是一样的,并且在发送时也多发送一个字节,服务器接收到数据后可以将最后一个字
节的数据设置为"\0。",表示字符串的结束。
④关闭套接字。当通信完成之后,需要调用 closesocket函数关闭套接字,释放为此套接字分配
的资源。最后需要调用 WSACleanup函数,终止对套接字库的使用。
同样,客户端程序也需要链接库文件: ws2_32 .l ib。需要注意的是,这时工作区中有两个工程,
因此在设置工程属性时,在工程设置对话框的左边面板中一定要选中当前要进行设置的工程。读
者可以按照上面编写服务器端程序时介绍的添加库文件的方法,为 TcpClient工程添加该库文件
的链接。
最后利用 Build命令生成 TCPClient程序。
这时,基于 TCP的服务器端和客户端程序都已经生成了。注意:服务器端程序总是应先启动。那
么我们首先运行服务器端程序,然后再运行客户端程序,可以看到客户端程序收到了服务器端返
回的信息: Welcome 127.0.0.1 to http://www.sunxin.org;而服务器端程序
收到了客户端程序发送的信息 :
This is lisi。如图 14.11所示。
图 14. 11基于 TCP的网络应用程序运行结果
如果这时服务器端程序和客户端程序是在两台机器上运行,在客户端收到的信息中就会显示客户
端程序所在机器上的 E地址。
从图 14.11可以看到,这时客户端程序已经退出了,但服务器端程序仍在运行。这是因为在实现
服务器程序时采用了一个死循环,当它为一个客户端提供服务之后,服务器端仍继续监听下一个
客户端的请求,所以客户端程序可以不断地启动,服务器端程序都可以为它们提供服务。读者可
以自行测试这种情况。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -