Presentation is loading. Please wait.

Presentation is loading. Please wait.

网络编程技术 授课教师:谭献海 Email:xhtan@swjtu.edu.cn.

Similar presentations


Presentation on theme: "网络编程技术 授课教师:谭献海 Email:xhtan@swjtu.edu.cn."— Presentation transcript:

1 网络编程技术 授课教师:谭献海

2 网络编程技术概述 网络编程技术 直接网卡编程(可编程芯片) 基于packet driver的编程技术 基于NDIS的网络编程
VPACKET,PACKET32 Berkeley Sockets编程技术 Winsock JAVA网络编程

3 Winsock 一、  Winsock概述 Windows网络编程规范-Windows Sockets是Windows下得到广泛应用的、开放的、支持多种协议的socket网络编程接口。 Windows Sockets规范以U.C. Berkeley大学BSD UNIX中流行的Socket接口为范例,定义了一套Windows下的网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数;也包含了一组针对Windows的扩展库函数,以便充分地利用Windows消息驱动机制和异步I/O选择进行编程。

4 Winsock winsock有开发组件和运行组件二大部分 开发组件供开发应用程序使用,包括定义的宏常量、数据结构和函数调用接口原型。
运行组件是winsock应用程序接口的动态链接库—winsock.DLL,应用程序在执行时通过装入DLL实现网络通信功能。

5 Winsock所处的位置 应用程序调用Windows Sockets的API实现相互之间的通信,Windows Sockets又利用下层的网络通信协议和操作系统调用实现实际的通信工作。 应用程序1 应用程序2 网络编程接口winsock 网络通信协议服务接口TCP/IP 操作系统 windows 物理通信介质 图1 winsock所处的位置

6 Windows 平台支持的Winsock 版本
1.1 (2.2) Windows 98 2.2 Windows NT 4.0 Windows 2000 Windows CE 1.1 采用Winsock 1的应用必须有Winsock.h 头文件 使用Winsock 2的应用需要Winsock2.h 头文件。

7 构建编程环境 Winsock有两个主要的版本,即Winsock1和Winsock2
头文件:编写与Winsock1兼容的程序需要引用头文件WINSOCK.H,如果编写使用Winsock2的程序,需要引用WINSOCK2.H;此外还有一个MSWSOCK.H头文件,它是专门用来支持在Windows平台上高性能网络程序扩展功能的 库文件:使用WINSOCK.H头文件时,同时需要库文件WSOCK32.LIB;使用WINSOCK2.H时,则需要WS2_32.LIB;如果使用MSWSOCK.H中的扩展API,则需要MSWSOCK.LIB

8 Winsock的初始化与卸载 每个Winsock 应用都必须加载Winsock DLL的相应版本。
加载Winsock 库是通过调用WSAStartup函数实现的。 如果调用Winsock函数之前,没有加载Winsock 库,就会返回一个SOCKET_ERROR,对应的错误信息是WSANOTINITIALISED。 在通信应用程序的最后调用WSACleanup来释放所使用的Windows Sockets DLL

9 WInsock2 程序的大致框架 WSAStartup (初始化Windows Sockets API) 应用程序
WSACleanup (释放所使用的Windows Sockets DLL)

10 Winsock函数 二、 Winsock提供的函数调用 WinSock包括三类函数: Berkeley socket函数
检索有关域名、通信服务和协议等Internet信息的数据库函数 Berkekley socket例程的Windows专用的扩展函数

11 Winsock WinSock函数分为阻塞和非阻塞函数。创建一个socket时,可以指定它是否为阻塞。
在缺省情况下,Berkeley的Socket函数和WinSock都创建“阻塞”的socket。 阻塞socket可以通过使用select函数或者WSAAsynSelect函数将指定操作(如接收、发送、异常等)变成非阻塞操作。

12 Winsock 2.1 基本套接口函数   Windows Sockets规范包含了以下30个Berkeley风格的套接口例程(与Berkeley socket兼容):  * 表示该函数为阻塞方式。

13 基本套接口函数 socket() 创建一个socket并返回一个套接口句柄 bind() 把一个本地名字和一个无名的套接口捆绑起来
Listen() 服务器监听 accept()* 响应连接请求,并且新建一个套接口。原来 的套接口则返回监听状态。 connect()* 初始化到一个指定套接口上的连接 getpeername() 得到连接在指定套接口上的对等通信方 的名字。 getsockname() 得到指定套接口的名字。 getsockopt() 得到与指定套接口相关的属性选项。

14 基本套接口函数 recv()* 从一个已连接的套接口接收数据(面向连接) recvfrom()* 从一个的套接口接收数据(无连接)
send()* 向一已连接的套接口发送数据(面向连接) sendto()* 向套接口发送数据(无连接) select()* 执行同步I/O多路复用。 setsockopt() 设置与指定套接口相关的属性选项。 Close() 关闭套接口 shutdown() 关闭一部分全双工的连接。 *表示例程在应用于阻塞套接口时会阻塞。

15 基本套接口函数 htonl() 把32位的数字从主机字节顺序转换到网络字节顺序
htons() 把16位的数字从主机字节顺序转换到网络字节顺序 ntohl() 把32位数字从网络字节顺序转换为主机字节顺序 ntons() 把16位数字从网络字节顺序转换为主机字节顺序 inet_addr() 把一个十进制“.”分IP地址转换成二进制IP地址 inet_ntoa()把一个二进制IP地址转换成十进制“.”分IP地址 ioctlsocket() 套接口I/O控制,用于设置套接口的I/O模式

16 数据库函数 2.2 数据库函数 Windows Sockets规范定义了如下数据库函数:
gethostbyaddr()*   根据网络地址得到对应的主机信息 gethostbyname() *  根据主机名得到对应的主机信息(IP地址,可能多个) gethostname()      得到本地主机名 getprotobyname()*  根据协议名得到对应的协议信息 getprotobynumber()*   根据协议号得到对应的协议名 getservbyname()*   根据一个服务的名字得到对应的服务信息 getservbyport()*   从根据一个端口号得到对应的服务信息  *表示该函数在某些情况下可能会阻塞

17 Winsock扩展的函数 主要针对windows的消息机制和异步IO选择来扩展

18 Windows内部机制 Windows是一个“基于事件和消息驱动的”操作系统。 系统检测到后 发给应用程序 用户进行了影响窗口的动作
“事件” “消息” 触发 处理消息

19 Winsock扩充的函数 2.3 针对Microsoft Windows的扩展函数
为了适应Windows下的消息机制和异步I/O选择操作,Windows Sockets API在功能上扩充了将近20个函数,所有扩充的函数均冠以前缀WSA(Windows Sockets Asynchronous)。这些扩展的API是为了应用程序能更好地处理基于消息的异步网络事件。 虽然基于Windows Sockets的编程并不强制使用这个扩展的API集(WSAStartup()和WSACleanup()除外)但推荐应用程序开发者遵循Microsoft Windows的编程范例。

20 Winsock扩充的函数 WSAAsyncGetHostByAddr()     获取对应于一个地址的主机信息(一个标准的Berkeley的getXbyY()函数集合的异步版本 )  WSAAsyncGetHostByName() 获取对应于一个主机名的主机信息(异步版本)  WSAAsyncGetProtoByName() 获取对应于一个协议名的协议信息(异步版本)  WSAAsyncGetProtoByNumber()获取对应于一个协议号的协议信息(异步)  WSAAsyncGetServByName() 获取对应于一个服务名的服务信息(异步) WSAAsyncGetServByPort() 获取对应于一个端口号的服务信息(异步版本) WSAAsyncSelect()            select()函数的异步版本。

21 Winsock扩充的函数 WSACancelAsyncRequest() 取消一个未完成的WSAAsyncGetXByY()函 数的实例。
WSACancelBlockingCall()     取消未完成的阻塞API调用。 WSAStartup()             初始化底层的Windows Sockets DLL WSACleanup()            撤销底层的Windows Sockets DLL注册。 WSAGetLastError()  得到最近的一个Windows Sockets API调用错误信息 WSASetLastError()  设置最近一次错误信息 WSAIsBlocking()   该线程是否已经被一个调用阻塞 WSASetBlockingHook()      为底层的Windows Sockets实现设置阻塞嵌 入(hook)程序入口 WSAUnhookBlockingHook()    恢复缺省的阻塞嵌入(hook)处理程序

22 Winsock对Berkeley socket的扩充
网络通信必须通过启动函数WSAStartup()启动winsock,装载动态链接库; 对于每一个WSAStartup的调用必须对应一个WSACleanup的调用,这个函数的作用是释放相应的WinSock库,函数原型为: Int WSACleanup(void);

23 WSAStartup()函数 int WSAStartup( WORD wVersionRequested,
LPWSDATA lpWSAData ) wVersionRequested:指定要加载的winsock库的版本,高字节为次版本号,低字节为主版本号。它的值可使用宏MAKEWORD(x,y)来建立,x为次版本号,y为主版本号 lpWSAData:一个指向WSADATA结构的指针,用来返回DLL库的详细信息

24 结构体WSADATA typedef struct WSAData{ WORD wVersion; //库文件建议应用程序使用的版本
WORD wHighVersion; //库文件支持的最高版本 char szDescription[WSADESCRIPTION_LEN+1]; //库描述字符串 char szSystemStatus[WSASYS_STATUS_LEN+1]; //系统状态字符串 unsigned short iMaxSockets; //同时支持的最大套接字数量 //以下两个参数在2.0版中已废弃 unsigned short iMaxUdpDg; char FAR* lpVendorInfo; } WSDATA,FAR * LPWSADATA;

25 winsock的启动与关闭程序实例 #include <stdio.h> //引入winsock库
#include <winsock2.h> #pragma comment(lib,"WS2_32") #pragma是标准C/C++的内容,用于向编译器传递特定参数。具体参数则与编译器有关 其中comment(lib,“WS2_32”)特定于VC的参数,并不通用 //加载winsock库 void initSocket(BYTE,BYTE); //释放winsock库 void cleanSocket();

26 加载、释放winsock库(WS2_32.DLL)
void initSocket(BYTE minorVer,BYTE majorVer) { WSADATA wsaData; WORD sockVersion = MAKEWORD(minorVer,majorVer); if (WSAStartup(sockVersion,&wsaData)!=0) exit(0); } void cleanSocket() WSACleanup();

27 Winsock对Berkeley socket的扩充
异步选择机制 在windows sockets中提供了一组异步扩展函数,实现基于消息机制的网络通信编程,其中关键是提供了对网络事件基于消息的异步存取,其核心是异步选择函数WSAAsyncSelect()。 WSAAsyncSelect()函数用来注册应用程序感兴趣的网络事件,当这些网络事件发生时,应用程序相应的窗口函数将得到一个消息,应用程序根据该消息进行判断,完成相应的操作。

28 Winsock对Berkeley socket的扩充
WSAAsyncSelecet ()函数的原型为: int PASCAL FOR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent); 参数含义: SOCKET s--应用程序申请到的套接字句柄 hWnd--接收winsock消息的窗口句柄 wMsg--向窗口hWnd发出的消息的名称 lEvent--被注册的网络事件 功能描述: 请求winsock.DLL在检测到套接字s上发生的某个网络事件IEvent时,向窗口hWnd发送一个消息wMSG,并自动地设置套接字处于非阻塞工作方式。其中的事件可以是一个或多个的组合,事件之间用“|”分隔。 通常事件包括允许读数据,写数据、连接、接纳、关闭、带外数据等六种。 利用重载以下成员函数来处理各种网络事件。

29 Winsock对Berkeley socket的扩充
标记 事件 需要重载的函数 FD_READ 有数据到达时发生 void OnReceive( int nErrorCode ); FD_WRITE 允许数据发送时产生 void OnSend( int nErrorCode ); FD_OOB 收到外带数据时发生 void OnOutOfBandData( int nErrorCode ); FD_ACCEPT 有连接请求到达时发生 void OnAccept( int nErrorCode ); FD_CONNECT 作为客户端连接成功时发生 void OnConnect( int nErrorCode ); FD_CLOSE 套接口关闭时发生 void OnClose( int nErrorCode );

30 Winsock对Berkeley socket的扩充
lEvent可以是一种或多种网络事件的组合(需要多个网络事件的组合时通过OR操作符(|)来表示),例如,如果我们希望在套接字S上收到数据或允许发送数据时收到消息,这时函数的调用格式为: WSAAsyncSelect(s,hWnd,wMsg, FD_READ|FD_WRITE);

31 Winsock对Berkeley socket的扩充
异步请求服务 异步数据库函数允许应用程序以异步方式请求信息。 WSAAsyncGetXByY()函数允许应用程序开发者不必象在使用Berkeley标准函数时阻塞整个Windows环境 WSACancelAsyncRequest()函数允许一个应用程序取消未完成的异步WSAAsyncGetXByY()请求

32 Winsock对Berkeley socket的扩充
利用阻塞HOOK进行阻塞处理 windows是一个异步操作系统,在windows内任何事件都是异步发生的。事件发生后,windows向程序启动此操作时指定的窗口发送一个消息 windows中对消息的处理由类似于下面的循环语句完成 MSG msg; /* 定义消息类型*/ While(GetMessage(&msg,NULL,0,0 ) ) {   TranslateMessage (&msg) ; /*对该消息进行翻译 */   DispatchMessage (&msg) ; /* 分发消息*/ } 函数GetMessage从消息队列中检索出一条消息,并将它存于具有MSG类型的一个变量中,然后交由函数TranslateMessage对该消息进行翻译,并通过函数DispatchMessage将消息发送到适当的对象上。 在循环语句后通常编写一个消息处理过程(hook)例程来处理该消息。

33 Winsock对Berkeley socket的扩充
在winsock中有一个默认的阻塞处理例程BlockingHook(…),这个处理例程是简单地获取并发送winsock消息。 对于要求更复杂的消息处理的应用程序,应建立自已的处理过程用的Hook程序。为此在winsock的API中提供了WSASetBlockingHook(…)和WSAUnHookBlockingHook(…)两个函数,前者用于定义使用自已的阻塞Hook例程 ,后者用于恢复缺省Hook例程。 winsock提供了两个检测和取消阻塞操作的函数:WSAIsBlocking(viod)和WSACancelBlockingCall(void) 前者检查确定当前是否有阻塞操作,并返回布尔值;若有阻塞操作,应用程序可以进入阻塞操作处理;若有必要调用后者取消当前的阻塞操作

34 Winsock对Berkeley socket的扩充
错误处理 如果调用一个Winsock 函数时发生了错误,就可用WSAGetLastError函数来获得最近一个错误代码,这个错误代码明确地表明发生的错误情况。 WSAGetLastError 函数返回的错误都有预定义常量值,根据Winsock 版本的不同,这些值在Winsock .h或Winsock2.h中声明。

35 Winsock错误代码一览表 常量值                                说明 ============================================== sckOutOfMemory               7        内存不足 sckInvalidPropertyValue     380      属性值不效 sckGetNotSupported          394       属性不可读 sckGetNotSupported          383       属性是只读的 sckBadState                 40006     所请求的事务或请求本身的错误协议或者错 误连接状态 sckInvalidArg               40014     传递给函数的参数格式不确定,或者不在指 定范围内 sckSuccess                   40017     成功 sckUnsupported          40018     不支持的变量类型 sckInvalidOp                40020     在当前状态下的无效操作 sckOutOfRange           40021     参数越界 sckWrongProtocol       40026     所请求的事务或请求本身的错误协议 sckOpCanceled            10004     取消操作 sckInvalidArgument    10014     所请求的地址是广播地址,但未设置标记

36 Winsock错误代码一览表 sckWouldBlock           10035     套接字不成块,而指定操作将使之成块 sckInProgress               10036     制造块的Winsock操作在进行之中 sckAlreadyComplete     10037     完成操作。未进行制作块的操作 sckNotSocket                10038     描述符不是套接字 sckMsgTooBig                10040     数据太大,不适于缓冲区的要求,因 而被截断 sckPortNotSupported      10043     不支持指定的端口 sckAddressInUse             10048     地址在使用中 sckAddressNotAvailable      10049     来自本地机器的不可用地址 sckNetworkSubsystemFailed   10050     网络子系统失败 sckNetworkUnreachable            当前不能从主机到达网络 sckNetReset                  10052     在设置SO_KEEPALIVE时连接超时 sckConnectAborted           10053     由于超时或者其它失败而中止接连 sckConnectionReset          10054     通过远端重新设置连接

37 Winsock错误代码一览表 sckNoBufferSpace            10055     没有可用的缓存空间 sckAlreadyConnected      10056     已连接的套接字 sckNotConnected             10057     未接连套接字 sckSockedShutdown        10058     已关闭套接字 sckTimedout                      套接字超时 sckConnectionRefused    10061     强行拒绝连接 sckNotInitialized                套接字没有初始化 sckHostNotFound             11001     授权应答:未找到主机 sckHostNotFoundTryAgain     11002     非授权应答:未找 到主机,重试 sckNonRecoverableError            不可恢复的错误 sckNoData                   11004     无效名,对所请求的类型 无数据记录

38 WINSOCK 2.0 的扩充功能 从Windows Sockets 1.1到Windows Socket 2的主要变动有:
1 同时使用多个传输协议   为了用户能够同时使用多个传输协议,在Windows Socket 2中,结构有所改变。在Windows Sockets 1.1中,软件开发商所提供的DLL实现了Windows Sockets的API和TCP/IP协议栈。Windows Sockets DLL和底层协议栈的接口是唯一而且独占的。Windows Socket 2改变了这种模型:它定义了一个Windows Sockets DLL和底层协议栈间的标准服务提供接口(SPI),这使得一个Windows Sockets DLL能够同时访问不同软件开发商的多个底层协议栈。

39 同时使用多个传输协议

40 WINSOCK 2.0 的扩充功能 2 与Windows Socket 1.1应用程序的向后兼容性 :源码和二进制代码。

41 WINSOCK 2.0 的扩充功能 3 在Windows Sockets中注册传输协议   要使Windows Sockets能够利用一个传输协议,该传输协议必须在系统上安装并且在Windows Sockets中注册。 Windows Sockets 2的DLL包含了一组API来完成这个注册过程。这个注册过程包括建立一个新的注册和取消一个已有的注册。

42 WINSOCK 2.0 的扩充功能 使用多个协议   一个应用程序可以通过WSAEnumProtocols()功能调用来得到目前有多少个传输协议可以使用,并且得到与每个传输协议相关的信息,这些信息包含在PROTOCOL_INFO结构中。 在Windows Sockets 1中仅有一个地址族(AF_INET),它包含了数量不多的一些众所周知的套接口类型和协议标识符。这在Windows Sockets 2中已经有所改变。除了现有的地址族、套接口类型和协议标识符为了兼容性原因被保留以外,Windows Sockets 2加入了许多唯一的但是可能并不为大家所知的地址族、套接口类型和协议标识符。

43 WINSOCK 2.0 的扩充功能 4 协议无关的名字解析   Windows Sockets 2包含了应用程序可以使用的多种标准化的网络名字服务。Windows Sockets 2应用程序并不需要理解与名字服务相关的许多迥异的接口(例如DNS,NIS,X.5000,SAP等等)。 5 重叠I/O和事件对象   Windows Sockets 2引入了重叠I/O的概念并且要求所有的传输协议提供者都支持这一功能。重叠I/O仅能在由WSASocket()函数打开的套接口上使用(使用WSA_FLAG_OVERLAPPED标记)。 WSAIoctl()函数(iocltosocket()函数的增强版本),还可以使用重叠I/O操作的延迟完成特性。

44 WINSOCK 2.0 的扩充功能 5.1 事件对象   重叠I/O概念的引入需要建立一个机制使得应用程序能够正确地把发送和接收事件与今后它们完成时的指示相连接。在Windows Sockets 2中,这一点是通过事件对象实现的,与事件对象相关的函数,包括 WSACreateEvent() WSACloseEvent() WSASetEvent() WSAResetEvent() WSAWaitForMultipleEvent() WSAGetOverlappedResult() 5.2 接收操作完成指示   提供给应用程序适当的灵活性,Windows Sockets 2为接收操作完成指示提供了多个选项。它们包括:等待(阻塞)事件对象,检查事件对象和套接口I/O完成例程。

45 WINSOCK 2.0 的扩充功能 6.使用事件对象异步通知   为了适应一些应用程序(例如精灵程序(守护进程))或者某些没有用户界面的服务程序(它们不使用窗口句柄),Windows Socket 2提供了WSAEventSelect()函数和WSAEnumNetworkEvents()函数。WSAEventSelect()函数和WSAAyncSelect()函数很类似,区别仅在于当一个FD_XXX网络事件发生时,WSAEventSelect()函数将导致一个应用程序指定的事件对象被设置,而WSAAyncSelect()将导致一条Windows消息被发送(例如FD_READ,FD_WRITE等等)。

46 WINSOCK 2.0 的扩充功能 7. 服务的质量(QoS)   Windows Sockets 2推荐的流规格把QoS特性划分为:   1. 源通信描述:应用程序的通讯事件以什么方式被送 入网络。   2. 延时性:最大延时和可接受的延时变化(抖动)。   3. 需要保证的服务级别:应用程序是否要求对服务质 量的绝对保证。   4. 费用:这一项是为将来可以决定有意义的费用时保 留的。   5. 服务提供者特定的参数:流规格可以根据具体的提 供者扩展。

47 WINSOCK 2.0 的扩充功能 8. 套接口组   Windows Sockets 2引入了一个所谓套接口组的概念。它允许应用程序(或者一组共同工作的应用程序)通知底层的服务提供者一组特定的套接口是相关的,它们享有一些特定的性质。组的特性包括了组内单个套接口之间的相关特性和整个组的服务规范的特性。

48 WINSOCK 2.0 的扩充功能 9. 共享套接口 为了在进程间共享套接口,Windows Sockets 2引入了WSADuplicateSocket()函数。共享套接口是通过对底层的套接口创建附加的套接口描述字实现的。 10.连接建立和拆除的高级函数 WSAAccept()函数 WSAConnect()函数 WSASendDisconnect()函数接收断连 WSARecvDisconnect()函数接收断连。

49 WINSOCK 2.0 的扩充功能 11.扩展的字节顺序转换例程 Windows Sockets 2提供了一套把16位或者32位数字转换到网络字节顺序或者从网络字节顺序转换的例程。 12. 分散/聚集方式I/O   WSASend(),WSASendTo(),WSARecv()和WSARecvFrom()函数都以应用程序缓冲区数组作为输入参数,因此它们可以进行分散/聚集方式(向量方式)的I/O操作。

50 WINSOCK 2.0 的扩充功能 13.协议无关的多点通讯   Windows Sockets 2支持基本的数据传输以一般的方式使用不同的传输协议。Windows Sockets 2也支持应用程序以一般的方式使用传输协议的多点通讯能力。

51 Windows Sockets 2新增函数一览
WSAAccept()        accept()函数的扩展版本,它支持条件接收和套接口分组 WSACloseEvent()             释放一个事件对象。 WSAConnect()     connect()函数的扩展版本,它支持连接数据交 换和QOS规范 WSACreateEvent()            创建一个事件对象。 WSADuplicateSocket()     为一个共享套接口创建一个新的套接口描述字。 WSAEnumNetworkEvents()  检查是否有网络事件发生。 WSAEnumProtocols()         得到每个可以使用的协议的信息 WSAEventSelect()           把网络事件和一个事件对象连接 WSAGetOverlappedResu()   得到重叠操作的完成状态。 WSAGetQOSByName()           对于一个传输协议服务名字提供相应的QOS参数 WSAHtonl()                  htonl()函数的扩展版本。 WSAHtons()                   htons()函数的扩展版本。 WSAIoctl()                  ioctlsocket()函数的允许重叠操作的版本。

52 Windows Sockets 2新增函数一览
WSAJoinLeaf()               在多点对话中加入一个叶节点。 WSANtohl()                  ntohl()函数的扩展版本。 WSANtohs()                  ntohs()函数的扩展版本。 WSARecv()        recv()函数的扩展版本。它支持分散/聚集I/O和重叠套接口操作 WSARecvDisconnect()     终止套接口的接收操作。如果套接口是基于连接的,得到拆除数据 WSARecvFrom() recvfrom()函数的扩展版本。它支持分散/聚集I/O和重叠套接口操作 WSAResetEvent()         重新初始化一个数据对象。 WSASend()               send()函数的或者版本。它支持分散/聚集I/O和重叠套接口操作。 WSASendDisconnect()     启动一系列拆除套接口连接的操作,并且可以选择发送拆除数据。 WSASendTo()            sendto()函数的扩展版本。它支持分散/聚集I/O和重叠套接口操作。 WSASetEvent()           设置一个数据对象。 WSASocket()             socket()函数的扩展版本。它以一个PROTOCOL_INFO结构作为输入参数,并且允许创建重叠套接口。它还允许创建套接口组。 WSAWaitForMultipleEvents()  阻塞多个事件对象。

53 面向连接的C/S程序工作流程图(TCP)

54 无连接的C/S程序工作流程图(UDP)

55 并发服务器 子线程

56 基于TCP的客户/服务器-服务器代码 // server.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h"
#include <Winsock2.h> #include <stdio.h> #include <stdlib.h> #define DEFAULT_PORT 5050 //服务端默认端口 int _tmain(int argc, char* argv[]) { int iPort = DEFAULT_PORT; WSADATA wsaData; SOCKET sListen,sAccept; int iLen; //客户地址长度 int iSend;//发送数据长度 char buf[] = "I am a server";//要发送给客户的信息 struct sockaddr_in ser,cli;//服务器和客户的地址 if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) printf("Failed to load Winsock.\n"); return -1; }

57 基于TCP的客户/服务器-服务器代码 sListen = socket(AF_INET,SOCK_STREAM,0);//创建服务器端套接口
if(sListen == INVALID_SOCKET) { printf("socket() Failed: %d\n",WSAGetLastError()); return -1; } //以下建立服务器端地址 //使用IP地址族 ser.sin_family = AF_INET; //使用htons()把双字节主机序端口号转换为网络字节序端口号 ser.sin_port = htons(iPort); //htonl()把一个四字节主机序IP地址转换为网络字节序主机地址 //使用系统指定的IP地址INADDR_ANY ser.sin_addr.s_addr = htonl(INADDR_ANY); //bind()函数进行套接定与地址的绑定 if(bind(sListen,(LPSOCKADDR)&ser,sizeof(ser)) == SOCKET_ERROR) printf("bind() Failed: %d\n",WSAGetLastError());

58 基于TCP的客户/服务器-服务器代码 //进入监听状态 if(listen(sListen,5) == SOCKET_ERROR) {
printf("lisiten() Failed: %d\n",WSAGetLastError()); return -1; } //初始化客户地址长度参数 iLen = sizeof(cli); //进入一个无限循环,等待客户的连接请求 while(1) sAccept = accept(sListen,(struct sockaddr *)&cli,&iLen); if(sAccept == INVALID_SOCKET) printf("accept() Failed: %d\n",WSAGetLastError()); //输出客户IP地址和端口号 printf("Accepted client IP: [%s], port:[%d]\n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port));

59 基于TCP的客户/服务器-服务器代码 iSend = send(sAccept,buf,sizeof(buf),0);
//给连接的客户发送信息 iSend = send(sAccept,buf,sizeof(buf),0); if(iSend == SOCKET_ERROR) { printf("send() Failed: %d\n",WSAGetLastError()); break; } else if(iSend == 0) else printf("send() byte: %d\n",iSend); closesocket(sAccept); closesocket(sListen); WSACleanup(); return 0;

60 基于TCP的客户/服务器-客户端代码 // client.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h"
#include <Winsock2.h> #include <stdio.h> #include <stdlib.h> #define DATA_BUFFER 1024 //默认缓冲区大小 int _tmain(int argc, char * argv[]) { WSADATA wsaData; SOCKET sClient; int iPort = 5050; int iLen;//从服务器端接收的数据长度 char buf[DATA_BUFFER];//接收数据的缓冲区 struct sockaddr_in ser;//服务器端地址 //判断参数输入是否正确:client [Server IP] if(argc<2) //提示在命令行中输入服务器IP地址 printf("Usage: client [server IP address]\n"); return -1; }

61 基于TCP的客户/服务器-客户端代码 memset(buf,0,sizeof(buf));//接收缓冲区初始化
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) { printf("Failed to load Winsock.\n"); return -1; } //填写要连接的服务器地址信息 ser.sin_family = AF_INET; ser.sin_port = htons(iPort); //inet_addr()将命令行中输入的点分IP地址转换为二进制表示的网络字节序IP地址 ser.sin_addr.s_addr = inet_addr(argv[1]); //建立客户端流式套接口 sClient = socket(AF_INET,SOCK_STREAM,0); if(sClient == INVALID_SOCKET) printf("socket() Failed: %d\n",WSAGetLastError());

62 基于TCP的客户/服务器-客户端代码 //请求与服务器端建立TCP连接
if(connect(sClient,(struct sockaddr *)&ser,sizeof(ser)) == INVALID_SOCKET) { printf("connect() Failed: %d\n",WSAGetLastError()); return -1; } else //从服务器端接收数据 iLen = recv(sClient,buf,sizeof(buf),0); if(iLen == 0) else if(iLen == SOCKET_ERROR) printf("recv() Failed: %d\n",WSAGetLastError()); printf("recv() data from server: %s\n",buf); closesocket(sClient); WSACleanup(); return 0;

63 基于TCP的客户/服务器-程序运行结果 服务器端 客户端

64 MFC中的Winsock编程API MFC编程技术定义了用于网络编程的Winsock类,类名为CAsyncSocket;还定义了一个派生于CAsyncSocket的CSocket类。这两个类简单实用,用户可以使用它们来实现自己的网络程序。 与前面的介绍相似,使用MFC的Winsock类进行操作时需要使用Winsock2.h、Winsock32.dll和ws2_32.lib三个文件

65 Winsock编程实例 VC++编程实现网络嗅探器
基于Windows提供的CAsyncSocket类实现的Client/Server通信程序框架

66 VC++编程实现网络嗅探器 对网卡混杂模式的设置是通过原始套接字(raw socket)来实现的
在创建了原始套接字后,需要通过setsockopt()函数来设置IP头操作选项 然后再通过bind()函数将原始套接字绑定到本地网卡。为了让原始套接字能接受所有的数据 还需要通过ioctlsocket()来进行设置,而且还可以指定是否亲自处理IP头。 通过recv()函数来完成对网络数据包进行嗅探 原始套接字此时捕获到的数据包并不仅仅是单纯的数据信息,而是包含有 IP头、 TCP头等信息头的最原始的数据信息,这些信息保留了它在网络传输时的原貌。通过对这些在低层传输的原始信息的分析可以得到有关网络的一些信息。

67 VC++编程实现网络嗅探器 通过数据结构_TCP来定义TCP数据段头: typedef struct _TCP{
WORD SrcPort; // 源端口 WORD DstPort; // 目的端口 DWORD SeqNum; // 顺序号 DWORD AckNum; // 确认号 BYTE DataOff; // TCP头长 BYTE Flags; // 标志(URG、ACK等) WORD Window; // 窗口大小 WORD Chksum; // 校验和 WORD UrgPtr; // 紧急指针 // 无选项 } TCP; typedef TCP *LPTCP; typedef TCP UNALIGNED * ULPTCP;

68 VC++编程实现网络嗅探器 IP数据报头 如何定义Flags和FragOff的比特数?
typedef struct _IP{ union{ BYTE Version; // 版本 BYTE HdrLen; // IHL }; BYTE ServiceType; // 服务类型 WORD TotalLen; // 总长 WORD ID; // 标识 union{ WORD Flags; // 标志 WORD FragOff; // 分段偏移 }; BYTE TimeToLive; // 生存时间 BYTE Protocol; // 协议 WORD HdrChksum; // 头校验和 DWORD SrcAddr; // 源地址 DWORD DstAddr; // 目的地址 BYTE Options; // 选项 } IP; typedef IP * LPIP; typedef IP UNALIGNED * ULPIP; 如何定义Flags和FragOff的比特数?

69 VC++编程实现网络嗅探器 为了使程序能成功编译,需要包含头文件winsock2.h和ws2tcpip.h。
为程序流程的清晰起见,去掉了错误检查等保护性代码。主要代码实现清单如下:

70 VC++编程实现网络嗅探器 // 启动 Winsock,WSAData为WSADATA结构对象 WSAStartup(MAKEWORD(2, 2), &WSAData); // 创建原始套接字 sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)); // 设置IP头操作选项,其中flag 设置为ture,亲自对IP头进行处理 setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)); // 获取本机名 gethostname((char*)LocalName, sizeof(LocalName)-1); // 根据名字获取本地 IP 地址 pHost = gethostbyname((char*)LocalName));

71 VC++编程实现网络嗅探器 // 填充SOCKADDR_IN结构 addr_in.sin_addr = *(in_addr *)pHost-> h_addr_list[0]; addr_in.sin_family = AF_INET; addr_in.sin_port = htons(57274); // 把原始套接字sock 绑定到本地网卡地址上 bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in)); // dwValue为输入输出参数,为1时执行,0时取消 DWORD dwValue = 1; // 设置 SOCK_RAW 为SIO_RCVALL,以便接收所有的IP包(混杂模式)。其中SIO_RCVALL 的定义为: #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) ioctlsocket(sock, SIO_RCVALL, &dwValue);

72 VC++编程实现网络嗅探器 while (true) { // 接收原始数据包信息 int ret = recv(sock, RecvBuf, BUFFER_SIZE, 0); //缓冲区长度BUFFER_SIZE定义为65535 if (ret > 0) { // 对数据包进行分析,并输出分析结果 ip = *(IP*)RecvBuf; tcp = *(TCP*)(RecvBuf + ip.HdrLen); TRACE("协议: %s\r\n",GetProtocolTxt(ip.Protocol)); TRACE("IP源地址: %s\r\n",inet_ntoa(*(in_addr*)&ip.SrcAddr)); TRACE("IP目标地址: %s\r\n",inet_ntoa(*(in_addr*)&ip.DstAddr)); TRACE("TCP源端口号: %d\r\n",tcp.SrcPort); TRACE("TCP目标端口号:%d\r\n",tcp.DstPort); TRACE("数据包长度: %d\r\n\r\n\r\n",ntohs(ip.TotalLen)); } }

73 VC++编程实现网络嗅探器 其中,在进行协议分析时,使用了GetProtocolTxt()函数,该函数负责将IP包中的协议(数字标识的)转化为文字输出,该函数实现如下: #define PROTOCOL_STRING_ICMP_TXT "ICMP" #define PROTOCOL_STRING_TCP_TXT "TCP" #define PROTOCOL_STRING_UDP_TXT "UDP" #define PROTOCOL_STRING_SPX_TXT "SPX" #define PROTOCOL_STRING_NCP_TXT "NCP" #define PROTOCOL_STRING_UNKNOW_TXT "UNKNOW" …… CString CSnifferDlg::GetProtocolTxt(int Protocol) { switch (Protocol){ case IPPROTO_ICMP : //1 /* control message protocol */ return PROTOCOL_STRING_ICMP_TXT; case IPPROTO_TCP : //6 /* tcp */ return PROTOCOL_STRING_TCP_TXT; case IPPROTO_UDP : //17 /* user datagram protocol */ return PROTOCOL_STRING_UDP_TXT; default: return PROTOCOL_STRING_UNKNOW_TXT; }

74 VC++编程实现网络嗅探器 在本示例中将分析结果用TRACE()宏进行输出,在调试状态下运行,得到的一个分析结果如下:
协议: UDP IP源地址: IP目标地址: TCP源端口号: TCP目标端口号: 数据包长度: 78 …… 协议: TCP IP源地址: IP目标地址: TCP源端口号: TCP目标端口号:10 数据包长度: 200 ……

75 Winsock编程流程及实例 参见WORD文档— windows socket编程实战 (winsock实现流程及主要函数说明)
winsock 编程实例(基于连接的点对点实时通信程序.它由两部分组成,服务器在主机UNIX下直接运行, 客户机在Windows下运行)

76 课程设计2 设计一个基于某一种I/O方法的socket (winsock)通信程序,要求具备下列基本功能:
系统配置界面:能通过界面配置通信双方的IP地址、端口号 通过界面输入待发送的数据,按“确定”键后发送 通过界面显示收到的数据 设置一个退出按钮,按下该按钮后退出程序的运行

77 Thank You!


Download ppt "网络编程技术 授课教师:谭献海 Email:xhtan@swjtu.edu.cn."

Similar presentations


Ads by Google