1 / 36

Visual C++ 网络通信编程

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 协议

truda
Download Presentation

Visual C++ 网络通信编程

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Visual C++网络通信编程 2007年8月

  2. Visual C++网络通信编程 一、网络编程的基本概念 二、WinSocket编程基础 三、VC.net网络编程 四、网络编程实例分析

  3. 一、网络编程的基本概念 • 网络协议 • 网络通信的特性 • 小结

  4. 1.网络协议 IPV4 • IP( Internet Protocol) • TCP( Transmission Control Protocol) • UDP(User Datagram Protocol) • IPX/SPX协议 • NetBIOS协议 • AppleTalk协议 • ATM异步传输模式协议 IPV6(1 6个字节) 面向连接和无连接的通信模式 统一的名字解析及注册方法

  5. 表示层 1.网络协议 会话层 传输层(TCP/UDP) 为用户提供相应的界面,以便使用提供的连网功能 网络层(IP) 完成数据的格式化 数据链路层 控制两个主机间的通信链路(开放、操作和关闭)提供数据传输服务(可靠或不可靠) 网络层 在两个主机之间提供一套定址/寻址机制,同时负责数据包的路由选择 物理层 控制两个主机间的物理通信链路:同时还要负责对数据进行整形,以便在物理媒体上传输 控制两个主机间的物理通信链路:同时还要负责对数据进行整形,以便在物理媒体上传输 物理媒体负责以一系列电子信号的形式,传出数据

  6. 2.网络通信的特性 ——主要讲WinSock的通信特性 (1)面向协议 对每个离散写命令来说,如果传送协议把它们(而且只有它们)当做一条独立的消息在网上传送,就可以认为该协议是面向协议的。 128 128 128 512 256 256 256 128 128 128 数据报服务,UDP、IPX 流服务,TCP、NETBIOS

  7. 2.网络通信的特性 (2)面向连接和无连接 A、面向连接的服务中,进行数据交换之前,必须与通信方建立一条路径。这样既确定了通信方之间存在路由,又保证了通信双方都是活动的、都可彼此响应,但其特点是在通信双方之间建立一个通信信道需要很多开支。 特点:数据可靠、有次序、网络资源占有少。 B、无连接协议不保证接收端是否正在收听。无连接服务类似于邮政服务:发信人把信装入邮箱即可。

  8. 2.网络通信的特性 (3)从容关闭 从容关闭只出现在面向连接的协议中。在这种关闭过程中,一方开始关闭通信会话,但另一方仍然可以读取线上或网络堆栈上已挂起的数据。如果面向连接的协议不支持从容关闭,只要其中一方关闭了通信信道,都会导致连接立即中断,数据丢失,接收端不能读取数据这些情况出现。 SOCKET类: (1)FIN Closesocket(SOCKET) (2)ACK CAsyncSocket类: (3)FIN、ACK OnClose(SOCKET) A B 使用T C P的关闭过程

  9. TCP套接字的关闭

  10. 2.网络通信的特性 (4)广播数据 广播数据即数据从一个工作站发出,局域网内的其他所有工作站都能收到它。这一特征适用于无连接协议,因为L A N上的所有机器都可获得并处理广播消息。 对于有连接协议,采用以下方式实现广播: 客户端2 客户端1 服务器端 客户端3 客户端4

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

  12. 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; }; 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; } IPV4的地址结构 IPV6的地址结构

  13. 2.网络通信的特性 ATM: typedef struct sockaddr_atm {u_short satm_family;ATM_ADDRESS satm_number;ATM_BLLI satm_blli;ATM_BHLI satm_bhli; } Bluetooth: typedef struct _SOCKADDR_BTH {USHORT addressFamily;BTH_ADDR btAddr;GUID serviceClassId;ULONG port; } 红外线: typedef struct _SOCKADDR_IRDA {u_short irdaAddressFamily;u_char irdaDeviceID[4];char irdaServiceName[25]; }

  14. 3.小结 在开发网络应用程序时,可从现有的大量协议中选出自己需要的,用来作为应用程序的基础。如果应用程序需要依靠特定协议进行通信,选择余地就有限;但是,如果想从头开发一个应用程序, 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的支持已经极弱 。

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

  16. 1.WinSocket简介 90年代初,由Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即Windows Sockets规范,它不是一种网络协议,而是一套开放的、支持多种协议的Windows下的网络编程接口。主要借鉴从U n i x平台的B e r k e l e y套接字接口。 现在的Winsock已经基本上实现了与协议无关。

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

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

  19. (1) TCP(面向连接)的编程基本原理 服务器 客户端 调用WSAStartup()初始化Winsocket 调用WSAStartup()初始化Winsocket 调用socket()一个socket 调用socket()一个socket 调用bind()绑定socket 调用listen()开始监听 调用connect()连接server 调用accept()接受客户端的连接请求 调用send()和receive()进行发送和接收操作 调用send()和receive()进行发送和接收操作 Closesocket()关闭socket Closesocket()关闭socket 调用WSAcleanup()清理内存 调用WSAcleanup()清理内存

  20. UDP(非连接)的编程基本原理 调用WSAStartup()初始化Winsocket 调用WSAStartup()初始化Winsocket 调用socket()一个socket 调用socket()一个socket 调用recvfrom或 Sendto进行通信 调用recvfrom或 Sendto进行通信 Closesocket()关闭socket Closesocket()关闭socket 调用WSAcleanup()清理内存 调用WSAcleanup()清理内存

  21. C. TCP主要函数说明: (1)WSAStartup(VersionReqd, lpmyWSAData); 其中,VersionReqd描述了WINSOCK的版本,lpmyWSAData指向一个WSADATA结构,该结构描述了Windows Sockets的实现细节。 例如://初始化TCP协议 WSADATA wsaData; BOOL ret = SAStartup(MAKEWORD(2,2), &wsaData);

  22. (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是最大连接个数;

  23. 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));

  24. (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.2 WSAStartup(MAKEWORD(2,2), &wsaData); // 创建一个新的Socket来响应客户端的连接请求 ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 填写服务器地址信息端口为5150 // IP地址为INADDR_ANY,ServerAddr.sin_family = AF_INET;

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

  26. 客户端 void main(void) {WSADATA wsaData; SOCKET s; SOCKADDR_IN ServerAddr; int Port = 5150; //初始化Windows Socket 2.2 WSAStartup(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();}

  27. (4)一个简单的无连接程序的实现过程(MFC类)(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 )接收数据

  28. 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()或开线程

  29. 3.WinSocket2.2介绍 • 2.2版本与1.1版本完全兼容 • 2.2的函数扩展,函数前加WSA表示使用的 是2.2版本。例如 • WSAAccept与accept • WSAAsyncSelect与select • 引入重叠I/O和事件对象概念 • 可以实现大数据的完成端口模型 • 质量服务(QoS),在WSAConnect()参数中定义质量参数等等

  30. IP地址的获取 • 取得客户端地址: • 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函数

  31. 三、VC.net网络编程 • 1、Vc.net网络编程的方式: • 面向winSocketAPI • 面向底层,原理性强,灵活性和扩展性较好。 • 应用CAsyncSocket类编程 • 采用异步非阻塞消息处理。 • Csocket类编程 • 采用阻塞套接字接口。

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

  33. 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

  34. 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);}

  35. 服务端 (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过程。

  36. 三、网络编程实例分析 1、基于winsock API的聊天程序 2、文件传输的例子 3、基于MFC的CAsyncSocket的一个经典的网络聊天的例子套接字类编程

More Related