visual c++ 网络通信编程

36
Visual C++ 网网网网网网 2007 网 8 网

Upload: truda

Post on 22-Jan-2016

170 views

Category:

Documents


0 download

DESCRIPTION

Visual C++ 网络通信编程. 2007 年 8 月. Visual C++ 网络通信编程. 一、网络编程的基本概念 二、 WinSocket 编程基础 三、 VC.net 网络编程 四、网络编程实例分析. 一、网络编程的基本概念. 网络协议 网络通信的特性 小结. 1. 网络协议. IPV4. IP ( Internet Protocol ) TCP ( Transmission Control Protocol ) UDP ( User Datagram Protocol ) IPX/SPX 协议 NetBIOS 协议 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Visual    C++ 网络通信编程

Visual C++ 网络通信编程

2007 年 8 月

Page 2: Visual    C++ 网络通信编程

一、网络编程的基本概念

二、 WinSocket 编程基础

三、 VC.net 网络编程

四、网络编程实例分析

Visual C++ 网络通信编程

Page 3: Visual    C++ 网络通信编程

一、网络编程的基本概念

1. 网络协议

2. 网络通信的特性

3. 小结

Page 4: Visual    C++ 网络通信编程

1. 网络协议

IP ( Internet Protocol )

TCP ( Transmission Control Protocol )

UDP ( User Datagram Protocol )

IPX/SPX 协议 NetBIOS 协议 AppleTalk 协议 ATM 异步传输模式协议

IPV4

IPV6 ( 1 6 个字节)

面向连接和无连接的通信模式

统一的名字解析及注册方法

Page 5: Visual    C++ 网络通信编程

1. 网络协议

表示层

会话层

传输层( TCP/UDP )

网络层( IP )

数据链路层

网络层

物理层

为用户提供相应的界面,以便使用提供的连网功能

完成数据的格式化

控制两个主机间的通信链路(开放、操作和关闭)提供数据传输服务(可靠或不可靠)在两个主机之间提供一套定址 / 寻址机制,同时负责数据包的路由选择

控制两个主机间的物理通信链路:同时还要负责对数据进行整形,以便在物理媒体上传输

控制两个主机间的物理通信链路:同时还要负责对数据进行整形,以便在物理媒体上传输物理媒体负责以一系列电子信号的形式,传出数据

Page 6: Visual    C++ 网络通信编程

2. 网络通信的特性—— 主要讲 WinSock 的通信特性

( 1 )面向协议 对每个离散写命令来说,如果传送协议把它们(而且只有它们)当做一条独立的消息在网上传送,就可以认为该协议是面向协议的。

数据报服务, UDP 、 IPX 流服务, TCP 、 NETBIOS

128

256

128

256

128

128

256

128128

512

Page 7: Visual    C++ 网络通信编程

( 2 )面向连接和无连接

2. 网络通信的特性

A 、面向连接的服务中,进行数据交换之前,必须与通信方建立一条路径。这样既确定了通信方之间存在路由,又保证了通信双方都是活动的、都可彼此响应,但其特点是在通信双方之间建立一个通信信道需要很多开支。

B 、无连接协议不保证接收端是否正在收听。无连接服务类似于邮政服务:发信人把信装入邮箱即可。

特点:数据可靠、有次序、网络资源占有少。

Page 8: Visual    C++ 网络通信编程

2. 网络通信的特性

从容关闭只出现在面向连接的协议中。在这种关闭过程中,一方开始关闭通信会话,但另一方仍然可以读取线上或网络堆栈上已挂起的数据。如果面向连接的协议不支持从容关闭,只要其中一方关闭了通信信道,都会导致连接立即中断,数据丢失,接收端不能读取数据这些情况出现。

( 3 )从容关闭

使用 T C P 的关闭过程

( 1 ) FIN

( 2 ) ACK

A B

( 3 ) FIN 、 ACK

Closesocket ( SOCKET )

OnClose ( SOCKET )

SOCKET 类 :

CAsyncSocket 类 :

Page 9: Visual    C++ 网络通信编程

TCP 套接字的关闭

Page 10: Visual    C++ 网络通信编程

2. 网络通信的特性

( 4 )广播数据 广播数据即数据从一个工作站发出,局域网内的其他所有工作站都能收到它。这一特征适用于无连接协议,因为L A N 上的所有机器都可获得并处理广播消息。

对于有连接协议,采用以下方式实现广播:

客户端客户端11

客户端客户端11

服务器端服务器端服务器端服务器端

客户端客户端44

客户端客户端44

客户端客户端33

客户端客户端33

客户端客户端 22客户端客户端 22

Page 11: Visual    C++ 网络通信编程

2. 网络通信的特性

( 5 )路由选择

如果协议可路由,就可在两个工作站之间建立一条成功的通信路径(要么是面向连接的回路,要么是数据报的数据路径),不管这两个工作站之间存在的网络硬件是什么。比如,机器 A 和机器 B分别在各自的子网中。连接两个子网的路由器 A 把这两台机器分开了。一个可路由的协议意识到机器 B和机器 A 不在同一个子网上:它就会把数据包定向到路由器,由路由器来决定数据的最佳转发方式,以便数据能抵达机器 B。 N e t B E U I 是 Wi ndows平台支持的唯一的一个协议,它不能路由。

Page 12: Visual    C++ 网络通信编程

2. 网络通信的特性( 6 )定址

客户机需要通过 T C P 或 U D P 和服务器通信时,必须指定服务器的 I P地址和服务端口号。

服务器只制定端口地址。

IP

struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }

struct sockaddr_in6 { short sin6_family; u_short sin6_port; u_long sin6_flowinfo; struct in6_addr sin6_addr; u_long sin6_scope_id; }; struct sockaddr_in6 { short sin6_family; u_short sin6_port; u_long sin6_flowinfo; struct in6_addr sin6_addr; u_long sin6_scope_id; }; struct sockaddr_in6 { short sin6_family; u_short sin6_port; u_long sin6_flowinfo; struct in6_addr sin6_addr; u_long sin6_scope_id; };

struct sockaddr_in6 {short sin6_family; u_short sin6_port; u_long sin6_flowinfo; struct in6_addr sin6_addr; u_long sin6_scope_id; }

IPV4 的地址结构 IPV6 的地址结构

Page 13: Visual    C++ 网络通信编程

2. 网络通信的特性ATM :typedef struct sockaddr_atm {u_short satm_family;ATM_ADDRESS satm_number;ATM_BLLI satm_blli;ATM_BHLI satm_bhli; }

红外线:typedef struct _SOCKADDR_IRDA {u_short irdaAddressFamily;u_char irdaDeviceID[4];char irdaServiceName[25]; }

Bluetooth :typedef struct _SOCKADDR_BTH {USHORT addressFamily;BTH_ADDR btAddr;GUID serviceClassId;ULONG port; }

Page 14: Visual    C++ 网络通信编程

在开发网络应用程序时,可从现有的大量协议中选出自己需要的,用来作为应用程序的基础。如果应用程序需要依靠特定协议进行通信,选择余地就有限;但是,如果想从头开发一个应用程序, T C P / I P 就是首选协议之一,至少从支持能力和微软的赞助这一角度来看,是这样的。 A p p l e Ta l k和 I P X / S P X 都囊括在微软的操作系统中,以便提供与其他操作系统的兼容性,同时,它们也是网络编程的重要元素。除此以外,在微软的 T C P / I P 实施方案中找到错误时,马上可以得到错误修复的响应,反之,其他协议中的错误要得到修复的话,可能就不那么幸运了(也许还是),这要看具体情况。 因此,在选择协议时,如果想把它用于网络编程, T C P / I P 是最安全的选择。除此以外,微软一直对 AT M 网络提供强大的技术支持。并增加了蓝牙、红外线传输的支持,而对N e t B I O S 的支持已经极弱 。

3. 小结

Page 15: Visual    C++ 网络通信编程

二、 WinSocket 编程基础

1.WinSocket简介2.WinSocket1.1 ( 1 )编程基本原理 ( 2 )主要函数说明 ( 3 )一个简单的有连接程序 ( 4 )一个简单的无连接程序

3.WinSocket2.2介绍

Page 16: Visual    C++ 网络通信编程

90年代初,由 Microsoft联合了其他几家公司共同制定了一套 WINDOWS下的网络编程接口,即 Windows Sockets规范,它不是一种网络协议 , 而是一套开放的、支持多种协议的Windows下的网络编程接口。主要借鉴从 U n i x平台的 B e r k e l e y 套接字接口。

1.WinSocket1.WinSocket 简介简介

现在的 Winsock 已经基本上实现了与协议无关。

Page 17: Visual    C++ 网络通信编程

1.WinSocket1.WinSocket 简介简介编程的一些概念:•同步 (Sync) 发送方不等接收方响应,便接着发下个数据包的通信方式。•异步 (Async) 发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式。•阻塞 (Block) 是指执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上。•非阻塞 (Unblock) 是指执行此套接字的网络调用时,不管是否执行成功,都立即返回。

Page 18: Visual    C++ 网络通信编程

2.WinSocket1.1

TCP/IP 协议的特点:提供双向的流数据、可靠、有次序、不重复的资料传送。 UDP 提供双向的数据报通信,但没有可靠、有次序、不重复的保证,所以 UDP 传送数据可能会收到无次序、重复的资料,甚至资料在传输过程中出现遗漏。 由于 UDP Socket 在传送资料时,并不保证资料能完整地送达对方,所以绝大多数应用程序都是采用 TCP处理 Socket ,以保证资料的正确性。

WinSocketWinSocket 编程通常使用编程通常使用 TCP/IPTCP/IP 和和UDP/IPUDP/IP 协议。协议。

Page 19: Visual    C++ 网络通信编程

( 1 ) TCP( 面向连接 ) 的编程基本原理

调用 WSAStartup()初始化Winsocket

调用 socket() 一个 socket

调用 bind ()绑定 socket

调用 listen() 开始监听

调用 send ()和 receive()进行发送和接收操作

调用 accept() 接受客户端的连接请求

调用 WSAStartup()初始化Winsocket

Closesocket ()关闭socket调用 WSAcleanup()清理内存

调用 WSAcleanup()清理内存

调用 connect ()连接server

调用 send ()和 receive()进行发送和接收操作

Closesocket ()关闭socket

服务器 客户端

调用 socket() 一个 socket

Page 20: Visual    C++ 网络通信编程

UDP(非连接 ) 的编程基本原理

调用 WSAStartup()初始化Winsocket

调用 WSAStartup()初始化Winsocket

Closesocket ()关闭socket调用 WSAcleanup()清理内存

调用 WSAcleanup()清理内存

Closesocket ()关闭socket

调用 socket() 一个 socket 调用 socket() 一个 socket

调用 recvfrom 或Sendto 进行通信

调用 recvfrom 或Sendto 进行通信

Page 21: Visual    C++ 网络通信编程

C. TCP 主要函数说明:

( 1 ) WSAStartup(VersionReqd, lpmyWSAData);其中 ,VersionReqd描述了WINSOCK的版本 ,lpmyWSAData指向一个WSADATA结构 ,该结构描述了Windows Sockets的实现细节。 例如: //初始化 TCP 协议

WSADATA wsaData;BOOL ret = SAStartup(MAKEWORD(2,2), &wsaData);

Page 22: Visual    C++ 网络通信编程

( 2 ) TCP 主要函数介绍( UDP略):

2 ) socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)该函数建立指定地址格式 , 数据类型和协议下的套接口 ,地址格式为 AF_INET(IP唯一支持的格式 ), 数据类型 SOCK_STREAM表示建立流式套接口 ,参数三为 IPPROTO_TCP声明是 TCP 传输 。 3 ) bind(SOCKET s, (struct sockaddr *)&server, sizeof(server))该函数将将服务器进程在网上标识出来。其中 ,server 是sockaddr_in结构 ,其成员描述了本地端口号和本地主机地址 ,经过 bind()

4 ) listen( SOCKET s, int backlog )参数 s 是需要建立监听的 Socket ; backlog 是最大连接个数;

Page 23: Visual    C++ 网络通信编程

5 ) accept(s, (struct sockaddr *)&client, &namelen)) accept()阻塞 (缺省 )等待请求队列中的请求 , 一旦有连接请求来 , 该函数就建立一个和 s 有相同属性的新的套接口。 client也是一个 sockaddr_in 结构。

6 ) recv(ns,buf, pktlen,0) 和 send(ns,buf,pktlen,0)

7 ) connect(SOCKET s, (struct sockaddr FAR *)&dst_addr, sizeof(dst_addr)) ,(客户端函数)例如: sockaddr_in m_addr;

m_addr.sin_family = AF_INET;m_addr.sin_addr.S_un.S_addr =inet_addr(szTemp);m_addr.sin_port = htons(m_uPort);ret = connect(ClientSock , (LPSOCKADDR)&m_addr,

sizeof(m_addr));

Page 24: Visual    C++ 网络通信编程

( 3 )一个简单的有连接程序( WinAPI )

服务器#include <winsock.h>;void main(void){WSADATA wsaData;SOCKET ListeningSocket;SOCKET NewConnection;SOCKADDR_IN ServerAddr;SOCKADDR_IN ClientAddr;int Port = 5150;// 初始化 Windows Socket 2.2WSAStartup(MAKEWORD(2 , 2) , &wsaData);// 创建一个新的 Socket 来响应客户端的连接请求ListeningSocket = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);// 填写服务器地址信息 端口为 5150// IP地址为 INADDR_ANY , ServerAddr.sin_family = AF_INET;

Page 25: Visual    C++ 网络通信编程

ServerAddr.sin_port = htons(Port); ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);// 绑定监听端口,其实端口从 1-65535 , 1024-4000 为注册端口号,自由端口号 4000-65536bind(ListeningSocket , (SOCKADDR *)&ServerAddr , sizeof(ServerAddr));// 开始监听,指定最大同时连接数为 5listen(ListeningSocket , 5); // 接受新的连接NewConnection = accept(ListeningSocket , (SOCKADDR *) &ClientAddr ,ClientAddrLen));// 新的连接建立后,就可以互相通信了closesocket(NewConnection);closesocket(ListeningSocket);// 释放 Windows Socket DLL 的相关资源WSACleanup();}

Page 26: Visual    C++ 网络通信编程

客户端void main(void){WSADATA wsaData;SOCKET s;SOCKADDR_IN ServerAddr;int Port = 5150;//初始化 Windows Socket 2.2WSAStartup(MAKEWORD(2 , 2) , &amp;wsaData);// 创建一个新的 Socket 来连接服务器s = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);// 填写客户端地址信息 端口为 5150 服务器 IP地址为“ 136.149.3.29”ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(Port); ServerAddr.sin_addr.s_addr = inet_addr("136.149.3.29");// 向服务器发出连接请求connect(s , (SOCKADDR *) &amp;ServerAddr , sizeof(ServerAddr));// 新的连接建立后,就可以互相通信了,在这个简单的例子中,我们直接关闭连接,// 并关闭监听 Socket ,然后退出应用程序closesocket(s);// 释放 Windows Socket DLL 的相关资源WSACleanup();}

Page 27: Visual    C++ 网络通信编程

( 4 )一个简单的无连接程序的实现过程( MFC类)

应用 VC 的 CAsyncSocket 类建立其派生类:主要应用以下两个函数通信:

•int CAsyncSocket::SendTo( const void* lpBuf, int nBufLen, UINT nHostPort, LPCTSTR lpszHostAddress = NULL, int nFlags = 0 ) 发送数据

• int CAsyncSocket::ReceiveFrom( void* lpBuf, int nBufLen, CString& rSocketAddress, UINT& rSocketPort, int nFlags = 0 ) 接收数据

Page 28: Visual    C++ 网络通信编程

1 )首先创建对话框解决方案2 )建立 CAsyncSocket 类的派生对象3 )在对话框类中创建 m_sock 套接字4 )创建本地套接口 m_sock.Create(6801,SOCK_DGRAM,“169.23.33.11”);

m_sock.Bind(6801 ,“169.23.33.11”);5) 应用m_sockRecv.ReceiveFrom(buffer,bufferlength,szIP,uPort,0)接收数据 应用SendTo((buffer,bufferlength,szIP,uPort,0) 发送数据 在接收和发送时采用 SetTimer ()或开线程

Page 29: Visual    C++ 网络通信编程

3.WinSocket2.2 介绍

2.2版本与 1.1版本完全兼容2.2 的函数扩展,函数前加WSA表示使用的 是 2.2版本。例如

WSAAccept 与 accept WSAAsyncSelect 与 select引入重叠 I/O 和事件对象概念可以实现大数据的完成端口模型质量服务( QoS ),在 WSAConnect ()参数中定义质量参数等等

Page 30: Visual    C++ 网络通信编程

取得客户端地址:sockaddr_in remoteServer; accept(listenSock,&remoteServer,sizeof(remoteServer)); char* cIP=inet_ntoa(remoteServer.sin_addr);// 得到该数据包源 IP 取得服务器的地址 ;SOCKADDR_IN server_name; getpeername(client_sock, &server_name, sizeof(SOCKADDR_IN)); char* szIP = inet_ntoa(server_name.sin_addr);

MFC 的 socekt 类中使用:((IPEndPoint)socket.RemoteEndPoint).Address.ToString();得到客户端的 IP得到端口用 getsockopt 函数

IP 地址的获取

Page 31: Visual    C++ 网络通信编程

三、 VC.net 网络编程1 、 Vc.net 网络编程的方式:

面向 winSocketAPI 面向底层,原理性强,灵活性和扩展性较好。

应用 CAsyncSocket 类编程 采用异步非阻塞消息处理。

Csocket 类编程 采用阻塞套接字接口。

Page 32: Visual    C++ 网络通信编程

2 、编程条件 winSocketAPI 编程需要三个文件: WINSOCK.h: 这是 WINSOCK API 的头文件,需要包含在项目中。  WSOCK32.LIB: WINSOCK API 连接库文件。在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去。   WINSOCK.DLL: WINSOCK 的动态连接库,位于WINDOWS 的安装目录下。CAsyncSocket 类和 Csocket 类直接在 MFC 类库中调用,从而形成他们的派生类。

Page 33: Visual    C++ 网络通信编程

3 、 CAsyncSocket 类的编程的基本过程

客户端( 1 )创建一个 Dialog Based项目: CSockClient;( 2 )设计对话框,去掉 Ok 和 Cancle 两个按钮,增加ID_Connect (连接)、 ID_Send (发送)、 ID_Exit (关闭)按钮,增加 ListBox 控件 IDC_LISTMSG 和 Edit 控件IDC_EDITMSG ,并按下表在 ClassWizard 中为CCSockClientDlg 类添加变量。( 3 ) CAsyncSocket 类用 DoCallBack函数处理 MFC 消息,当一个网络事件发生时, DoCallBack函数按网络事件类型:FD_READ 、 FD_WRITE 、 FD_ACCEPT 、 FD_CONNECT分别调用 OnReceive 、 OnSend 、 OnAccept 、 OnConnect函数。---- 以 Public 方式继承 CAsyncSocket 类,生成新类 MySock; ---- 为 MySock 类添加虚函数OnReceive 、 OnConnect 、 OnSend

Page 34: Visual    C++ 网络通信编程

MySock::~MySock(){ // 关闭套接字 if(m_hSocket!=INVALID_SOCKET) Close(); }

void MySock::OnReceive(int nErrorCode) { m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0); //下面两行代码用来获取对话框指针CCSockClientApp* pA=(CCSockClientApp*)AfxGetApp(); CCSockClientDlg*pDlg=(CCSockClientDlg*)pA->m_pMainWnd;.......CAsyncSocket::OnReceive(nErrorCode); }

void MySock::OnSend(int nErrorCode) { Send(m_szBuffer,m_nLength,0); AsyncSelect(FD_READ|FD_CLOSE); CAsyncSocket::OnSend(nErrorCode);}

Page 35: Visual    C++ 网络通信编程

服务端

(1) 建立一个 CNewSocket 类,重载 CAsyncSocket 类的OnReceive 、 OnSend函数,进行与客户端的通信

( 2 )建立一个 CMyServerSocket 类,在该类中完成初始化过程,并重载 CAsyncSocket 类的 OnAccept函数void CMyServerSocket :: OnAccept ( int nErrorCode ){ //侦听到连接请求,调用 Accept函数 CNewSocket* pSocket = new CNewSocket (); if ( Accept ( *pSocket )) {pSocket- >AsyncSelect ( FD_READ );m_pSocket=pSocket; } else delete pSocket ; } ( 3 )为对话框添加一个“侦听”按钮,实现 Listen 过程。

Page 36: Visual    C++ 网络通信编程

三、网络编程实例分析

1 、基于 winsock API 的聊天程序

2 、文件传输的例子

3 、基于 MFC 的 CAsyncSocket 的一个经典的网络聊天的例子套接字类编程