socket高并发解决方案Word下载.docx
《socket高并发解决方案Word下载.docx》由会员分享,可在线阅读,更多相关《socket高并发解决方案Word下载.docx(20页珍藏版)》请在冰点文库上搜索。
什么缘故要将数据的表示与其存储、处置分离开来?
提供了一个专门好的例子。
在Web上,你无需操纵最终用户用来访问你数据的平台和软件。
你能够考虑编写出适用与每一种潜在的目标平台的应用程序。
?
客户机/效劳器应用程序的效劳器部份?
治理通过量个客户机访问效劳器的、多个用户共享的资源。
说明?
客户机/效劳器程序的效劳器部份?
壮大功能的最好例子应该是Web效劳器,它通过Internet将HTML页传递给不同的Web用户。
Java编程语言中最大体的特点是在Java中创建的程序的代码的可移植性。
因为具有其他语言所不具有的代码可移植性,Java许诺用户只要编写一次应用程序,就能够够在任何客户机系统上发布它,并能够让客户机系统说明该程序。
这意味着:
你只要写一次代码,就能够使其在任何平台上运行。
2协议
当你同朋友交谈时,你们遵循一些暗含的规那么(或协议)。
例如:
你们俩不能同时开始说话,或持续不中断地说话。
若是你们如此作的话,谁也不能明白得对方所说的东西。
当你说话时,你的朋友倾听,反之亦然。
你们以两边都能明白得的语言和速度进行对话。
当运算机之间进行通信的时候,也需要遵循必然的规那么。
数据以包的形式从一台机械发送到另一台。
这些规那么治理数据打包、数据传输速度和从头数据将其恢复成原始形式。
这些规那么被称为网络协议。
网络协议是通过网络进行通信的系统所遵循的一系列规那么和老例。
连网软件通常实现有高低层次之分的多层协议。
网络协议的例子有:
TCP/IP、UDP、AppleTalk和NetBEUI。
Java提供了一个丰硕的、支持网络的类库,这些类使得应用程序能方便地访问网络资源。
Java提供了两种通信工具。
它们是:
利用用户报文协议(UDP)的报文和利用传输操纵协议/因特网协议(TCP/IP)的Sockets(套接字)。
数据报包是一个字节数组从一个程序(发送程序)传送到另一个(同意程序)。
由于数据报遵守UDP,不保证发出的数据包必需抵达目的地。
数据报并非是可信任的。
因此,仅当传送少量数据时才利用,而且发送者和同意者之间的距离距离不大,假设是网络交通顶峰,或同意程序正处置来自其他程序的多个请求,就有机遇显现数据报包的丢失。
Sockets套接字用TCP来进行通信。
套接字模型同其他模型相较,优越性在于其不受客户
请求来自何处的阻碍。
只要客户机遵循TCP/IP协议,效劳器就会对它的请求提供效劳。
这意味着客户性能够是任何类型的运算机。
客户机再也不局限为UNIX、Windows、DOS或Macintosh平台,因此,网上所有遵循TCP/IP协议的运算性能够通过套接字相互通信。
3Sockets套接字
Sockets概况
在客户机/效劳器应用程序中,效劳器提供象处置数据库查询或修改数据库中的数据之类的效劳。
发生在客户机和效劳器之间的通信必需是靠得住的,同时数据在客户机上的顺序应该和效劳器发送出来的顺序相同。
什么是套接字?
既然咱们已经明白套接字扮演的角色,那么剩下的问题是:
什么是套接字?
BruceEckel在他的《Java编程思想》一书中如此描述套接字:
套接字是一种软件抽象,用于表达两台机械之间的连接“终端”。
关于一个给定的连接,每台机械上都有一个套接字,您也能够想象它们之间有一条虚拟的“电缆”,“电缆”的每一端都插入到套接字中。
固然,机械之间的物理硬件和电缆连接都是完全未知的。
抽象的全数量的是使咱们不必明白没必要明白的细节。
简言之,一台机械上的套接字与另一台机械上的套接字交谈就创建一条通信通道。
程序员能够用该通道来在两台机械之间发送数据。
当您发送数据时,TCP/IP协议栈的每一层都会添加适当的报头信息来包装数据。
这些报头帮忙协议栈把您的数据送到目的地。
好消息是Java语言通过"
流"
为您的代码提供数据,从而隐藏了所有这些细节,这也是什么缘故它们有时候被叫做流套接字(streamingsocket)的缘故。
把套接字想成两头上的听筒,我和您通过专用通道在咱们的听筒上发言和聆听。
直到咱们决定挂断,对话才会终止(除非咱们在利用蜂窝)。
而且咱们各自的线路都占线,直到咱们挂断。
若是想在没有更高级机制如ORB(和CORBA、RMI、IIOP等等)开销的情形下进行两台运算机之间的通信,那么套接字就适合您。
套接字的低级细节相当棘手。
幸运的是,Java平台给了您一些尽管简单但却壮大的更高级抽象,使您能够容易地创建和利用套接字。
传输操纵协议(TCP)提供了一条靠得住的、点对点的通信通道,客户机/效劳器应用程序能够用该通道相互通信。
要通过TCP进行通信,客户机和效劳器程序成立连接并绑定套接字。
套接字用于处置通过网络连接的应用程序之间的通信。
客户机和效劳器之间更深切的通信通过套接字完成。
Java被设计成一种连网语言。
它通过将连接功能封装到套接字类里而使得网络编程加倍容易。
套接字类即Socket类(它创建一个客户套接字)和ServerSocket类(它创建一个效劳器套接字)。
套接字类大致介绍如下:
lSocket是基类,它支持TCP协议。
TCP是一个靠得住的流网络连接协议。
Socket类提供了流输入/输出的方式,使得从套接字中读出数据和往套接字中写数据都很容易。
该类关于编写因特网上的通信程序而言是必不可少的。
lServerSocket是一个因特网效劳程序用来监听客户请求的类。
ServerSocket事实上并非执行效劳;
而是创建了一个Socket对象来代表客户机。
通信由创建的对象来完成。
IP地址和端口
因特网效劳器能够被以为是一组套接字类,它们提供了一样称为效劳的附加功能。
效劳的例子有:
电子邮件、远程登录的Telnet、和通过网络传输文件的文件传输协议(FTP)。
每种效劳都与一个端口相联系。
端口是一个数值地址,通过它来处置效劳请求(就象请求Web页一样)。
TCP协议需要两个数据项:
IP地址和端口号。
因此,当键入时,你是如何进入金诺的主页呢?
因特网协议(IP)提供每一项网络设备。
这些设备都带有一个称为IP地址的逻辑地址。
由因特网协议提供的IP地址具有特定的形式。
每一个IP地址都是32位的数值,表示4个范围在0到255之间的8位数值金诺已经注册了它的名字,分派给的IP地址为。
注意:
域名效劳或DNS效劳是将翻译成的效劳。
这使你能够键入而没必要记住IP地址。
想象一下,怎么可能记居处有需要访问的站点的IP地址!
有趣的是一个网络名能够映射到许多IP地址。
关于常常访问的站点可能需要这一功能,因为这些站点容纳大量的信息,并需要多个IP地址来提供业务效劳。
的实际的内部名称为。
DNS能够将分派给jinnuoLtd.的一系列IP地址翻译成。
若是没有指明端口号,那么利用效劳文件中效劳器的端口。
每种协议有一个缺省的端口号,在
端口号未指明时利用该缺省端口号。
端口号应用
21FTP.传输文件
23Telnet.提供远程登录
25SMTP.传递邮件信息
67BOOTP.在启动时提供配置情形
80HTTP.传输Web页
109POP.利用户能访问远程系统中的邮箱
让咱们再来看一下URL:
URL的第一部份(http)意味着你正在利用超文本传输协议(HTTP),该协议处置Web文档。
若是没有指明文件,大多数的Web效劳器会取一个叫文件。
因此,IP地址和端口既能够通过明确指出URL各部份来决定,也能够由缺省值决定。
4创建Socket客户
咱们将在本部份讨论的例如将说明在Java代码中如何利用Socket和ServerSocket。
客户机用Socket连接到效劳器。
效劳器用ServerSocket在端口1001侦听。
客户机请求效劳器C:
驱动器上的文件内容。
创建RemoteFileClient类
import*;
publicclassRemoteFileClient{
protectedBufferedReadersocketReader;
protectedPrintWritersocketWriter;
protectedStringhostIp;
protectedinthostPort;
//构造方式
publicRemoteFileClient(StringhostIp,inthostPort){
=hostIp;
=hostPort;
篇二:
高并发效劳器实现方案研究
高并发效劳器实现方案研究
摘要:
讲述并发效劳器设计的要紧技术,包括多进程效劳器、多线程效劳器和I/0复用效劳器,同时对以上效劳器技术的性能进行了简要分析,给出了在Linux操作系统下利用socket实现并发效劳器的方式。
关键词:
并发效劳器;
多进程;
多线程;
I/0复用
网络效劳器是网络软件的核心部份,Linux由于其源码开放,性能稳固,因此在效劳器市场中占有的份额愈来愈多,许多经典的效劳器(如wEB效劳器)都利用Linux系统。
随着网络应用的不断增多,效劳器的效劳类型也不断增多,而且性能要求愈来愈高,开发高性能的网络效劳器是大势所趋。
效劳器设计技术有很多,按利用的协议来分有TCP效劳器和u)P效劳器。
按处置方式来分有迭代效劳器和并发效劳器。
一个好的效劳器,一样都是并发效劳器。
并发效劳器设计技术一样有:
多进程效劳器、多线程效劳器、I/O复用效劳器等。
下面,给出了这3种并发效劳器的设计与实现。
1多进程并发效劳器
在Linux环境下多进程的应用很多,其中最要紧的确实是网络/客户效劳器。
可是在电信、金融等数据量和稳固性要求都比较高的环境中,也会涉及到多进程的应用。
多进程效劳器是当客户有请求时,效劳器用一个子进程来处置客户请求。
父进程继续等待其它客户的请求。
这种方式的优势是当客户有请求时,效劳器能及时处置客户,专门是在客户效劳器交互系统中。
关于一个TCP效劳器,客户与效劳器的连接可能并非马上关闭,可能会等到客户提交某些数据后再关闭,这段时刻效劳器端的进程会阻塞,因此这时操作系统可能调度其它客户效劳进程。
比起迭代效劳器大大提高了效劳性能。
1.1多进程效劳器传统方式
多进程效劳器设计方式也很多,传统方式是父进程进行监听(TCP效劳器),当有连接抵达时,生成一个子进程处置客户请求,以后父进程继续监听。
因此有时把父进程称为监听进程。
其模板如下:
intmain(void)
{listen(1istenfd,雕屺KLlⅪ);
/*listenfd是监听套接字*/
while
(1){
if
((connfd=accept(sOckfd,NULL,NULL))==~1){
/*错误处置*/
}
if((pid=f6rk())>
0){
cbe(connfd);
/*父进程关闭客户连接套接字*/
conntinue:
elseif(pid==0){
close(1istenfd);
/*子进程关闭监听套接字*/
?
/*处置客户请求*/
close(∞nnfd);
eXit(0);
程序需专门注意的地址是父进程要关闭连接套接字,子进程要关闭监听套接字。
因为fork以后,子进程是父进程的一个复制品,它们都有监听套接字和连接套接字。
若是父进程不关闭连接套接字,当子进程关闭连接套接字时,那个套接字并非会关闭,因为每一个套接字描述符都有一个“引用计数”。
当fork函数返回后,Iistenfd和connfd的引用计数变成2,而系统只有在描述符的“引用计数”为O时,才真正关闭该描述符。
子程序退出后,还有一些状态信息留给父进程,父进程应挪用waitpid函数清理子进程,因为子进程终止后,它变成一个僵尸进程,父进程若是不清理僵尸进程,随着客户终止数量的增多,僵尸进程愈来愈多,将耗掉系统资源。
清理僵尸进程的
一样方式是:
让父进程收到SIGCHLD号后挪用waitpid函数。
代码如下:
void
sig—chld(intsigno)
{pid—tpid;
intstat;
whiIe((pid=waitpid(一1,&
stat,WNOHANG))>
0)
printf(“child%dterminated\n”,pid);
return:
程序中sig—chld是一个信号处置程序。
当子进程终止时,会向父进程发送一个SIGCHLD信号,因此在父进程中应捕捉那个信号。
方式能够是:
signal(SIGCHLD,sig—chld)。
注意程序顶用的是waitpid函数,而不是wait函数。
缘故是多个子进程可能同时终止,这种情形下wait函数只能处置一个子进程。
挪用waitpid函数可能处置多个已终止子进程。
关于waitpid函数的参数可查看linux的帮忙文档。
这种传统的多进程效劳器有很多缺点:
(1)消耗系统资源。
因为一个进程有自已的地址空间、执行堆栈和文件描述符等。
创建进程要复制父进程的执行堆栈、文件描述符等给子进程。
因此创建一个进程的系统开销较大。
若是处置客户的时刻比较短,而客户的连接比较多,如此系统大部份时刻都花在创建进程和终止进程,造成了极大的浪费。
(2)进程状态难以了解。
若是在以上情形下某一个子进程发生了延迟或其他异样,长期处在等待的状态下,父进程全然无法对如此众多的子进程进行监控,因此就会对子进程无法操纵。
(3)进程数难以操纵。
在每次开启子进程时,父进程能够判定是不是达到了最大进程数,若是达到了,那么需要等待子进程的释放。
如此固然能够操纵最大的进程,可是不能依照需要自主调剂进程数。
下面是一种改良的多进程并发效劳器。
1.2预先派生子进程效劳器
预先派生子进程效劳器程序是效劳器启动时就派生多个子进程,做好为接入的客户作效劳预备。
这种效劳器的工作方式是:
启动程序后派生出N个进程,每一个子进程都挪用accept函数并由内核置入眠眠状态。
当第一个客户连接到来时,N个子进程均被唤醒,但只有最先被调度的进程才能取得客户连接,其它的进程再次转入眠眠。
这种现象称这为惊群问题。
这会阻碍效劳器的性能,但较传统的多进程效劳器有较大的改良,客户连接时当即就有一个进程为其效劳,提高了效率,惊群问题对性能的阻碍要紧与阻塞的进程数量有关拉J,因此在连接量较小时应取消一些子进程。
还有一种改良的方式是accept利用文件锁爱惜,确实是让应用进程在挪用accept前后设置某种形式的锁,如此每次只有一个子进程阻塞在accept挪用,其它子进程那么阻塞在试图获取提供挪用accept权利的锁上,这种方式对惊群问题有专门大的改善。
预先派生子进程技术的优势是:
不需要引入父进程执行fork的开销,新客户就能够得处处置,提高了实时处置的能力。
缺点是:
每次启动效劳器时,父进程必需猜想到底需要预先派生多少子进程,而且若是再也不考虑派生子进程,一旦所有子进程都为客户所占用,新到的请求就将被临时忽略,直到有一个进程可用。
2多线程并发效劳器
多线程效劳器是对多进程的效劳器的改良,由于多进程效劳器在创建进程时要消耗较大的系统资源,因此用线程来取代进程,如此效劳处置程序能够较快的创建。
据统计,创建线程与创建进程要快10100倍,因此又把线程称为“轻量级”进程。
线程与进程不同的是:
一个进程内的所有线程共享相同的全局内存、全局变量等信息。
这种机制又带来了同步问题。
以下是多线程效劳器模板:
void*start—routine(void*arg);
intmain(void){
intlistenfd,connfd;
pthread—ttid;
typearg;
/*CreateTCPSocket*/
/*BindSocket
toaddreSs*/
/*Listen*/
/*
Acceptconnection*/
if((pthread—create(&
tid,NULL,start—routine,(void*)&
arg))
/*handleexceDtion*/
}
程序中pthread—create(&
arg)用于创建一个线程,tid用于存储线程ID,start—routine是线程将要执行的函数。
arg是给执行函数传递的参数。
第二个参数是线程属性,那个地址设置为空,说明是缺省设置。
创建新线程后,由新线程来处置客户的请求,但主线程继续等待客户连接。
由于同一个进程的线程共享相同的全局变量等信息,因此带来了同步问题。
为了使多个线程访问一个全局变量而不引发混乱,必需利用同步机制。
线程的同步机制有:
互斥锁、条件变量等。
下面以互斥锁为例来介绍。
在主线程中将一个互斥锁变量初始化,在线程中访问公共变量时加锁,利用完了解锁。
若是一个线程加了锁,其它线程只有等待该线程完成对这一互斥锁解锁后,才能完成加锁操作。
代码如下:
pthread—mutex—tmymutex=刚READ—MUTEX—INITIALIZER;
/*互斥锁变量声明和初始化*/
void*thread—function(void*arg)
{
pthread—mutex—lock(&
mymutex);
/*力Ⅱ锁*/
/*关键区代码*/
pthread—mutex—unlock(&
m)恤utex);
/*解锁*/
线程除可能访问一些共享变量外,还可能需要私有全局变量。
这能够利用TSD(线程特定数据)来实现。
其方式如下:
pthread—key—create(&
key,(void)echomsg);
此函数用于创建一个键。
那个函数应在主线程中执行,函数执行时,系统搜索key结构数组(系统为每一个进程保护一个如此的数组)并找到第一个未被利用的项。
其下标称做键(key),那个下标返回给挪用线程,每一个线程能够给该键存储一个值(具体原理可参阅文献2)。
如此在线程中能够对该键存储一个值或取值,而每一个线程可不能彼此阻碍。
用如下函数实现:
pthread—Setspecific(key,(void)tid);
/*设置一个值*/
pthread—getspeci“c(key)/*取值*/
注意create—key—create函数的第二个参数,它是一个析构函数,当线程退出时被挪用。
用于释放线程特定数据。
上述多线程服器设计技术是每当一个新的连接请求,就创建一个新线程。
其实也可像预先派生子进程一样,预先生成许多新线程。
每一个线程挪用accept同意连接请求。
为了保证一个客户请求只有一个线程处置,利用互斥锁来保证只有一个线程阻塞在accept上。
接收到一个连接后,互斥锁解锁,新线处置客户请求,其它线程中会有一个线程阻塞于accept,等待其它客户的请求。
这种方式的优势是客户请求时不需要创建新线程,实时性较好。
3I/O复用并发效劳器
I/O复用技术是为了解决进程或线程阻塞到某个I/O系统挪用而显现的技术,使进程不阻塞于某个特定的I/O系统挪用。
它也可用于并发效劳器的设计。
但很多情形下它是与多线程和多进程一路利用。
以下是I/O复用的一个程序:
fd—Setinfds:
FOr(;
;
){
FD~ZEf的(&
infds);
/*重置描述符集*/
FD~SET(fileno(stdin),&
/*把标准输入置人描述符集*/
FD~SET(s。
ckfd,&
/*把socket置入描述符集*/
nlaxfd=H1ax(fileno(stdin),sockfd)+1;
If(Select(maxfd,&
infds,NULL,NULL,NULL)==一1)
{/*错误处置*/}
if(FD—ISSET(sockfd,&
infds))/*测试sOcket是不是可读*/
{/*读socket*/}
if(FD—ISSET(fileno(stdin),&
infds))/*测试标准输入是不是可读*/
{/*读标准输入*/}
此程序不是一个网络效劳器程序,可是它能够说明I/O复用的工作原理。
此程序阻塞于select,而不是真正的I/o系统挪用,Select的第二个参数是读描述符集。
在那个描述符集中是一些套接口描述字和标准输入描述字。
若是描述符集中没有一个描述符可读,那么进程阻塞,直到有一个描述符可读或超时(第5个参数可设置超不时刻)才返回。
返回后,要判定是哪个描述符可读,依次进行处置。
select的