Presentation is loading. Please wait.

Presentation is loading. Please wait.

SOCKET编程原理.

Similar presentations


Presentation on theme: "SOCKET编程原理."— Presentation transcript:

1 SOCKET编程原理

2 内容 Socket简介 Socket常用函数介绍 TCP/IP网络程序框架与实例 通信方式 Windows Socket
Linux Socket Socket常用函数介绍 TCP/IP网络程序框架与实例 通信方式 阻塞 非阻塞

3 为什么需要Socket 普通的I/O操作过程 TCP/IP协议被集成到操作系统的内核中,引入了新型的“I/O”操作
打开文件->读/写操作->关闭文件 TCP/IP协议被集成到操作系统的内核中,引入了新型的“I/O”操作 进行网络操作的两个进程在不同的机器上,如何连接? 网络协议具有多样性,如何进行统一的操作? 需要一种通用的网络编程接口:Socket 独立于具体协议 BSD Socket(伯克利套接字)是通过标准的UNIX文件描述符和其它程序通讯的一个方法,目前已经被广泛移植到各个平台。

4 Socket类型 流式套接字(SOCK_STREAM) 数据报套接字(SOCK_DGRAM)
提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。 数据报套接字(SOCK_DGRAM) 提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。

5 Socket的位置

6 两类系统中使用的Socket 不同操作系统中的Socket Windows Socket (Winsock)
Linux Socket (BSD Socket)

7 Windows Socket 简称Winsock,是在Windows环境下使用的一套网络编程规范,基于4.3BSD的BSD Socket API制定 1991年Winsock 1.1,16位,由WINSOCK.DLL支持,主要用在Windows 95中 1997年Winsock 2.2 版,32位,由WSOCK32.DLL支持,主要用在Windows 98及以后的版本中 已经成为Windows环境下网络编程的事实标准 三类函数 与BSD Socket相兼容的基本函数 与BSD Socket相兼容的网络信息检索函数 Windows专用扩展函数

8 Linux Socket 基本上就是BSD Socket 需要使用的头文件
数据类型:#include <sys/types.h> 函数定义:#include <sys/socket.h>

9 TCP/IP网络程序框架 面向连接的C/S程序工作流程 无连接的C/S程序工作流程

10 面向连接的C/S程序工作流程(TCP) 服务器端工作流程 使用WSAStartup()函数检查系统协议栈安装情况
使用socket()函数创建服务器端通信套接口 使用bind()函数将创建的套接口与服务器地址绑定 使用listen()函数使服务器套接口做好接收连接请求准备 使用accept()接收来自客户端由connect()函数发出的连接请求 根据连接请求建立连接后,使用send()函数发送数据,或者使用recv()函数接收数据 使用closesocket()函数关闭套接口(可以先用shutdown()函数先关闭读写通道) 最后调用WSACleanup()函数结束Winsock Sockets API

11 面向连接的C/S程序工作流程(TCP) 客户端程序工作流程 使用WSAStartup()函数检查系统协议栈安装情况
使用socket()函数创建客户端套接口 使用connect()函数发出向服务器建立连接的请求(调用前可以不用bind()端口号,由系统自动完成) 连接建立后使用send()函数发送数据,或使用recv()函数接收数据 使用closesocet()函数关闭套接口 最后调用WSACleanup()函数,结束Winsock Sockets API

12 <本地IP地址,本地端口号> <远程IP地址,远程端口号>
面向连接的C/S程序工作流程(TCP) 服务器与客户端五元组的建立 五元组 <协议> <本地IP地址,本地端口号> <远程IP地址,远程端口号> 服务器端五元组 由socket()确定 由服务器端调用bind()时确定 由accept()确定 客户端五元组  由客户端的bind()调用确定。如果客户端没有进行bind()调用,或调用了bind()但没有指定具体地址或端口号,则由系统内核自动确定地址和端口 由connect()确定

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

14 无连接的C/S程序工作流程(UDP) 无连接的数据报传输服务通信时,客户端与服务器端所使用的函数是类似的,其工作流程如下:
使用WSAStartup()函数检查系统协议栈的安装情况 使用socket()函数创建套接口,以确定协议类型 调用bind()函数将创建的套接口与本地地址绑定,确定本地地址和本地端口号 使用sendto()函数发送数据,或者使用recvfrom()函数接收数据 使用closesocket()函数关闭套接口 调用WSACleanup()函数,结束Windows Sockets API

15 无连接的C/S程序工作流程(UDP) 注意事项: 通信的一方可以不用bind()绑定地址和端口,由系统分配
不绑定IP地址和端口号的一方必须首先向绑定地址的一方发送数据 无连接的应用程序也可以调用connect()函数,但是它并不向对方发出建立连接的请求,而是在本地返回,由内核将connect()中指定的目标IP地址和端口号记录下来,在以后的通信中就可以使用面向连接的数据发送函数send()和数据接收函数recv() 无连接的数据报传输过程中,作为服务器的一方必须先启动 无连接客户端一般不调用connect(),在数据发送前客户与服务器各自通过socket()和bind()建立了半相关,发送数据时除指定本地套接口的地址外,还需要指定接收方套接口地址,从而在数据收发过程中动态建立全连接

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

17 阻塞通信与非阻塞通信 阻塞方式:套接字进行I/O操作时,函数要等待到相关的操作完成以后才能返回,对提高处理机的利用率不利,但编程简单。
阻塞方式编程简单,一个套接口的默认操作模式为阻塞,可以调用函数ioctlsocket()进行设置。

18 并发服务器

19 Socket常用函数介绍 基本函数 网络信息检索函数

20 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

21 基本函数(续) 转换函数 IP地址转换函数 字节排序函数 inet_addr() 点分十进制数表示的IP地址转换为网络字节序的IP地址
inet_ntoa() 网络字节序的IP地址转换为点分十进制数表示的IP地址 字节排序函数 htonl 4字节主机字节序转换为网络字节序 ntohl  4字节网络字节序转换为主机字节序 htons 2字节主机字节序转换为网络字节序 ntohs 2字节网络字节序转换为主机字节序

22 网络信息检索函数 网络信息检索函数 gethostname 获得主机名 getpeername 获得与套接口相连的远程协议地址
getsockname 获得套接口本地协议地址 gethostbyname 根据主机名取得主机信息 gethostbyaddr 根据主机地址取得主机信息 getprotobyname 根据协议名取得主机协议信息 getprotobynumber 根据协议号取得主机协议信息 getservbyname 根据服务名取得相关服务信息 getservbyport 根据端口号取得相关服务信息 getsockopt/setsockopt 获取/设置一个套接口选项 ioctlsocket 设置套接口的工作方式

23 Windows中的Socket编程 Windows中的Socket编程 Winsock 的启动 Winsock API基本函数
TCP/IP网络程序框架(C/S模式) 阻塞与非阻塞通信方式 实例程序说明

24 Winsock Winsock是一个基于Socket模型的API,在Windows系统中广泛使用
需要包含头文件Winsock2.h,需要使用库ws2_32.lib,包含办法可以用语句来告诉编译时调用该库   #pragma comment(lib,”ws2_32.lib”); 如果使用Visual C++ 6.0,可以通过“工程” > “设置”>“工程设置”>“链接”>“对象/库模块”中加入“ws2_32.lib”

25 Windows Socket的启动 使用Winsock API编制的网络应用程序中,在调用任何一个Winsock函数之前都必须检查协议栈安装情况,使用函数WSAStartup()完成操作。 int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData ); wVersionRequested是一个WORD型(双字节型)数值,指定使用的版本号,对Winsock2.2而言,此参数的值为0x0202,也可以用宏MAKEWORD(2,2)来获得 lpWSAData是一个指向WSADATA结构的指针,它返回关于Winsock实现的详细信息

26 Winsock启动示例 #include <Winsock2.h> WORD wVersionRequested;
WSADATA wsaData; wVersionRequested=MAKEWORD(2,2); if(WSAStartup(wVersionRequested,&wsaData)!=0) { //Winsock初始化错误 return; } if(wsaData.wVersion!=wVersionRequested) //Winsock版本不匹配 WSACleanup(); }//说明WinsockDLL正确加载,可以执行以下代码

27 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

28 创建套接口socket() 应用程序在使用套接口通信前,必须要拥有一个套接口,使用socket()函数来给应用程序创建一个套接口。
int af, int type, int protocol );

29 socket()参数说明 af参数说明套接字接口要使用的协议地址族,地址族与协议族含义相同。如果想建立一个TCP或UDP,只能用常量AF_INET表示使用互联网协议(IP)地址。Winsock还支持其他协议,但一般很少使用。 type参数描述套接口的类型,af是AF_INET的时候只能为SOCK_STREAM、SOCK_DGRAM或SOCK_RAW protocol说明该套接口使用的特定协议,当协议地址族af和协议类型type确定后,协议字段可以使用的值是限定的 协议 地址族 套接口类型 套接口类型使用的值 协议字段 互联网协议(IP) AF_INET TCP SOCK_STREAM IPPROTO_TCP UDP SOCK_DGRAM IPPROTO_UDP Raw SOCK_RAW IPPROTO_RAW IPPROTO_ICMP 使用socket()后,实际指定了五元组中的“协议”这一元

30 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

31 指定本地地址-bind() 当socket()创建了一个套接口后,需要将该套接口与该主机上提供服务的某端口联系在一起,bind()函数用于完成这样的绑定。 int bind( SOCKET s, const struct sockaddr FAR * name, int namelen ); Bind()指定了五元组中本地的二元

32 bind()参数说明 s标识一个未绑定的套接口描述字,它是socket()函数调用成功时返回的值
name是一个与指定协议有关的地址结构指针,存储了套接口的地址信息,Winsock中使用sockaddr_in结构指定IP地址和端口信息 struct sockaddr_in{ short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; } sin_family一般为AF_INET,表示使用网络字节序的IP地址族;sin_port是以网络字节序表示的16位端口号;sin_addr是32位IP地址;sin_zero字段一般不用,用0填充 IP地址参数为INADDR_ANY,则由系统内核来自动指定 port为0,则由系统自动指派一个1024~5000之间惟一的端口号 namelen表示地址参数(name)的长度

33 bind()实例 #include <Winsock2.h> SOCKET s; sockaddr_in tcpaddr;
int iSockErr; int port=5000; //端口号 s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); tcpaddr.sin_family=AF_INET; tcpaddr.sin_port=htons(port); tcpaddr.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(s,(LPSOCKADDR)&tcpaddr,sizeof(tcpaddr))==SOCKET_ERROR){ iSockErr=WSAGetLastError(); //根据不同的错误类型进行不同的处理 return; } 函数调用成功,进行其他处理。

34 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

35 服务器端启动监听-listen()函数 在一个服务器端用socket()调用成功创建了一个套接口,并用bind()函数和一个指定的地址关联后,就需要指示该套接口进入监听连接请求状态,这需要通过listen()函数来实现 int listen( SOCKET s, int backlog ); s代表一个已绑定了地址,但还未建立连接的套接口描述字 backlog指定了正在等待连接的最大队列长度 Backlog用于限制最大请求的个数,目前为5

36 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

37 客户端请求连接-connect()函数 当服务器端建立好套接口并与一个本地地址绑定后,就进入监听状态,等待客户发出连接请求。在客户端套接口建立好之后,就调用connect()函数来与服务器建立连接。 int connect( SOCKET s, const struct sockaddr FAR * name, int namelen );

38 connect()函数参数说明 s将要建立连接的套接口描述字
name是一个指向远端套接口地址结构(sockaddr_in)的指针,表示s套接口欲与其建立一条连接 namelen是服务器端的地址长度,即name的长度

39 Connect()函数的说明 在客户端使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立一条到服务器TCP的连接。如果调用该函数前没有调用bind()来绑定本地地址,则由系统隐式绑定一个地址到该套接口 该函数用在UDP的客户端时,connect()函数并不是真正地发出建立请求连接的请求,调用将从本地操作系统直接返回。这样可以将服务器的地址信息保存下来,在后续UDP端口发送数据时,由套接口自动在发送函数中填入服务器地址,而不需要由应用程序在调用发送函数时填入

40 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

41 服务器端接受连接-accept()函数 在服务器端通过listen()函数调用表示服务器进入监听客户的连接请求状态,而在服务器端调用accept()函数时表示可以接收来自客户端由connect()发出的连接请求,双方进入连接状态。 SOCKET accept( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen );

42 accept()函数参数说明 s标识一个套接字,该套接口处于监听状态
addr是一个地址结构的指针,用来存放发出连接请求的那个客户机的IP地址信息 addrlen指出客户套接口地址结构的长度 函数说明:该函数用于面向连接的服务器端,在IP协议族中,只用于TCP服务器端

43 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

44 发送数据-send()函数 在已经建立连接的套接口上发送数据,可以使用send()函数 int send( SOCKET s,
const char FAR * buf, int len, int flags );

45 send()函数参数说明 s用于标识已建立连接的套接字 buf是一个字符缓冲区,内有将要发送的数据 len即将发送的缓冲区中的字符数
flags用于控制数据传输方式,0表示按正常方式发送数据; 宏MSG_DONTROUTE说明系统目标主机就在直接连接的本地网络中,无需路由选择; MSG_OOB指出数据是按带外数据发送的,如urgency 函数说明:send()函数适用于已建立连接的数据报或流式套接口发送数据,对于数据报类型必须注意发送数据长度不大于通信子网的IP包最大长度

46 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

47 接收数据-recv()函数 对于已建立连接的套接口来说,要从套接口上接收数据,就要使用recv()函数。 int recv(
SOCKET s, char FAR * buf, int len, int flags );

48 recv()函数参数说明 s为已建立连接的套接口 buf为用于接收数据的缓冲区 len为缓冲区的长度
flags指定调用的方式。0表示接收的是正常数据,无特殊行为。MSG_PEEK表示会使有用的数据复制到所提供的接收端缓冲区内,但是没有从系统缓冲区中将数据删除。MSG_OOB表示处理带外数据。

49 无连接的套接口上接收数据-recvfrom()
int recvfrom( SOCKET s, char FAR * buf, int len, int flags, struct sockaddr FAR * from, int FAR * fromlen );

50 recvfrom()函数参数说明 s标识一个套接口的描述字 buf接收数据的缓冲区 len接收数据缓冲区的长度
flags调用操作方式,同recv()中的flags from可选指针,指向装有源地址的缓冲区 fromlen可选指针,指向from缓冲区的长度值 函数说明:该函数的用法与有连接时recv()的用法一致,也可以用于有连接时数据的接收

51 在无连接套接口上发送数据-sendto()
int sendto( SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen );

52 sendto()函数参数说明 s本机的套接字 buf待发送数据的缓冲区 len指明buf缓冲区中要发送的数据长度
flags调用方式标志位,同send()中的flags to可选指针,指向接收数据的目的套接口地址 tolen是to所指的地址的长度 函数说明:该函数的使用方法类似send()函数,当用于无连接套接字接口,调用函数前要设置,指出目标IP地址和目标端口号。如果用于有连接的套接口时,则不能指定目标地址和目标端口,将to设置为空,地址长度设为0。

53 基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字

54 关闭读写通道-shutdown()函数 在一个套接口上的读写操作完成后,应该首先使用shutdown()函数来关闭套接口的读通道、写通道或读写通道,这样做的好处是当双方不再有数据要发送或接收时,可以通知对方,以防止数据丢失,并能“优雅”地关闭连接 int shutdown( SOCKET s, int how );

55 shutdown()函数参数说明 1 2 s标识一个套接口的描述字 how是一个标志,用于描述禁止哪些操作,取值如下表所示 关闭方式 参数值
说  明 SD_RECEIVE 表示不允许再调用接收函数,它关闭读通道。套接口接收缓冲区中的所有数据都被丢弃,并且有新数据到达套接口时,也被TCP协议层丢弃,但它对发送缓冲区没有影响,进程仍然可以在套接口上发送数据 SD_SEND 1 表示不允许再调用发送函数,它关闭写通道。在套接口发送缓冲区中的数据都被发送出去,得到接收端确认之后,就生成一个FIN包关闭连接。但它对接收缓冲区没有影响,进程仍然可以在套接口上接收数据 SD_BOTH 2 关闭读写通道,相当于执行了上面SD_RECEIVE和SD_SEND两个命令

56 关闭套接口-closesocket()函数
shutdown函数只关闭读写通道,并不关闭套接口,且套接口所占有的资源将被一直保留到closesocket()调用之前。 一个套接口不再使用时一定要关闭这个套接口,以释放与该套接口关联的所有资源,包括等候处理的数据。 int closesocket( SOCKET s ); 参数s表示即将被关闭的套接口

57 基本函数(续) 转换函数 IP地址转换函数 字节排序函数 inet_addr() 点分十进制数表示的IP地址转换为网络字节序的IP地址
inet_ntoa() 网络字节序的IP地址转换为点分十进制数表示的IP地址 字节排序函数 htonl 4字节主机字节序转换为网络字节序 ntohl  4字节网络字节序转换为主机字节序 htons 2字节主机字节序转换为网络字节序 ntohs 2字节网络字节序转换为主机字节序

58 IP地址转换函数 char * inet_ntoa ( struct in_addr in )
in为传入参数,表示一个结构型的IP主机地址,该函数将一个32位数字表示的IP地址转换成点分十进制IP地址字符串 unsigned long inet_addr(const char FAR * cp)  该函数将一个点分十进制IP地址字符串转换成32位数字表示的IP地址。 两函数互为反函数

59 字节序转换函数 u_long htonl( u_long hostlong )
4字节主机字节序表示的整数转换为4字节相应的网络字节序表示的整数 u_short htons( u_short hostshort ) 2字节主机字节序表示的整数转换为2字节相应的网络字节序表示的整数 u_long ntohl( u_long netlong ) 4字节网络字节序表示的整数转换为4字节相应的主机字节序表示的整数 u_short ntohs( u_short netshort ) 2字节网络字节序表示的整数转换为2字节相应的主机字节序表示的整数

60 终止使用Winsock-WSACleanup()函数
当应用程序不再使用Winsock API中的任何函数时,必须调用WSACleanup()将其从Windows Socket的实现中注销,以释放为此应用程序或DLL分配的任何资源。 int WSACleanup(void); 函数说明:WSACleanup()函数是任何一个Winsock应用程序在最后必须要调用的函数。在一个多线程的环境下,WSACleanup()函数中止了Windows Sockets在所有线程上的操作

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

62 The End


Download ppt "SOCKET编程原理."

Similar presentations


Ads by Google