[TOC]
优点 | 缺点 | |
---|---|---|
电路交换 | 双方可以随时通信,实时性强. 双方通信时按发送顺序传送数据,不存在失序问题. | 电路交换的平均连接建立时间较长. 信道利用低. |
报文交换 | 不需要为通信双方预先建立一条专用的通信线路。 通信双方不是固定占有一条通信线路,提高了通信线路的利用率. | 经历存储、转发这一过程,从而引起转发时延. 要求网络中每个结点有较大的缓冲区 |
分组交换 | 加速了数据在网络中的传输 简化了存储管理 减少了出错机率和重发数据量 | 每个分组都要加上源、目的地址和分组编号等信息,使传送的信息量大 可能出现失序、丢失或重复分组 |
C/S 架构是一种典型的两层架构,其全称是Client/Server,即客户端、服务器端架构,其客户端包含一个或多个在用户的电脑上运行的程序,而服务器端有两种,一种是数据库服务器端,客户端通过数据库连接访问服务器端的数据;另一种是Socket服务器端,服务器端的程序通过Socket与客户端的程序通信。
C/S 架构也可以看做是胖客户端架构。因为客户端需要实现绝大多数的业务逻辑和界面展示。
C/S优点和缺点
优点:
缺点:
B/S架构的全称为Browser/Server,即浏览器/服务器结构。主要事务逻辑在服务器端实现,B/S架构的系统无须特别安装,只有Web浏览器即可。因此也被成为瘦客户端。
必须强调的是C/S和B/S并没有本质的区别:B/S是基于特定通信协议(HTTP)的C/S架构,也就是说B/S包含在C/S中,是特殊的C/S架构。
B/S优点和缺点
优点:
缺点:
UDP: 无连接交互
TCP: 面向连接的交互
应用程序只在以下情况使用UDP:
服务器所维护的与客户交互的信息成为状态信息。不保存任何状态信息的服务器成为无状态服务器,反之成为有状态服务器。
有状态服务器在服务器中保存少量信息,可减少客户端与服务器端交换报文的大小,保存了客户之前有过的请求,允许服务器快速的相应请求。
特点
优点
缺点
无状态服务器的动机是协议的不可靠性。客户请求报文必须指定操作类型、文件名、传输数据在文件中的位置和传输字节数、要写入的文件数据等。
特点:
优点
无状态服务器则不会因为报文丢失,失序等问题导致状态信息出错,出现问题。
缺点
每次都要携带额外的状态信息,产生额外的数据。
并发:
当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间
段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。这种方式我们称之为并发(Concurrent)。
并行:
当系统有一个以上CPU时,
当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
区别:
并行是指两个或者多个事件在同一时刻发生;
并发是指两个或多个事件在同一时间间隔内发生。是指在一段时间内宏观上有多个程序在同时运行,在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。
进程的概念
进程是表示资源分配的基本单位。它是一个执行某一个特定程序的实体,它拥有独立的地址空间、执行堆栈、文件描述符等。
线程的概念
有时被称为轻量级进程,线程是进程中执行运算的最小单位,亦即执行处理机调度的基本单位。
进程和线程的联系
一个进程至少拥有一个线程——主线程,也可以拥有多个线程;一个线程必须有一个父进程。
多个进程可以并发执行;一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
进程与线程的区别:
调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
同步:
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
异步:
异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
阻塞:
是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
阻塞调用和同步调用实际上是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
非阻塞:
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
fork相当于复制了一个进程的执行版本。以当前进程作为父进程创建出一个新的子进程,并且将父进程的所有资源拷贝给子进程,这样子进程作为父进程的一个副本存在。父子进程几乎时完全相同的,但也有不同的如父子进程pid不同。
fork后,父子进程具有相同的数据空间、代码空间、堆栈、所有的文件描述字;但相互之间互不影响。
fork函数有三个返回值
那么fork函数为什么是一次调用,却返回了两次呢?
当程序执行到下面的语句: pid=fork();
由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的。
系统调用execl执行另一个程序。调用execl并不创建新进程,所以前后的进程ID并未改变,execl只是用另一个新程序替换了当前进程的正文、数据、堆栈;
#include
int execl(const char *path, const char *arg, ...);
path 是要执行的二进制文件或脚本的完整路径。
arg是要传给程序的完整参数列表,包括arg[0],一般是执行程序的名字。
最后一个参数可为NULL
exec函数一共有六个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数。fork是分身术,exec变身术。
阻塞I/O
进程会一直阻塞,直到数据拷贝完成
非阻塞I/O
非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的;
I/O复用(select 和poll)
可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。
信号驱动I/O
允许套接口进行信号驱动I/O,并使用一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
异步I/O
数据拷贝的时候进程无需阻塞
同步IO引起进程阻塞,直至IO操作完成。IO复用是先通过select调用阻塞。异步IO不会引起进程阻塞。
同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞!
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!
套接字是一个主机本地网络应用程序所创建的, 为操作系统所控制的接口 (“门”) .
应用进程通过这个接口,使用传输层提供的服务, 跨网络发送(或接收)消息.
Client/server模式的通信接口——套接字接口.
创建方式相同,使用方式不同
等待传入连接的套接字——被动,如服务器套接字
发起连接的套接字——主动,如客户套接字
指明端点地址:创建时不指定,使用时指明,允许协议族自由的选择地址表示方式
TCP/IP需要指明协议端口号和IP地址
TCP/IP协议族和地址族的对应:
TCP/IP协议族:PF_INET
对应的TCP/IP的地址族:AF_INET
PF_INET: TCP/IP的协议族
AF_INET: TCP/IP的地址族
struct sockaddr 是通用的地址结构
struct socketaddr_in 是IP专用的地址结构
SSOCK_DGRAM: 数据报服务,UDP协议
SSOCK_STREAM: 流服务,TCP协议
socket函数
int Socket( int domain, int type, int protocol)
功能:创建一个新的套接字,返回套接字描述符
参数说明:
domain:域类型,指明使用的协议栈,
AF_INET:IPv4协议,
AF_INET6:IPv6协议,
AF_LOCAL:Unix域协议,
AF_ROUTE:路由套接口,
AF_KEY:密钥套接口
type: 指明需要的服务类型, AF_INET地址族下如
SOCK_DGRAM: 数据报服务,UDP协议
SOCK_STREAM: 流服务,TCP协议
SOCKET_RAW:提供传输层以下的协议,例如接收和发送ICMP报文
protocol:一般都取0(由系统根据服务类型选择默认的协议)
IPPROTO_TCP、
IPPROTO_UDP、
IPPROTO_ICMP
请创建一个用于TCP通信的套接字。
举例:
s=socket(AF_INET,SOCK_STREAM,0)
send函数
int send(int sockfd, const void * data, int data_len, unsigned int flags)
功能:
在TCP连接上发送数据,返回成功传送数据的长度,出错时返回-1。
send会将外发数据复制到OS内核中,也可以使用send发送面向连接的UDP报文。
参数说明:
sockfd:套接字描述符
data:指向要发送数据的指针
data_len:数据长度
flags:通常为0,设置为 MSG_DONTWAIT为非阻塞
记住如果send()函数的返回值小于len的话,则你需要再次发送剩下的数据。
802.3,MTU为1492B,如果包小于1K,那么send()一般都会一次发送光的。
举例(p50):
send(s,req,strlen(req),0);另外可尝试send的阻塞效果
recv函数
int recv(int sockfd, void *buf, int buf_len,unsigned int flags);
功能:
从TCP接收数据,返回实际接收的数据长度,出错时返回-1。
服务器使用其接收客户请求,客户使用它接受服务器的应答。如果没有数据,将阻塞。
如果TCP收到的数据大于(/小于)缓存的大小,只抽出能够填满缓存的足够数据(/抽出所有数据并返回它实际接收的字节数)。
也可以使用recv接收面向连接的UDP的报文,若缓存不能装下整个报文,填满缓存后剩下的数据将被丢弃。
参数说明:
Sockfd:套接字描述符
Buf:指向内存块的指针
Buf_len:内存块大小,以字节为单位
flags:一般为0(MSG_WAITALL接收到指定长度数据时才返回),设置为 MSG_DONTWAIT为非阻塞
举例:
recv(sockfd,buf,8192,0)
sendto函数(UDP)
int sendto(int sockfd, const void * data, int data_len, unsigned int flags, struct sockaddr *remaddr,sock_len remaddr_len)
功能:基于UDP发送数据报,返回实际发送的数据长度,出错时返回-1
参数说明:
sockfd:套接字描述符
data:指向要发送数据的指针
data_len:数据长度
flags:通常为0,设置为 MSG_DONTWAIT为非阻塞
remaddr:远端地址:IP地址和端口号
remaddr_len :地址长度
举例:
sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&address, sizeof(address));
recvfrom函数(UDP)
int recvfrom(int sockfd, void *buf, int buf_len,unsigned int flags,struct sockaddr *from,sock_len *fromlen);
功能:从UDP接收数据,返回实际接收的字节数,失败时返回-1
参数说明:
Sockfd:套接字描述符
buf:指向内存块的指针
buf_len:内存块大小,以字节为单位
flags:一般为0
from:远端的地址,IP地址和端口号
fromlen:远端地址长度
举例:
recvfrom(sockfd,buf,8192,0,(struct sockaddr *)&address, &sizeof(address));
close函数
close(int sockfd);
功能:
撤销套接字.
如果只有一个进程使用,立即终止连接并撤销该套接字,如果多个进程共享该套接字,将引用数减一,如果引用数降到零,则关闭连接并撤销套接字。
参数说明:
Sockfd:套接字描述符
举例:
close(socket_descriptor)
bind函数
int bind(int sockfd,struct sockaddr * my_addr,int addrlen)
功能:为套接字指明一个本地端点地址
TCP/IP协议使用sockaddr_in结构,包含IP地址和端口号,服务器使用它来指明熟知的端口号,然后等待连接。
参数说明:
Sockfd: 套接字描述符,指明创建连接的套接字
my_addr: 本地地址,IP地址和端口号
addrlen: 地址长度
为什么TCP服务端需要调用bind函数而客户端通常不需要呢?
客户端使用socket服务时,操作系统随之指定一个不会产生冲突的端口给客户端。在很多场景下, 我们要在一个pc上开启多个客户端进程, 如果指定固定端口, 必然会造成端口冲突, 影响通信!
参考链接
举例:
struct sockaddr_in server_addr; /*服务器地址结构*/ /*设置服务器地址*/ bzero(&server_addr, sizeof(server_addr)); /*清零*/ server_addr.sin_family = AF_INET; /*协议族*/ server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*本地地址*/ server_addr.sin_port = htons(PORT); /*服务器端口*/ /*绑定地址结构到套接字描述符*/ err = bind(ss, (struct sockaddr*)&server_addr, sizeof(server_addr));
accept函数
int accept(int sockfd, struct sockaddr *addr, int *addrlen);
功能:获取传入连接请求,返回新的连接的套接字描述符。
为每个新的连接请求创建了一个新的套接字,服务器只对新的连接使用该套接字,原来的监听套接字接收其他的连接请求。新的连接上传输数据使用新的套接字,使用完毕,服务器将关闭这个套接字。
参数说明:
Sockfd: 套接字描述符,指明正在监听的套接字
addr: 提出连接请求的主机地址
addrlen: 地址长度
举例:
new_sockfd = accept(sockfd, (struct sockaddr *)&address, sizeof(address));
listen函数
#include
int listen(int sockfd, int backlog);
/**
* 监听socket
*
* @param sockfd socket文件描述符
* @param backlog 提示内核监听队列的最大长度
* @return 函数执行成功返回0,失败返回-1
*/
connect函数
int connect(int sockfd,struct sockaddr *server_addr,int sockaddr_len)
功能: 同远程服务器建立主动连接,成功时返回0,若连接失败返回-1。
参数说明:
Sockfd: 套接字描述符,指明创建连接的套接字
Server_addr: 指明远程端点:IP地址和端口号
sockaddr_len: 地址长度
举例(P49):
/*连接服务器*/ connect(s, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));
大于一个字节的变量类型的表示方法有两种:
小端字节序(Little Endian,LE):在表示变量的内存地址的起始地址存放低字节,高字节顺序存放;
大端字节序(Big Endian,BE):在表示变量的内存地址的起始地址存放高字节,低字节顺序存放。
网络字节顺序:最高位字节在前(大端字节序)
主机字节序:一般和主机制造商的规定有关,不同PC字节序也不同
主机字节序和网络字节序的转换
#include
uint32_t htonl(uint32_t hostlong); /*主机字节序到网络字节序的长整型转换*/
uint32_t ntohl(uint32_t netlong); /*网络字节序到主机字节序的长整型转换*/
uint16_t htons(uint16_t hostshort); /*主机字节序到网络字节序的短整型转换*/
uint16_t ntohs(uint16_t netshort); /*网络字节序到主机字节序的短整型转换*/
TCP/IP和应用程序之间的接口应该是不精确指明的:
优点:
缺点:
扩展文件描述符: 可以用于网络通信
扩展read和write: 可以用于网络标识符
额外功能的处理,增加新系统调用:
服务端:
socket()
bind()
listen()
accept()
read()、write()
并进行数据处理close()
客户端:
socket()
connect()
read()、write()
并进行数据处理close()
过程。用户层和内核层交互过程
信号是发生某件事情时的一个通知。信号将事件发送给相关的进程,相关进程可以对信号进行捕捉并处理。信号的捕捉由系统自动完成,信号处理函数的注册通过函数signal()完成。
signal(int signum, sighandler_t handler);
参数signum指出要设置处理方法的信号:
SIGINT:用于终止进程运行向当前活动的进程发送这个信号。通常是由Ctrl+C终止进程造成的,与Ctrl+C一 致,kill命令默认发送SIGINT信号。
SIGPIPE:正在写入套接字的时候,当读取端已经关闭时,可以得到一个SIGPIPE信号。
第二个参数handler是一个处理函数。
void sig_int(int sign)
{
printf("Catch a SIGINT signal\n");
/*释放资源*/
}
signal(SIGINT, sig_int);
服务器端:
socket()
,生成套接字文件描述符。bind()
函数,将套接字文件描述符和一个地址类型变量进行绑定。recvfrom()
函数接收客户端的网络数据。sendto()
函数向服务器主机发送数据。close()
函数释放资源。客户端:
socket()
;struct sockaddr_in
;sendto()
;recvfrom()
;close()
。bind()
int s;
s = socket(AF_INET, SOCK_DGRAM, 0);
UDP报文丢失数据
对策:客户端和服务端会对超时的数据进行重发。
UDP数据发送中的乱序
主要是由于路由的和路由的存储转发的顺序不同造成的。路由器的存储转不同发可能造成数据顺序的更改。
对策:可以采用发送端在数据段中加入数据报序号的方法
UDP协议中的connect()函数
connect()函数在TCP协议中会发生三次握手,建立一个持续的连接,一般不用于UDP。在UDP协议中使用connect()函数的作用仅仅表示确定了另一方的地址,并没有其他的含义。
UDP缺乏流量控制
UDP协议没有TCP协议所具有的滑动窗口概念,接收数据的时候直接将数据放到缓冲区中。当缓冲区满的时候,后面到来的数据会覆盖之前的数据造成数据的丢失。
对策:增大接收数据缓冲区 或 接收方接收单独处理
在编译程序时,将服务器的域名或者IP地址说明为常量
执行快,但是服务器移动后不便
要求用户在启动程序时指定服务器
使用机器名,不必重新编译客户程序
从稳定的存储设备中获得关于服务器的信息
如果文件不存在,客户软件就不能执行
使用某个单独的协议来找到服务器(如广播或组播)
只能在本地小环境下应用
TCP不保持记录的边界,面向流的概念,多次接收。
原因:大块数据被分片封装发送或由于接收方接收缓冲小而数据被发方分次发送
/**
* 客户发送请求,等待响应
* 发送请求:send;
* 等待响应:recv;
*/
send(s, req, strlen(req), 0);
while ((n = recv (s, bptr, buflen, 0)) > 0)
{
bptr +=n;
buflen -=n;
}
ugethostbyname:
主机域名到二进制的转换
接受一个机器域名字符串,返回一个hostent结构,内含一个二进制表示的主机IP地址
getservbyname:
两个参数指明期望的服务和协议。返回servent类型的结构指针;
注意按网络字节序返回协议端口号;
getprotobyname:
由协议名返回协议号;
返回一个protoent类型结构的地址
TCP客服服务器中客户端connect接口完成的四项任务
TCP 面向连接的算法:
UDP无连接客户端算法
本章与第八第九章内容相似,故结合第八第九章一起复习。
使用TCP的服务器是面向连接的服务器
使用UDP的服务器是无连接的服务器
选择要考虑TCP和UDP的语义特点
TCP的语义
UDP的语义
面向连接服务的优点:
易于编程
自动处理分组丢失,分组失序问题
自动验证数据差错,处理连接状态
面向连接服务的缺点:
无连接服务的优点:没有资源耗尽问题
无连接服务的缺陷:需要自己完成可靠通信问题
特殊情况
是否需要组播或者广播是考虑选择何种传输方式的一个因素
支持组播或者广播的服务器必须是无连接的,今后会不断增加这样的应用。
状态信息:服务器维护的,关于它和客户正进行的交互状态信息
无状态服务器:没有保留任何状态信息
状态服务器:维护状态信息的服务器
状态问题源于对确保可靠性的要求,特别对无连接传输
传输协议不能保证可靠,应用协议的设计必须保证可靠
优化服务器
观测响应时间
客户发送请求到服务器响应之间的全部时延。
请求处理时间
服务器处理单个孤立的请求所花费的时间。
循环服务器观测响应时间为N/2+1的推导
假设服务器处理一个任务的时间为t,队列第一个等待的时间是t ,第二个等待的时间为:2t ,这样第N个等待的时间为Nt ,所以等待的总时间变为1t+2t+…+Nt=(1+N)Nt/2.所以平均等待时间为(1+N)Nt/2/N=
(1+N)t/2=(N/2+1/2)t,因此约等于(N/2+1 )个服务器处理时间.
判断循环服务器是否够用
如果一个服务器设计处理K个客户,每个客户每秒发送R个请求,服务器请求处理时间必须小于每请求1/KR秒。否则请求队列将溢出。这时设计者必须考虑并发实现
基本类型 | 特点 |
---|---|
循环的 无连接 | 对每个请求的处理少,通常为无状态的,简单服务,计算少 |
循环的 面向连接 | 要求可靠传输的,对每个请求处理少的服务 |
并发的 无连接 | 不常见,为每个请求创建一个新线程或进程 |
并发的 面向连接 | 最一般的。可靠传输,并发处理多个请求。 |
结合8,9章的进程结构模型图,一起复习。
进程结构
使用一个单执行线程
使用两个套接字
进程结构:只需要一个执行进程
并发面向连接单线程进程服务器进程结构
单线程服务器的线程结构
单线程、并发服务器的线程和套接字结构
可以通过共享内存的线程达到期望的并发,但当:
客户的问题或者使用了阻塞系统调用的情况对于循环服务器以及使用单线程实现的并发服务中死锁都可能发生
TIME服务器选用什么模型最合适?
TIME服务几乎不需要什么计算,可以利用循环实现
Time服务需要实时性,因此选择无连接方式
因此循环无连接服务器模型最为合适
循环的面向连接的服务器每处理一个连接循环一次
连接达到以前在accept阻塞
建立新的连接以后创建新套接字处理
处理完毕,关闭,返回accept阻塞
DAYTIME服务
不需要客户的请求信息,检测到连接就响应
发送完响应,服务器主动关闭连接
每个连接只发送一个响应
DAYTIME服务用循环面向连接的服务模型比较好
优点
更高的效率:上下文切换的额外开销减少
共享存储:
缺点
由于线程间共享存储和进程状态,一个线程的动作可能对同一个进程内的其他线程产生影响。
并发服务器可以在一个进程中用若干线程实现
优点是:
缺点是:
单线程并发服务器必须完成原来主线程和从线程双方的职责
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
- int maxfdp是一个整数值,是指描述符集合中所有文件描述符的范围,即所有文件描述符的最大值加1;
- fd_set *readfds是指向fd_set结构的指针,如果有一个文件可读,select就会返回一个大于0的值,表示有文件可读
- fd_set *writefds,如果有一个文件可写,select就会返回一个大于0的值
- fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
- struct timeval* timeout是select的超时时间,
Select函数有三种执行结果:
Select函数的返回值如下:
多协议服务器(TCP、UDP)
一个服务器的服务可以同时在TCP和UDP传输协议之上来提供。
多服务服务器( DayTIME、TIME、Echo )
利用同一台服务器提供多种服务
多协议多服务服务器(DayTIME、TIME、Echo 、TCP、UDP)
利用同一台服务器提供多种服务,而且可以通过不同的协议进行传输。
在任何时候,一个循环的多协议服务器至少打开3个套接字。最初,服务器打开一个UDP和一个TCP套接字。当一个请求到达UDP套接字,服务器会计算出相应,通过UDP套接字返回给客户端。当一个TCP请求到达TCP套接字时,服务器调用accept获得这个新的连接。accept为这个连接穿件第三个套接字,服务器使用这个套接字与客户端通信。一旦交互结束,服务器将关闭第三个套接字,并等待另外两个套接字激活。
单线程并发多协议服务器是有多个用于一个TCP链接的套接字
打开一组套接字,每一个套接字与一个熟知服务相对应。服务器使用select系统调用的等待任一套接字的数据报的到达。
先为每一种服务创建一个套接字,并将该套接字绑定在熟知服务端口上,使用select等待任一套接字上传入连接请求。只要有一个套接字就绪,服务器就调用accept获得这个新连接。accept为这个传入连接创建新的套接字。服务器使用这个新的套接字与客户交互,之后便将关闭。除了主套接字外,服务器在任何时候最多只有一个打开的附加套接字。
当一个连接请求到达时,服务器就调用一个进程,接受并直接处理这个新的连接,或者,他也可以创建一个新的从进程来处理这个新连接。实际上,一个多服务器程序可以设计成循环的处理某些服务,而对其他一些服务则并发处理。
当这个多服务服务器开始执行时,它先为每个服务穿件一个套接字,并将该套接字绑定在熟知服务端口上,,使用select等待任一套接字上传入连接请求。只要有一个套接字就绪,服务器就调用accept获得这个新连接。accept为这个传入连接创建新的套接字。服务器使用这个新的套接字与客户交互,之后便将关闭。除了主套接字外,服务器在任何时候最多只有一个打开的附加套接字。
主服务器使用fork创建一个新进程来处理每个链接。然而,与以前的设计不同,从进程以调用execve的方式用一个新的程序替代原来的代码,这个新的程序将处理所有的客户端通信。从概念上来看,使用execve就把处理各个服务同设立在连接的主服务器代码分离开了。
算法:多个从线程同时绑定在一个socket上调用recvfrom获得发送方的地址和其发送的数据报,并调用sendto应答。一个数据报到达的时候系统只唤醒一个从进程。
无连接服务器并发等级取决于到达的请求数
如果某操作系统在调用recvfrom时,会同时激活所有的从进程,请问应该使用什么技术手段加以解决,并简要描述。
互斥,调用recvfrom前申请互斥,pthread_mutex_lock
调用结束pthread_mutex_unlock
所有的从进程继承了对熟知端口套接字的访问。当各个从进程调用accept返回时,它接受新套接字以用于这个连接。虽然主进程创建了对应熟知端口的套接字,但是它并不使用这个套接字进行其他操作。图中虚线表明主进程使用该套接字的方式与从进程不同。
面向连接服务器的并发等级与活跃的连接数有关
预分配方案怎样用于不能进程并发调用accept的系统中?
使用一个共享的互斥量mutex或文件锁定,以便保证任何时候只有一个从线程能够调用accept。