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类的三个对象spa
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(); //关闭套接字对象套接字关闭的同时,也将服务器与客户端之间链接关闭了。