2.1 基本原理
2.1.1 网络应用的体系结构
客户机/服务器结构(Client-Server, C/S)
服务器:7*24小时提供服务;永久性访问地址/域名;利用大量服务器实现可扩展性
客户机:与服务器通信,使用服务器提供的服务;间歇性接入网络;可能使用动态IP地址;不会与其他客户机直接通信
Web应用
点对点结构(Peer-to-peer, P2P)
没有永远在线的服务器,任意端系统/节点之间可以直接通讯,节点间歇性接入网络,节点可能改变IP地址
优点在于高度可伸缩,缺点在于难于管理
文件传输应用
混合结构(Hybrid)
文件传输使用P2P结构,文件的搜索采用C/S结构——集中式
每个节点向中央服务器登记自己的内容,而在查找感兴趣的内容时向中央服务器提交查询请求
Napster
2.1.2 网络应用进程通信
同一主机上运行的进程之间由操作系统提供进程间通信机制
不同主机上运行的进程间靠消息交换通信,进程间通信利用socket发送/接收消息实现
进程寻址:通过标识符(IP地址+端口号)在不同主机上的进程间通信
使用IP地址对主机寻址
为主机上每个需要通信的进程分配一个端口号,使用端口号对主机上的进程寻址
- HTTP Server: 80
- Mail Server:25
应用层协议:网络应用需遵循应用层协议
- 公开协议:由RFC定义的,允许互操作的协议(HTTP、SMTP)
私有协议:多数P2P文件共享应用
应用层协议内容
- 消息的类型(type):请求消息、响应消息
- 消息的语法(syntax)/格式:消息中有哪些字段(field)、每个字段如何描述
- 字段的语义(semantics):字段中信息的含义
- 规则(rules):进程何时发送/响应消息、进程如何发送/响应消息
2.1.3 网络应用的服务需求
- 数据丢失(data loss)/可靠性(reliability)
- 某些网络应用能够容忍一定的数据丢失:网络电话
- 某些网络应用要求100%可靠的数据传输:文件传输,telnet
- 时间(timing)/延迟(delay)
- 有些应用只有在延迟足够低时才“有效”,如:网络电话/网络游戏
- 带宽(bandwidth)
- 某些应用只有在带宽达到最低要求时才“有效”:网络视频
- 某些应用能够适应任何带宽——弹性应用:email
TCP服务 | UDP服务 | |
---|---|---|
连接 | 面向连接: 客户机/服务器进程间需要建立连接 | 无连接 |
传输 | 可靠的传输 | 不可靠的数据传输 |
流量控制 | 发送方不会发送速度过快,超过接收方的处理能力 | 不提供 |
拥塞控制 | 当网络负载过重时能够限制发送方的发送速度 | 不提供 |
时间/延迟保障 | 不提供 | 不提供 |
带宽保障 | 不提供最小带宽保障 | 不提供 |
2.2 web应用
2.2.1 HTTP连接
HTTP连接使用TCP传输服务:
服务器在80端口等待客户的请求
浏览器发起到服务器的TCP连接(创建套接字Socket)
服务器接受来自浏览器的TCP连接
浏览器(HTTP客户端)与Web服务器(HTTP服务器)交换HTTP消息
关闭TCP连接
HTTP协议是无状态的,服务器不维护任何有关客户端过去所发请求的信息。
有协议的更复杂,需要维护每个用户的历史信息状态,而且当客户或者服务器失效会产生状态的不一致,解决这种不一致代价高
HTTP1.0 | HTTP1.1 |
---|---|
非持久性连接(Nonpersistent HTTP) 每个TCP连接最多允许传输一个对象 | 持久性连接(Persistent HTTP) 每个TCP连接允许传输多个对象 |
每个对象需要2个RTT 操作系统需要为每个TCP连接开销资源(overhead) | 发送响应后,服务器保持TCP连接的打开 |
无流水(pipelining)的持久性连接 | 带有流水机制的持久性连接(HTTP 1.1的默认选项) |
---|---|
客户端只有收到前一个响应后才发送新的请求 | 客户端只要遇到一个引用对象就尽快发出请求 |
每个被引用的对象耗时1个RTT | 理想情况下,收到所有的引用对象只需耗时约1个RTT |
RTT(Round Trip Time):从客户端发送一个很小的数据包到服务器并返回所经历的时间
响应时间 = 建立TCP连接的1个RTT + 发送HTTP请求消息到HTTP响应消息的前几个字节到达的1个RTT + 响应消息中所含的文件/对象传输时间 = 2RTT + 文件发送时间
2.2.2 HTTP消息格式
2.2.2.1 请求消息(request)
- HTTP 1.0
- GET方法: 输入信息通过request行的URL字段上传
- POST方法:网页经常需要填写表格(form),在请求消息的消息体(entity body)中上传客户端的输
- HEAD方法:请Server不要将所请求的对象放入响应消息中
- HTTP 1.1
- GET, POST, HEAD
- PUT:将消息体中的文件上传到URL字段所指定的路径
- DELETE:删除URL字段所指定的文件
2.2.2.2 响应消息(response)
HTTP响应状态代码,响应消息的第一行:
- 200 OK
- 301 Moved Permanently
- 400 Bad Request
- 404 Not Found
- 505 HTTP Version Not Supported
2.2.3 Cookie技术
HTTP协议无状态,但很多应用需要服务器掌握客户端的状态
Cookie技术:某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。
Cookie的组件:
- TTP响应消息的cookie头部行
- HTTP请求消息的cookie头部行
- 保存在客户端主机上的cookie文件,由浏览器管理
- Web服务器端的后台数据库
Cookie能够用于:身份认证、购物车、推荐、Web e-mail、……
但是cookie存在用户隐私泄露的问题
2.2.4 Web缓存/代理服务器技术:条件GET
为了缩短客户请求的响应时间、减少机构/组织的流量、在大范围内(Internet)实现有效的内容分发,所以提出了在不访问服务器的前提下满足客户端的HTTP请求的技术——Web缓存/代理服务器技术。
浏览器向缓存/代理服务器发送所有的HTTP请求:如果所请求对象在缓存中,缓存返回对象;否则,缓存服务器向原始服务器发送HTTP请求,获取对象,然后返回给客户端并保存该对象。所以缓存既充当客户端,也充当服务器,一般由ISP(Internet服务提供商)架设。
条件性GET方法
目标:如果缓存有最新的版本,则不需要发送请求对象
缓存:在HTTP请求消息中声明所持有版本的日期(在HTTP请求消息有 If-modified-since: <date>)
服务器:如果缓存的版本是最新的,则响应消息中不包含对象(响应消息为 HTTP/1.0 304 Not Modified),如果不是最新的,正常返回请求对象
在HTTP响应消息中有一行
Last-Modified
header line,表明最后一次的修改时间
2.3 Email应用
Email应用的构成组件
邮件客户端(user agent):Client应用或者Web页面
邮件服务器
SMTP协议(Simple Mail Transfer Protocol):在邮件服务器之间传递消息使用的协议
- 客户端:发送消息的服务器
- 服务器:接收消息的服务器
2.3.1 SMTP协议
使用TCP在25端口建立持久性连接进行email消息的可靠传输,传输过程的三个阶段:握手、消息的传输、关闭。
命令/响应交互模式:
命令(command):ASCII文本
响应(response):状态代码和语句
Email消息只能包含7位ASCII码
SMTP服务器利用CRLF.CRLF
确定消息的结束。
SMTP与HTTP对比:
HTTP是拉式(pull),而SMTP是推式(push)
HTTP的每个对象封装在独立的响应消息中,SMTP的多个对象在由多个部分构成的消息中发送
都使用命令/响应交互模式
命令和状态代码都是ASCII码
2.3.2 Email消息格式
Email消息由三部分组成:
- 头部行(header)
- To
- From
- Subject
- 空白行
- 消息体:消息本身,只能是ASCII字符
而为了传输非文本类型的数据,对如上的消息做出了扩展。
MIME:多媒体邮件扩展。通过在邮件头部增加额外的行以声明MIME的内容类型。
2.3.3 邮件访问协议
邮件访问协议:从服务器获取邮件
- POP (Post Office Protocol):认证/授权(客户端<—->服务器)和下载
- IMAP (Internet Mail Access Protocol):更多功能、更加复杂、能够操纵服务器上存储的消息
- 所有消息统一保存在一个地方:服务器
- 允许用户利用文件夹组织消息
- 支持跨会话(Session)的用户状态:文件夹的名字、文件夹与消息ID之间的映射等
- HTTP:163, QQ Mail等(网页版使用)
2.3.3.1 POP3协议
- 认证过程
- 客户端命令
- User:声明用户名
- Pass:声明密码
- 服务器响应
- +OK
- -ERR
- 客户端命令
- 事务阶段
- List:列出消息数量
- Retr:用编号获取消息
- Dele:删除消息
- Quit
POP协议有两种模式:
“下载并删除”模式:用户如果换了客户端软件,无法重读该邮件
“下载并保持”模式:不同客户端都可以保留消息的拷贝
POP3是无状态的
2.3.3.2 IMAP协议
所有消息统一保存在一个地方:服务器
允许用户利用文件夹组织消息,支持跨会话(Session)的用户状态:文件夹的名字、文件夹与消息ID之间的映射等
2.4 DNS应用
DNS:Domain Name System 域名解析系统
- 多层命名服务器构成的分布式数据库
- 应用层协议:完成名字的解析。Internet核心功能,用应用层协议实现
DNS服务:域名向IP地址的翻译;主机别名;邮件服务器别名;Web服务器负载均衡
2.4.1 分布式层次式数据库
根域名服务器:本地域名解析服务器无法解析域名时,访问根域名服务器。
顶级域名服务器(TLD, top-level domain):负责com, org, net,edu等顶级域名和国家顶级域名,例如cn, uk, fr等。
权威(Authoritative)域名服务器:组织的域名解析服务器,提供组织内部服务器的解析服务。组织或服务提供商负责维护。
本地域名解析服务器:不严格属于层级体系,每个ISP有一个本地域名服务器,是默认域名解析服务器。
当主机进行DNS查询时,查询被发送到本地域名服务器,作为代理(proxy),将查询转发给(层级式)域名解析服务器系统。
- 迭代查询:被查询服务器返回域名解析服务器的名字。“我不认识这个域名,但是你可以问题这服务器”
- 递归查询:将域名解析的任务交给所联系的服务器
只要域名解析服务器获得域名—IP映射,即缓存这一映射,一段时间过后,缓存条目失效(删除)
本地域名服务器一般会缓存顶级域名服务器的映射,因此根域名服务器不经常被访问
2.4.2 DNS记录
资源记录(RR, resource records),RR format: (Name, Value, Type, TTL)
- Type=A
- Name:主机域名
- Value:IP地址
- Type=NS
- Name:域(edu.cn)
- Value:该域权威域名解析服务器的主机域名
- Type=CNAME
- Name:某一真实域名的别名,如 www.ibm.com – servereast.backup2.ibm.com
- Value:真实域名
- Type=MX
- Value是与name相对应的邮件服务器
2.4.3 DNS协议和消息格式
DNS协议:查询 (query) 和回复 (reply) 消息,消息格式相同
消息头部:
- Identification:16位查询编号,回复使用相同的编号
- flags:查询或回复、期望递归、递归可用、权威回答
为公司 “Network Utopia”注册域名:
在域名管理机构(如Network Solutions)注册域名networkutopia.com。向域名管理机构提供你的权威域名解析服务器的名字和IP地址,域名管理机构向com顶级域名解析服务器中插入两条记录
(networkutopia.com, dns1.networkutopia.com, NS)
(dns1.networkutopia.com, 212.212.212.1, A)
在权威域名解析服务器中为www.networkuptopia.com加入Type A记录,为networkutopia.com加入Type MX记录
2.5 FTP
file transfer protocol,基于C/S架构。
FTP有两个连接,一个用于控制,一个用于数据传输。FTP客户端使用TCP在端口21联系FTP服务器,通过控制连接授权客户端,客户端浏览远程目录,通过控制连接发送命令,当服务器接收文件传输命令时,服务器打开第二个TCP数据连接(用于文件)到客户端,传输一个文件后,服务器关闭数据连接。
控制连接被称为带外传输
2.6 P2P应用
2.6.1 原理
- 没有服务器
- 任意端系统之间直接通信
- 节点阶段接入Internet
- 节点可能更换IP地址
问题:从一个服务器向N个节点分发一个文件F需要多长时间?
- 设:
- u~s~ ——服务器上传带宽
- u~i~ ——节点i的上传带宽
- d~i~ ——节点i的下载带宽
- C/S架构
- 服务器发送N个副本的时间:NF/u~s~
- 客户机i的下载时间:F/d~i~
- 分发N个F所需时间:d~cs~=max{ NF/u~s~ , F/min{d~i~} }——时间关于N是线性增长的
- P2P
- 服务器必须发送一个副本(最小的消耗时间):F/u~s~
- 客户机i的下载时间:F/d~i~
- 下载总量:NF bits
- 最快的上传速率(服务器和所有的节点都在上传):u~s~ + Σu~i~
- 分发N个F所需要的时间:d~P2P~ = max { F/u~s~, F/min(d~i~) , NF/(u~s~ + Σu~i~) }——非线性的
2.6.2 文件分发:BitTorrent
- 洪流(torrent):参与一个特定文件分发的所有对等方的集合
- 文件块(chunk):一个洪流中的对等方下载等长度的文件块,典型长度256KB
- 追踪器(tracker):一个对等方加入一个洪流时,它向追踪器注册自己并周期性地通知追踪器它还在线
BitTorrent
文件划分为256KB的chunk
节点加入torrent时没有chunk,但是会逐渐积累;每个节点向tracker注册以获得节点清单,与某些节点(“邻居”)建立连接
下载的同时,节点需要向其他节点上传chunk
节点可能加入或离开:一旦节点获得完整的文件,它可能(自私地)离开或(无私地)留下
获取chunk
- 给定任一时刻,不同的节点持有文件的不同chunk集合
- 节点(Alice)定期查询每个邻居所持有的chunk列表
- 节点发送请求,请求获取缺失的chunk:稀缺优先
发送chunk:tit-for-tat
- Alice向正在向其发送Chunk且速率最快的4个邻居发送chunk:每10秒重新评估top 4
- 每30秒随机选择一个其他节点,向其发送chunk:新选择节点可能加入top 4,“optimistically unchoke”
2.6.3 索引技术
2.6.3.1 集中式索引
节点加入时,通知中央服务器:IP地址和内容
Alice查找“Hey Jude”
Alice从Bob处请求文件
存在的问题:内容和文件传输是分布式的,但是内容定位是高度集中式的
- 单点失效问题
- 性能瓶颈
- 版权问题
2.6.3.2 洪泛式查询:Query flooding
完全分布式架构(Gnutella采用这种架构)——每个节点对它共享的文件进行索引,且只对它共享的文件进行索引、
覆盖网络(overlay network):Graph
节点X与Y之间如果有TCP连接,那么构成一个边
所有的活动节点和边构成覆盖网络
边:虚拟链路
节点一般邻居数少于10个
查询消息通过已有的TCP连接发送,节点转发查询消息,如果查询命中,则利用反向路径发回查询节点。
2.6.3.3 层次是覆盖网络
介于集中式索引和洪泛查询之间的方法
每个节点或者是一个超级节点,或者被分配一个超级节点
节点和超级节点间维持TCP连接,某些超级节点对之间维持TCP连接
超级节点负责跟踪子节点的内容
2.7 Socket编程
2.7.1 应用编程接口(API)
- 应用层(Application):Web/RPC/中间件编程
- Socket编程
- NetBIOS编程——Windows
- 传输层(Transport)
- 网络层(Network)
- 基于NDIS网络编程——Windows
- 基于LibPcap/WinPcap、Libnet、Libnids、Libicmp编程
- 数据链路层(Data link)
- 直接网卡编程,硬件相关的
- 基于Packet Driver编程,屏蔽网卡细节,适用于所有网卡
- 物理层(Physical)
API:为了使应用层的应用进程可以和相邻层(传输层)传递数据,需要一个接口,即API。所以API就是应用进程的控制权和操作系统的控制权进行转换的一个系统调用接口
典型的应用编程接口:
- socket interface(socket、套接字)——Berkeley UNIX
Windows Socket Interface(WINSOCK)——微软
- Transport Layer Interface(TLI)——AT&T UNIX 系统 V
2.7.2 Socket API概述
标识通信端点(对外):IP地址+端口号(16位整数)
操作系统/进程管理套接字(对内):套接字描述符(socket descriptor)——小整数
Socket抽象
类似于文件的抽象:当应用进程创建套接字时,操作系统分配一个数据结构存储该套接字相关信息,操作系统返回套接字描述符
操作系统维护一个套接字描述符表,存储的是指向套接字数据结构的指针
地址结构:
sockaddr_in
1 2 3 4 5 6 7 8
struct aockaddr_in { u_char sin_len; /*地址长度*/ u_char sin_family; /*地址族*/ u_short sin_port; /*端口号*/ struct in_addr sin_addr; /*IP地址*/ char sin_zero[8]; /*未用(置0)*/ }
地址族:为了适应不同协议的变化,引入了地址族,其在TCP/IP下使用的值为AF_INET
2.7.3 Socket API函数
所有的API都是在WINSOCK中的,与UNIX下的socket大同小异
2.7.3.1 WSAStartup
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
- 使用Socket的应用程序在使用Socket之前必须首先调用此函数加载DLL
- 第一个参数指明程序请求使用的WinSock版本:高位字节指明副版本、低位字节指明主版本
- 第二个参数是返回实际的WinSock的版本信息:指向WSADATA结构的指针
1
2
3
/* 例 */
wVersionRequested = MAKEWORD(2, 1);
err = WSAStartup(wVersionRequested, &wsaData);
2.7.3.2 WSACleanup
int WSACleanup (void);
应用程序在完成对请求的Socket库的使用,最后要调用WSACleanup函数解除与Socket库的绑定,释放Socket库所占用的系统资源
2.7.3.3 socket
sd = socket(protofamily,type,proto);
用于创建套接字
- 操作系统返回套接字描述符(sd)
- 第一个参数指明协议族:在TCP/IP下为PF_INET
- 第二个参数指明套接字类型:在TCP/IP下可以为SOCK_STREAM、SOCK_DGRAM或SOCK_RAW
- 第三个参数指明协议号:默认为0,当某类套接字只面向一类协议时直接使用0即可,而面向多个协议时就需要指定协议号了
Socket面向TCP/IP的服务类型
应用层 ———– 应用进程
/ \
SOCK_STREAM SOCK_DGRAM / SOCK_RAW \
传输层–TCP UDP 网络层———-IP/ICMP/IGMP
2.7.3.4 Closesocket
int closesocket(SOCKET sd)
- unix下该函数名是
close
,也就是关闭文件的函数 - 该函数是关闭描述符为sd的套接字
- 但如果多个进程共享一个套接字,调用该函数是将套接字引用计数减1,减至0才关闭,它清除掉的只是该进程中对它的引用
- 而一个进程中的多线程对一个套接字的使用是无计数的,也就是说,在一个线程中关闭了一个套接字意味着其他线程也不能再访问该套接字了
- 返回0表示成功,返回SOCKET_ERROR表示失败
2.7.3.5 bind
int bind(sd, localadr, addrlen);
- 绑定套接字的本地端点地址:IP地址+端口号
- 由于在创建SOCKET的时候可能没有对应的地址信息,所以要进行地址信息的绑定
- 客户程序一般不需要调用bind函数,操作系统自动设置
- 服务端需要绑定端口号,而不能绑定特定的IP,一旦绑定了特定IP意味着其他IP就不能访问该服务器了,所以为了解决该问题,服务端绑定的是一个地址通配符INADDR_ANY
2.7.3.6 listen
int listen(sd, queuesize);
- C/S架构下将服务器端的流套接字置为监听状态,所以此函数仅被服务器调用,仅用于面向连接(TCP)的流套接字
- 第二个参数设置连接请求队列的大小,服务器从队列中提取
- 返回0表示成功,返回SOCKET_ERROR表示失败
2.7.3.7 connect
connect(sd,saddr,saddrlen);
使客户套接字(sd)与特定计算机的特定端口(saddr)的套接字(服务)进行连接
仅用于客户端,可用于TCP客户端,也可用于UDP客户端
- TCP客户端:建立TCP连接,客户端调用此函数对服务器发起连接请求
- UDP客户端:指定服务器端点地址,UDP是无连接的,所以即使调用
commect
成功,也有可能无法与服务器通信
2.7.3.8 accept
newsock = accept(sd,caddr,caddrlen);
- 服务程序调用
accept
从处于监听状态的流套接字sd的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道- 如果直接使用服务器的主套接字则服务器在同一时刻只能与一个客户端连接,所以使用创建新套接字来达到并行连接的目的(并发的TCP服务器)
- 仅用于服务器,仅用于TCP套接字
2.7.3.9 send, sendto
send(sd,*buf,len,flags);
sendto(sd,*buf,len,flags,destaddr,addrlen);
send
是没有指定服务器的地址的,也就是说连接已经建立了,故可用于:- TCP套接字(客户端与服务器端均可)
- 调用了
connect
的UDP客户端的套接字(连接模式的UDP套接字)
sendto
函数用于UDP服务器端套接字与未调用connect
函数的UDP客户端套接字
2.7.3.10 recv, recvfrom
recv(sd,*buffer,len,flags);
recvfrom(sd,*buf,len,flags,senderaddr,saddrlen);
recv
函数从TCP连接的另一端接收数据,或者从调用了connect
函数的UDP客户端套接字接收服务器发来的数据recvfrom
函数用于从UDP服务器端套接字与未调用connect
函数的UDP客户端套接字接收对端数据
2.7.3.11 setsockopt, getsockopt
int setsockopt(int sd, int level, int optname, *optval, int optlen);
int getsockopt(int sd, int level, int optname, *optval, socklen_t *optlen);
setsockopt
函数用来设置套接字sd的选项参数getsockopt
函数用于获取任意类型、任意状态套接口的选项当前值,并把结果存入optval
2.7.4 网络字节顺序
由于五层网络模型中不存在表示层,所以无法进行不同机器间的表示转化,于是TCP/IP定义了标准的用于协议头中的二进制整数表示:网络字节顺序(network byte order)
某些Socket API函数的参数需要存储为网络字节顺序(如IP地址、端口号等),因此实现本地字节顺序与网络字节顺序间转换的函数有:
- htons:本地字节顺序→网络字节顺序(16bits)
- ntohs:网络字节顺序→本地字节顺序(16bits)
- htonl:本地字节顺序→网络字节顺序(32bits)
- ntohl:网络字节顺序→本地字节顺序(32bits)
2.7.5 客户端软件设计
2.7.5.1 解析服务器IP地址
由于客户端可能使u用域名或IP地址来标识服务器,而IP协议需要32位的二进制IP地址,所以需要将域名或IP地址转换为32位的IP地址。
- 函数
inet_addr()
实现点分十进制IP地址到32位IP地址转换 - 函数
gethostbyname()
实现域名到32位IP地址转换,返回一个指向结构hostent的指针
以上两个函数得到的已经是网络字节顺序,可以直接使用。
2.7.5.2 解析服务器端口号
客户端还可能使用服务名(如HTTP)标识服务器端口,因此需要将服务名转换为熟知端口号。
- 函数
getservbyname()
,返回一个指向结构servent的指针
2.7.5.3 解析协议号
客户端可能使用协议名(如:TCP)指定协议,因此需要将协议名转换为协议号(如:6)
- 函数
getprotobyname()
实现协议名到协议号的转换,返回一个指向结构protoent的指针
2.7.5.4 TCP/UDP客户端软件流程
TCP:
确定服务器IP地址与端口号
- 创建套接字
- 分配本地端点地址(IP地址+端口号)——不需要设计软件时手动来做,系统自动完成
- 连接服务器(套接字)——
connect()
- 遵循应用层协议进行通信——根据协议确定客户端和服务器哪方先发信息
- 关闭/释放连接
UDP:
- 确定服务器IP地址与端口号——并非只是每次的第一步做,之后可能每次都需要做
- 创建套接字
- 分配本地端点地址(IP地址+端口号)——自动完成
- 指定服务器端点地址,构造UDP数据报——
connect()
,构造的UDP数据报可以发给不同的服务器,此时需要重复做第一步 - 遵循应用层协议进行通信——一定是UDP客户端先给服务器发信息
- 关闭/释放套接字
2.7.6 服务器软件设计
循环无连接(Iterative connectionless)服务器,基本流程:
- 创建套接字
- 绑定端点地址(INADDR_ANY+端口号)
- 反复接收来自客户端的请求
- 遵循应用层协议,构造响应报文,发送给客户
数据发送:
- 服务器端不能使用
connect()
函数 - 无连接服务器使用
sendto()
函数发送数据报
获取客户端点地址:
- 调用
recvfrom()
函数接收数据时,自动提取
循环面向连接(Iterative connection-oriented)服务器,基本流程:
- 创建(主)套接字,并绑定熟知端口号;
- 设置(主)套接字为被动监听模式,准备用于服务器;
- 调用
accept()
函数接收下一个连接请求(通过主套接字),创建新套接字用于与该客户建立连接; - 遵循应用层协议,反复接收客户请求,构造并发送响应(通过新套接字);
- 完成为特定客户服务后,关闭与该客户之间的连接,返回步骤3.
并发无连接(Concurrent connectionless)服务器,基本流程:
主线程1:创建套接字,并绑定熟知端口号;
主线程2:反复调用
recvfrom()
函数,接收下一个客户请求,并创建新线程处理该客户响应;子线程1:接收一个特定请求;
子线程2:依据应用层协议构造响应报文,并调用
sendto()
发送;子线程3:退出(一个子线程处理一个请求后即终止)。
并发面向连接(Concurrent connection-oriented)服务器,基本流程:
主线程1:创建(主)套接字,并绑定熟知端口号;
主线程2:设置(主)套接字为被动监听模式,准备用于服务器;
主线程3:反复调用
accept()
函数接收下一个连接请求(通过主套接字),并创建一个新的子线程处理该客户响应;子线程1:接收一个客户的服务请求(通过新创建的套接字);
子线程2:遵循应用层协议与特定客户进行交互;
子线程3:关闭/释放连接并退出(线程终止)。
客户端与服务器的实现范例参见PPT