网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读

源代码在线查看: 14.4 基于tcp的网络应用程序的编写及服务端程序.txt

软件大小: 956 K
上传用户: kzdai22
关键词: TXT VC 目录
下载地址: 免注册下载 普通下载 VIP

相关代码

				14.4 基于 TCP的网络应用 
				14.4.1 服务器端程序
				首先,利用 VC++集成开发环境新建一个 Win32 Console Application类型的应用程序,程序取
				名为: TCPSrv,并在应用程序创建向导的第 1步选择: An empty project选项,以创建一个空的
				工程。
				然后利用【文件\新建】菜单命令,为 TCPSrv工程添加一个 C++源文件: TcpSrv.cpp, 并按照上
				面讲述的基于 TCP(面向连接〕的 socket编程中的服务器端程序流程,编写具体的实现代码,结
				果如例 14-1所示。
				在使用之前须链接库函数:工程->设置->Link->输入ws2_32.lib,OK!
				#include  
				#include  
				void main() { 
				//加载套接字库 
				WORD wVersionRequested; 
				WSADATA wsaData; 
				ìnt err; 
				
				wVersionRequested = MAKEWORD( 1 , 1 ); 
				err = WSAStartup( wVersionRequested, &wsaData ); 
				if ( err != 0 ) { 
				return; 
				
				if ( LOBYTE( wsaData.wVersion ) != 1 || 
				HIBYTE( wsaData.wVersion ) != 1 ) { 
				WSACleanup( ); 
				return; 
				
				//创建用于监听的套接字 
				SOCKET sockSrv=socket(AF_INET, SOCK_STREAM, 0); 
				SOCKADDR_IN addrSrv; 
				addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY) ; 
				addrSrv.sin_family=AF_INET; 
				addrSrv.sin_port=htons(6000) ; 
				
				//绑起套接字 
				bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); 
				//将套接字设为监听模式,准备接收客户请求 
				listen(sockSrv, 5); 
				SOCKADDR_IN addrClient; 
				int len=sizeof(SOCKADDR); 
				
				while(1) 
				//等待客户请求到来
				SOCKET sockConn=accept(sockSrv, (SOCKADDR*)&addrClient , &len); 
				char sendBuf[100]; 
				sprintf (sendBuf, "Welcome %s to http://www.sunxin.org" , inet_ntoa(addrClient.sin_addr)); 
				
				11发送数据 
				send(sockConn , sendBuf , strlen(sendBuf)+1 , 0) ;
				 
				char recvBuf[lOOl ; 
				11接收数据 
						char recvBuf[100];
				recv (sockConn, recvBuf , 100 , 0) ; 
				11打印接收的数据 
				printf ("%S\n " , recvBuf) ; 
				11关闭套接字 
				closesocket(sockConn) ; 
				E 小技巧:如果程序俐的缩进比较混乱,可以先将需要调整的代码全部选
				中,然后同时按下 ALT和 F8键,代码就会变得整齐了。
				在 TcpSrv.cpp源文件,以及本章随后的几个示例程序中,因为需要使用 WinSock库中的函数,
				因此需要包含头文件: Winsock.h,程序中还使用了 C语言中标准输入和输出函数,因此还
				需要包含标准输入输出头文件: stdio.h.因此在如例 14-1所示代码中,首先在该源文件开始位置
				添加 语句: 
				#include  
				#include < stdio.h>
				
				' 
				接着,编写一个 main函数。在此函数中,首先定义了一个 WORD类型的变量 : 
				wVersionRequested,用来保存 WinSock库的版本号,接着调用 MAKEWORD宏创建一个包含了请求
				版本号的 WORD值,之后,调用 WSAStartup函数加载套接字库,如果其返回值不等于 0,则程序
				退出。接下来,判断 wsaData. wVersion的低字节和高字节是否都等于1,如果不是我们所请求
				的版本,那么调用 WSACleanup函数,终止对 Winsock库的使用并返回。
				加载套接字库之后,就可以按照上面讲述的基于 TCP (面向连接)的 socket编程中的服务器端程
				序流程来编写实现代码:
				(1)创建套接字 (socket)。
				利用 socket函数创建套接字。首先定义了一个 SOCKET类型的变量: sockSrv,用来接收 socket
				函数返回的套接字。对 socket函数来说,第一个参数只能是 AF_INET (或 
				PF_INET);本例是基于 TCP协议的网络程序,需要创建的是流式套接宇,因此将 socket
				函数的第二个参数设置为 SOCK STREAM;将其第三个参数指定为 0,这样的话,该函数
				将根据地址格式和套接字类别,自动选择一个合适的协议。
				①将套接宇绑定到一个本地地址和端口上 ( bind )。
				在创建了套接字之后,应该将这个套接字绑定到本地的某个地址和端口上。这时就需要调用 bind
				函数。在绑定之前,定义了一个 SOCKADDR IN类型的变量: addrSrv,然后对该地址结构体变量
				中的成员进行赋值。首先对该变量的 sin_addr.S_un.S_addr成员赋值,该成员需要 u_long类型。
				前面己经提过,在 SOCKADDR_IN结构体中,除了 sa_family成员以外,其他成员都是按网络字节
				顺序表示的。因此为了将 INADDR_ANY值转换为网 
				络字节顺序,需要使用 htonl函数,该函数将一个 u_long类型的值从主机字节顺序转换为 
				TCP_IP的网络字节顺序。要注意的是,这里不作转换也是可以的,因为 INADDR_ANY这个宏的值就
				是 0,所以转换为网络字节顺序之后其值还是 0,但这里仍然建议要作这个转换,主要是表明这
				里使用的是网络字节顺序,同时给程序员也作一个提醒:接下来,代码指定 sin_fanùly成员的值,
				该字段只能指定为 AF_INET;最后是指定端口 :sm-PORT字段的值,前面已经提过,为所编写的网
				络程序指定端口号时,要使用 1024以上的端口号,本例使用 6000这个端口号。同时要注意,这
				里需要的是网络字节顺序,并且因为端口号是两个字节的,所以.需要调用 htons函数来转换转
				换。读者一定要注意 htons与 htonl这两个函数之间的区别,如果利用 htons将一个 int或 long
				型数值转换为网络字节顺序,可能会丢失数据。
				接下来,调用 bind函数把套接字 sockSrv绑定到本地地址和指定的端口号上。该函数的第一个
				参数就是将要绑定的套接字:第二个参数需要一个指针,可以用取地址符来实现,并且 addrSrv
				变量是 SOCKADDR IN结构体类型,而这里需要的是 SOCKADDR*类型,所以需要进行强制类型转换。
				注意=对 SOCKADDR类型来说,其大写和小写是一样的,如果我们在其上单击鼠标右键,选择【 00 
				To Defmition Of SOCKADDR】菜单项即可定位于该类型的定义处,其定义代码如下所示 (位于 
				WinSock2 .h文件中): 
				typedef struct sockaddr SOCKADDR; 
				而 bind函数的第三个参数是指定地址结构的大小,可以利用 sizeof操作符来获取。 
				3将套接字设为监昕模式 ( listen ),准备接收客户请求。
				绑定之后,接下来应该调用 listen函数,将己绑定的套接字设置为监昕模式。该函数
				第一个参数就是将要设置的套接字,第二个参数是等待连接队列的最大长度,本例将此参数设置
				为 5.
				④等待客户请求到来:当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字 
				(accept)。
				接下来,需要调用 accept函数等待并接受客户的连接请求。此时,需要定义一个地址结构体 
				SOCKADDR IN的变量(本程序中即 addrC lient变量),用来接收客户端的地址信息。另外,因为
				对 accept函数的第三个参数来说,在调用这个函数之前必须为它赋予一个初始值,就是 
				SOCKADDR_IN结构体的长度,否则调用会失败。所以,上述代码定义了一个 int类型的变量: len,
				并将其初始化为 SOCKADDR_剧结构体的长度。
				因为作为服务器端,它需要不断地等待客户端的连接请求的到来,所以,程序接着进入一个循环,
				而且是一个死循环,让服务器端程序能够不断地运行。在此循环中,第一步就是调用 accept函
				数等待并接受客户的连接请求,其中第一个参数是处于监昕状态的套接字:第二个参数利用 
				addrClient变量接收客户端的地址信息。当客户端连接请求到来时,该函数接受该请求,建立连
				接,同时它将返回一个相对于当前这个新连接的一个套接字描述符,保存于 sockConn变量中,
				然后利用这个套接字就可以与客户端进行通信了,而我们先前的套接字仍继续监昕客户端的连接
				请求。 
				5用返回的套接字和客户端进行通信 ( sendlrecv)。此时,可以利用 send函数向客户端发送数
				据。为了发送数据,首先定义了一个字符数
				组,并将客户端的地址进行格式化处理后放到这个数组中。在格式化客户端的地址时,使用了 
				inet_ntoa函数,该函数接受一个in addr结构体类型的参数并返回一个以点分十进制格式表示的
				IP地址字符串,而SOCKADDRIN结构体中的sinaddr成员是in addr类型的,正好可以作为参数传递。
				接下来就可以调用send函数向客户端发送数据了,注意这个函数使用的套接字,这里需要使用已
				建17-连接的那个套接字: sockConn,而不是用于监昕的那个套接字: addrSrv 0 另外,发送数
				据的长度可以用strlen函数获得,但上述程序在发送数据时,多发送了一个字节,主要是为了让
				接收端在接收数据后,可以在该数据字符串之后增加一个"\0。"结尾标志。
				在发送完数据之后,还可以从客户端接收数据,这可以使用recv函数,应注意,该函数的第一个
				参数也应该是建立连接之后的那个套接字: sockConn。并且定义一个字符数组: recvBuf,用来
				保存接收的数据。
				当接收到数据之后,利用 printf函数将其显示出来。上述代码调用 printf函数时在接收到的数
				据之后加上一个 "\n"字符,这将打印一个换行符。
				⑥返回,等待另一客户请求。
				当前通信完成之后,需要调用closesocket函数关闭己建立连接的套接字,释放为该套接宇分配
				的资源。然后进入下一个循环,等待另一客户请求的到来。
				⑦关闭套接字。本例是一个死循环,如果不是一个死循环的话,这时在closesocket函数调用之
				后还需要关闭监听套接字,并调用WSACleanup函数终止对套接字库的使用。
				因为本程序使用了WinSock库中的函数,按照、动态链接库的使用方法之一,这时还需要为程序
				链接相应的.lib文件,关于原因将在后面的动态链接库一章会详细讲解。这里,需要为本程序链
				接相应的库文件: ws2_32.lib.方法是选择【Project\Setting...】菜单项,并在弹出的Project 
				Setting对话框上选择Link选项卡,然后在Objectl1ibrary modules编辑框中添加ws232.lib文件
				(如图 14.6所示)。读者应注意,输入的库文件与前面的库文件之间一定要有一个空格。 
				图 14.6添加ws2_32.lib文件的链接最后利用Build命令,生成TCPSrv执行程序。
							

相关资源