VC++网络编程学习笔记

Visual C++网络编程是指用户使用MFC类库(微软基础类库)在VC编译器中,以实现网络应用。用户通过VC编程实现的
网络软件可以在网络中不同的计算机之间互传文件,图像等信息。
基础知识:
如果用户要进行VC网络编程,则必须首先了解计算机网络通信的基本框架和工作原理。在两台或多台计算机之
间进行网络通信时,其通信的双方还必须遵循相同的通信原则好数据格式。

1.OSI七层网络模型
OSI网络模型是一个开放式系统互联的参考模型。
发送信息的计算机 接收信息的计算机
7.应用层      7.应用层      表示计算机网络中的物理设备。常见的有计算机网卡等
6.表示层      6.表示层      将传输数据进行压缩与解压缩
5.会话层      5.会话层      将传输数据进行网络传输
4.数据传输层    4.数据传输层    进行信息的网络传输
3.网络层      3.网络层      建立物理网络的链接
2.数据链路层    2.数据链路层    将传输数据以某种格式进行表示
1.物理硬件层    1.物理硬件层    应用程序接口
发送方数据传输由高到低,接收方数据传输由低到高。各层数据对等通信。

2.TCP/IP协议
TCP/IP协议实际上是一个协议簇,其包含了很多协议。例如,FTP(文本传输协议),SMTP(邮件传输协议)等应用层协议。
TCP/IP协议的网络模型有4层:
数据链路层:网卡等网络硬件设备以及驱动程序
网络层: IP协议等互联协议
数据传输层:为应用程序提供通信方法,通常为TCP,UDP协议
应用层: 负责处理应用程序的实际用于层协议
在数据传输层中,包括了TCP和UDP协议。其中,TCP协议是基于面向连接的通信协议。其具有重发机制,即当数据破坏或者丢失时,发送方将重发该数据。而UDP协议是基于用户数据报协议,属于不可靠连接通信协议。(没有重发机制)

3.C/S编程模型
C/S编程模型是基于可靠连接的通信模型。在通信的双方必须使用各自的IP地址以及端口进行通信。否则,通信过程将无法实现。通常情况下,当用户使用C/S模型进行通信时,其通信的一方称为客户端,则另一端称为服务端。
服务器端等待客户端连接请求的到来,这个过程称为监听过程。通常,服务器监听功能是在特定的IP地址和端口上进行。然后客户端像服务器发送连接请求,服务器响应连接请求则连接成功。否则,客户端的链接请求失败。
由于客户端链接服务器时,需要使用服务器的IP地址和监听端口号才能完成连接。所以,服务器的IP地址和端口号必须是固定的。部分协议所使用的端口号:HTTP协议(网页浏览服务)所使用的端口号是80,FTP(超文本传输协议)端口号是21
注意:用户在实际编程中,通信双方的链接以及数据通信均是基于Socket(套接字)进行的

4.Socket套接字
网络应用程序可以使用MFC中封装的套接字类进行编程,也可以使用WindowsAPI函数进行程序开发。相比较而言,MFC网络编程比较简单一点,用户使用也比较方便。
用户在Windows中编写网络通信程序时,需要使用WindowsSockets(Windows套接字)。与Windows套接字相关的API函数称为Winsock函数。
在网络通信的双方,均有各自的套接字,并且该套接字与特定的IP地址和端口号相关联。通常,套接字主要有两种类型,分别是流式套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)。其中,流式套接字是专门用于使用TCP协议通信的应用程序中,而数据报套接字则是专门用于使用UDP协议进行通信的应用程序中。

5.网络字节顺序
网络字节顺序是指TCP/IP协议中规定的数据传输使用格式,与之相对的字节顺序是主机字节顺序。网络字节顺序表示首先将数据中最重要的字节进行存储。例如,当数据0x357451使用网络字节顺序进行存储时,该值在内存中的存放顺序将是0X35,.0x74,0x51。因为通信数据可能会在不同的机器之间进行传输,所以通信数据必须以相同的格式进行整理。只有经过格式处理的通信数据,才能在不同的机器之间进行传输。

6.Windows Sockets介绍
在MFC类库中,几乎封装了Windows Sockets的全部功能。介绍两个主要的套接字相关类,分别是CAsyncSocket类和CSocket类
微软基础库中,CAsyncSocket类封装了异步套接字的基本功能。使用该类进行网络数据传输的步骤如下:
1).调用构造函数创建套接字对象
2).如果创建服务器端套接字,则调用函数Bind()绑定本地IP和端口,然后调用函数Listen()监听客户端的请求。如果请求到来,则调用函数Accept()响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可。
3).调用Send()等功能函数进行数据传输与处理
4).关闭或销毁套接字对象。
注意:在MFC中,所有类中均有一个变量m_hWnd表示该类的实例句柄
CSocket类派生于CAsyncSocket类。该类不但具有CAsyncSocket类的基本功能,还具有串行化功能。用户在实际编程中,通过将CSocket类与CSocketFile类和CArchive类一起使用,能够很好地管理数据以及发送数据。使用该类进行网络编程的步骤如下:
1).创建CSocket类对象
2).如果创建服务器端套接字,则调用函数Bind()绑定本地IP和端口,然后调用函数Listen()监听客户端的请求。如果请求到来,则调用函数Accept()响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可。
3).创建与CSocket类对象相关联的CSocketFile类对象
4).创建与CSocket类相关联的CArchive对象
5).使用CArchive类对象在客户端和服务器之间进行数据传输
6).关闭或销毁CSocket类,CSocketFile类和CArchive类的三个对象

7.Socket套接字编程
套接字是由美国伯克利大学提出并设计的一种在网络中不不同主机之间进行数据交互的通信桥梁。在实际生活中,人么所使用的网络通信软件功能均是基于Socket套接字作为通信桥梁实现。所以,套接字在网络编程中,有着非常重要得作用。
在Socket套接字编程中,为了准确定位通信双方和数据传输的有效性,完整性,编程时必须使用统一的寻址方式和字节排序顺序。
1)寻址方式:因为套接字需要在各种网络协议中使用,所以为了区分程序所使用的网络协议必须使用同一的寻址方式。例如,在TCPIP协议通信中,用户使用IP地址和端口号进行确定通信双方。而在其他的协议中不一定也使用该方式确定通信双方。
在Winsock(Socket API)中,用户可以使用TCP/IP地址家族中统一的套接字地址结构解决TCP/IP寻址中可能出现的问题。该套接字地址结构定义如下:

1 struct sockaddr_in{
2   short sin_family;    //指定地址家族即地址格式
3   unsigned short sin_port; //端口号码
4   struct in_addr    sin_addr;    //IP地址
5   char    sin_zero[8];    //留作备用,需要指定为0
6 }

在这个结构中,成员sin_family指定使用该套接字地址的地址家族。在这里必须设置为AF_INET,表示程序所使用的的地址家族是TCP/IP
注意:该结构的最后一个成员并未实际使用,主要是为了与第一个版本的套接字地址结构大小相同而设置。在实际使用时,将这8个字节直接设为0即可。
该结构成员变量sin_addr表示32位的IP地址结构。其结构定义如下:

 1 struct in_addr{
 2   union{    //联合函数(公用一块内存,各成员中最长的为准)
 3   struct{
 4   unsigned char s_b1,s_b2,s_b3,s_b4;
 5   }S_un_b;    //用4个u_char字符描述IP地址
 6   struct{
 7   unsigned short s_w1,s_w2;
 8   }S_un_w;    //用2个u_short类型描述IP地址
 9   struct{
10   unsigned long S_addr; //用1个u_logn类型描述IP地址
11   }S_un;
12 };

通常我们在网络编程中使用一个u_long类型的字符进行描述IP地址,例如:

1 sockaddr_in addr;
2 addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.0")

在程序中,首先定义sockaddr_in结构对象addr,然后为IP地址结构in_addr中的成员S_addr赋值。因为结构成员S_addr所描述的IP地址均为网络字节顺序,所以程序调用inet_addr()函数将字符串IP转换为以网络字节顺序排列的IP地址。
2)字节顺序:在Socket套接字编程中,传输数据的排列顺序以网络字节顺序和主机字节顺序为主。通常情况下,如果用户将数据通过网络发送时,需要将数据转换成以网络字节顺序排序,否则可能造成数据损坏。如果用户是将网络中接收到的数据存储在本地计算机上,那么需要将数据转换成以主机字节顺序排列。
网络字节顺序将数据中最重要的字节首先进行存储,而主机字节顺序则将不重要的字节首先存储。
IP地址结构in_addr中的成员S_addr的值均是以网络字节顺序排列。
2.1)字节顺序转换函数:
在Winsock中提供了几个关于网络字节顺序与主机字节顺序之间的转换函数。函数定义如下:

1 u_short htons(u_short hostshort); //将一个u_short类型的IP地址从主机字节顺序转换到网络字节顺序
2 u_long htonl(u_long hostlong);    //将一个u_long类型的IP地址从主机字节顺序转换到网络字节顺序
3 u_long ntohl(u_long netlong)    //将一个u_long类型的IP地址从网络字节顺序转换到主机字节顺序
4 u_short ntohs(u_short netshort)    //将一个u_short类型的IP地址从网络字节顺序转换到主机字节顺序
5 unsigned long inet_addr(const char FAR*cp)    //将一个字符串IP转换到以网络字节顺序排列的IP地址
6 char FAR* inet_ntoa(struct in_addr in)    //将一个以网络字节顺序排列的IP地址转换为一个字符串IP

实例:

1 sockaddr_in addr;    //定义套接字地址结构变量
2 in_addr in_add;    //定能够以IP地址结构变量
3 addr.sin_family = AF_INET;    //定义地址家族为TCP/IP
4 addr.sin_port = htons(80);    //指定端口号
5 addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1")    //将字符串IP转换为网络字节顺序排列的IP
6 char address[] = inet_ntoa(addr.sin_addr.S_un.S_addr)    //将网络字节顺序排列的IP装换为字符串IP

在程序中,首先使用inet_addr()将字符串IP “127.0.0.1”转换为以网络字节顺序排列的IP地址结构成员S_addr中。然后,再使用函数inet_ntoa()将成员所表示的IP值转换成字符串IP

8.Socket相关函数
1)创建套接字
使用CSocket类创建套接字对象是通过该类的构造函数创建的。CSocket:CSocket()
创建CSocket类对象

1 CSocket sock;

如果用户需要创建套接字对象指针,则应该使用关键字new进行创建。

1 CSocket* sock = new CSocket

2)绑定地址信息

BOOL Bind(const SOCKADDR* lpSockAddr,int nSockAddrLen);

该函数的作用是将套接字对象与服务器地址结构绑定在一起。如果函数调用成功,则返回true。否则,返回false。参数lpSockAddr指定将要绑定的服务器地址结构,参数nSockAddrLen表示地址结构的长度。
在服务器端,当地址信息绑定套接字成功后,还需要调用函数Listen()在指定端口监听客户端的链接请求。

1 BOOL Listen(int nConnectionBacklog = 5)

参数nConectionBacklog表示套接字监听客户端请求的最大数目。

1 CSocket sock;    //创建套接字对象
2 sockaddr_in addr;    //定义套接字地址结构变量
3 in_addr in_add;    //定义IP地址结构变量
4 addr.sin_family = AF_INET    //指定地址家族为TCP/IP
5 addr.sin_port = htons(80)    //指定端口号
6 addr.sin_addr.S_un,S_addr = inet_addr("127.0.0.1")    //将字符串IP转换为网络字节顺序排列的IP
7 
8 sock.Bind((SOCKADDR*)addr,sizeof(addr))    //绑定套接字与地址结构
9 sock.Listen(5);    //监听端口

3)连接服务器
客户端创建套接字成功以后,可以调用函数Connect()向服务器发送连接请求。函数原型如下:

1 Bool Connect(const const SOCKADDR* lpSockAddr,int nSockAddrLen);

实例:

1 CSocket sock;    //创建套接字对象
2 sockaddr_in addr;    //定义套接字地址结构变量
3 in_addr in_add;    //定义IP地址结构变量
4 addr.sin_family = AF_INET;    //指定地址家族为TCP/IP
5 addr.sin_port = htons(80);    //指定端口号
6 addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");    //将字符串IP转换为网络字节顺序排列的IP
7 
8 sock.Connect((SOCKADDR*)addr,sizeof(addr))    //连接服务器

4)数据交换
无论是服务端,还是客户端都是通过函数Send()和Receive()进行数据交换。函数原型:

1 Virtual int Send(const void* lpBuf,int nBufLen,int nFlags = 0);
2 Virtual int Receive(void* lpBuf,int nBufLen,int nFlags = 0);

其中,函数send()用于发送指定缓冲区的数据,函数Recive()用于接收对方发送的数据,并将数据存放在指定缓冲区中。参数lpBuf表示数据缓冲区地址。参数nBufLenvia表示数据缓冲区的大小。参数nFlags表示数据发送或接收的标志,一般情况下,该参数均设置为0。例如,使用这两个函数进行数据的发送和接收。

1 char buff[] = '123456';
2 sock.Send(&buff,sizeof(buff),0)
3 sock.Receive(&buff,sizeof(buff),0)
4 Virtual int Receive(void* lpBuf,int nBufLen,int nFlags = 0);
5 Virtual int Receive(void* lpBuf,int nBufLen,int nFlags = 0);

5)关闭套接字对象

1 Virtual void close();sock.Close();    //关闭套接字对象套接字关闭的同时,也将服务器与客户端之间连接关闭了。

 

posted @ 2016-11-11 16:57  freshstraw  阅读(2535)  评论(0编辑  收藏  举报