2、tcp连接建立的时候3次握手,断开连接的4次握手的具体过程1
3、什么是同步?什么是异步?2
4、.什么是阻塞?什么是非阻塞?5
5、什么是阻塞IO?什么是非阻塞IO?6
6、什么是同步IO?什么是异步IO?7
7、IO模型有几种?分别是什么?8
8、Reactor和ProactorIO设计模式是什么?13
9、JavaNIO中的Buffer是什么?如何使用?16
10、Niobuffer的内部结构是什么?17
11、JavaNIO中的Channel是什么?有什么特点?18
12、JavaNIO中的Selector是什么?21
13、简单讲一下文件IO中的Path和Files22
14、select、poll和epoll的区别23
15、网络编程中设计并发服务器,使用多进程与多线程,请问有什么区别?29
15、网络编程的一般步骤29
16、TCP的全称是31
17、UDP的全称是31
18、请说出TCP和UDP的区别31
19、TCP为什么不是两次连接?而是三次握手?33
20、说明socket是什么?34
21、为什么需要端口端口是真实存在的还是虚拟的概念35
22、Java中,端口使用两个字节表示,可以表示多少个端口UDP和TCP端口是各自独立的吗35
23、URL类有什么作用35
24、基于TCP的Socket网络编程的主要步骤是什么?36
25、【上机】写出建立TCP服务器ServerSocket的代码。并说明accept方法有什么特点37
26、【上机】写出建立TCP客户端Socket的代码。并说明建立Socket后,通过什么方法Socket获得流对象37
27、基于UDP的Socket网络编程的主要步骤是什么?37
28、【上机】使用UDP的方式,完成对象的传递。38
30、NIO和传统BIO区别是什么?40
31、JavaNIO的几个核心组成部分是什么?作用分别是什么?41
35、HTTP协议中常用的请求方法有哪些50
36、常见的HTTP状态码有哪些51
37、HTTP协议中content-type指的是什么?55
38、网络传输协议本质和作用是什么?65
39、可以实现一个简单的网络协议吗?65
TCP:是面向连接的流传输控制协议,具有高可靠性,确保传输数据的正确性,有验证重发机制,因此不会出现丢失或乱序。
UDP:是无连接的数据报服务,不对数据报进行检查与修改,无须等待对方的应答,会出现分组丢失、重复、乱序,但具有较好的实时性,UDP段结构比TCP的段结构简单,因此网络开销也小。
l第一次握手是客户端connect连接到server
l第二次serveracceptclient的请求之后,向client端发送一个消息,相当于说我都准备好了,你连接上我了
l第三次就是client向server发送的,就是对第二次握手消息的确认。之后client和server就开始通讯了。
2.断开连接的4次握手,具体如下:
l断开连接的一端发送close请求是第一次握手
l另外一端接收到断开连接的请求之后需要对close进行确认,发送一个消息,这是第二次握手
l发送了确认消息之后还要向对端发送close消息,要关闭对对端的连接,这是第3次握手
l而在最初发送断开连接的一端接收到消息之后,进入到一个很重要的状态time_wait状态,这个状态也是面试官经常问道的问题,最后一次握手是最初发送断开连接的一端接收到消息之后。对消息的确认。
同步:
如果有多个任务或者事件要发生,这些任务或者事件必须逐个地进行,一个事件或者任务的执行会导致整个流程的暂时等待,这些事件没有办法并发地执行;
异步:
如果有多个任务或者事件发生,这些事件可以并发地执行,一个事件或者任务的执行不会导致整个流程的暂时等待。
这就是同步和异步。
举个简单的例子,假如有一个任务包括两个子任务A和B,对于同步来说,当A在执行的过程中,B只有等待,直至A执行完毕,B才能执行;而对于异步就是A和B可以并发地执行,B不必等待A执行完毕之后再执行,这样就不会由于A的执行导致整个任务的暂时等待。
如果还不理解,可以先看下面这2段代码:
voidfun1(){
}
voidfun2(){
voidfunction(){
fun1();
fun2()
.....
这段代码就是典型的同步,在方法function中,fun1在执行的过程中会导致后续的fun2无法执行,fun2必须等待fun1执行完毕才可以执行。
接着看下面这段代码:
newThread(){
publicvoidrun(){
}.start();
fun2();
这段代码是一种典型的异步,fun1的执行不会影响到fun2的执行,并且fun1和fun2的执行不会导致其后续的执行过程处于暂时的等待。
事实上,同步和异步是一个非常广的概念,它们的重点在于多个任务和事件发生时,一个事件的发生或执行是否会导致整个流程的暂时等待。我觉得可以将同步和异步与Java中的synchronized关键字联系起来进行类比。当多个线程同时访问一个变量时,每个线程访问该变量就是一个事件,对于同步来说,就是这些线程必须逐个地来访问该变量,一个线程在访问该变量的过程中,其他线程必须等待;而对于异步来说,就是多个线程不必逐个地访问该变量,可以同时进行访问。
同步和异步可以表现在很多方面,但是记住其关键在于多个任务和事件发生时,一个事件的发生或执行是否会导致整个流程的暂时等待。一般来说,可以通过多线程的方式来实现异步,但是千万记住不要将多线程和异步画上等号,异步只是宏观上的一个模式,采用多线程来实现异步只是一种手段,并且通过多进程的方式也可以实现异步。同步和异步着重点在于多个任务的执行过程中,一个任务的执行是否会导致整个流程的暂时等待
阻塞:
当某个事件或者任务在执行过程中,它发出一个请求操作,但是由于该请求操作需要的条件不满足,那么就会一直在那等待,直至条件满足;
非阻塞:
当某个事件或者任务在执行过程中,它发出一个请求操作,如果该请求操作需要的条件不满足,会立即返回一个标志信息告知条件不满足,不会一直在那等待。
举个简单的例子:
假如我要读取一个文件中的内容,如果此时文件中没有内容可读,对于同步来说就是会一直在那等待,直至文件中有内容可读;而对于非阻塞来说,就会直接返回一个标志信息告知文件中暂时无内容可读。
阻塞和非阻塞着重点在于发出一个请求操作时,如果进行操作的条件不满足是否会返会一个标志信息告知条件不满足。理解阻塞和非阻塞可以同线程阻塞类比地理解,当一个线程进行一个请求操作时,如果条件不满足,则会被阻塞,即在那等待条件满足。
在了解阻塞IO和非阻塞IO之前,先看下一个具体的IO操作过程是怎么进行的。
通常来说,IO操作包括:对硬盘的读写、对socket的读写以及外设的读写。
当用户线程发起一个IO请求操作(本文以读请求操作为例),内核会去查看要读取的数据是否就绪,对于阻塞IO来说,如果数据没有就绪,则会一直在那等待,直到数据就绪;对于非阻塞IO来说,如果数据没有就绪,则会返回一个标志信息告知用户线程当前要读的数据没有就绪。当数据就绪之后,便将数据拷贝到用户线程,这样才完成了一个完整的IO读请求操作,也就是说一个完整的IO读请求操作包括两个阶段:
1)查看数据是否就绪;
2)进行数据拷贝(内核将数据拷贝到用户线程)。
那么阻塞(blockingIO)和非阻塞(non-blockingIO)的区别就在于第一个阶段,如果数据没有就绪,在查看数据是否就绪的过程中是一直等待,还是直接返回一个标志信息。
Java中传统的IO都是阻塞IO,比如通过socket来读数据,调用read()方法之后,如果数据没有就绪,当前线程就会一直阻塞在read方法调用那里,直到有数据才返回;
而如果是非阻塞IO的话,当数据没有就绪,read()方法应该返回一个标志信息,告知当前线程数据没有就绪,而不是一直在那里等待。
我们先来看一下同步IO和异步IO的定义,在《Unix网络编程》一书中对同步IO和异步IO的定义是这样的:
AsynchronousI/OoperationcausestherequestingprocesstobeblockeduntilthatI/Ooperationcompletes.
AnasynchronousI/Ooperationdoesnotcausetherequestingprocesstobeblocked.
从字面的意思可以看出:同步IO即如果一个线程请求进行IO操作,在IO操作完成之前,该线程会被阻塞;而异步IO为如果一个线程请求进行IO操作,IO操作不会导致请求线程被阻塞。
事实上,同步IO和异步IO模型是针对用户线程和内核的交互来说的:
对于同步IO:当用户发出IO请求操作之后,如果数据没有就绪,需要通过用户线程或者内核不断地去轮询数据是否就绪,当数据就绪时,再将数据从内核拷贝到用户线程;
而异步IO:只有IO请求操作的发出是由用户线程来进行的,IO操作的两个阶段都是由内核自动完成,然后发送通知告知用户线程IO操作已经完成。也就是说在异步IO中,不会对用户线程产生任何阻塞。
这是同步IO和异步IO关键区别所在,同步IO和异步IO的关键区别反映在数据拷贝阶段是由用户线程完成还是内核完成。所以说异步IO必须要有操作系统的底层支持。
注意同步IO和异步IO与阻塞IO和非阻塞IO是不同的两组概念。
阻塞IO和非阻塞IO是反映在当用户请求IO操作时,如果数据没有就绪,是用户线程一直等待数据就绪,还是会收到一个标志信息这一点上面的。也就是说,阻塞IO和非阻塞IO是反映在IO操作的第一个阶段,在查看数据是否就绪时是如何处理的。
在《Unix网络编程》一书中提到了五种IO模型
分别是:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。
下面就分别来介绍一下这5种IO模型的异同。
1.阻塞IO模型
最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。
当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。
典型的阻塞IO模型的例子为:
data=socket.read();
如果数据没有就绪,就会一直阻塞在read方法。
2.非阻塞IO模型
当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。
典型的非阻塞IO模型一般如下:
伪代码
while(true){
newMyThread(socket)
classMyThread{
if(data!=error){
处理数据
break;
但是对于非阻塞IO就有一个非常严重的问题,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率非常高,因此一般情况下很少使用while循环这种方式来读取数据。
3.多路复用IO模型
多路复用IO模型是目前使用得比较多的模型。JavaNIO实际上就是多路复用IO。
在多路复用IO模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。
在JavaNIO中,是通过selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在那里,因此这种方式会导致用户线程的阻塞。
也许有朋友会说,我可以采用多线程+阻塞IO达到类似的效果,但是由于在多线程+阻塞IO中,每个socket对应一个线程,这样会造成很大的资源占用,并且尤其是对于长连接来说,线程的资源一直不会释放,如果后面陆续有很多连接的话,就会造成性能上的瓶颈。
而多路复用IO模式,通过一个线程就可以管理多个socket,只有当socket真正有读写事件发生才会占用资源来进行实际的读写操作。因此,多路复用IO比较适合连接数比较多的情况。
另外多路复用IO为何比非阻塞IO模型的效率高是因为在非阻塞IO中,不断地询问socket状态时通过用户线程去进行的,而在多路复用IO中,轮询每个socket状态是内核在进行的,这个效率要比用户线程要高的多。
不过要注意的是,多路复用IO模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行响应。因此对于多路复用IO模型来说,一旦事件响应体很大,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询。
4.信号驱动IO模型
在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。
5.异步IO模型
异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。
而另一方面,从内核的角度,当它受到一个asynchronousread之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。
然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。
也就说用户线程完全不需要实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。
也就说在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成。
用户线程中不需要再次调用IO函数进行具体的读写。
这点是和信号驱动模型有所不同的
在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,然后需要用户线程调用IO函数进行实际的读写操作;而在异步IO模型中,收到信号表示IO操作已经完成,不需要再在用户线程中调用iO函数进行实际的读写操作。
注意,异步IO是需要操作系统的底层支持,在Java7中,提供了AsynchronousIO。也就是java中的AIO
前面四种IO模型实际上都属于同步IO,只有最后一种是真正的异步IO,因为无论是多路复用IO还是信号驱动模型,IO操作的第2个阶段都会引起用户线程阻塞,也就是内核进行数据拷贝的过程都会让用户线程阻塞。
在传统的网络服务设计模式中,有两种比较经典的模式:一种是多线程,一种是线程池。
对于多线程模式,也就说来了client,服务器就会新建一个线程来处理该client的读写事件,如下图所示:
这种模式虽然处理起来简单方便,但是由于服务器为每个client的连接都采用一个线程去处理,使得资源占用非常大。因此,当连接数量达到上限时,再有用户请求连接,直接会导致资源瓶颈,严重的可能会直接导致服务器崩溃。
因此,为了解决这种一个线程对应一个客户端模式带来的问题,提出了采用线程池的方式,也就说创建一个固定大小的线程池,来一个客户端,就从线程池取一个空闲线程来处理,当客户端处理完读写操作之后,就交出对线程的占用。因此这样就避免为每一个客户端都要创建线程带来的资源浪费,使得线程可以重用。
因此便出现了下面的两种高性能IO设计模式:Reactor和Proactor。
在Reactor模式中,会先对每个client注册感兴趣的事件,然后有一个线程专门去轮询每个client是否有事件发生,当有事件发生时,便顺序处理每个事件,当所有事件处理完之后,便再转去继续轮询,如下图所示:
多路复用IO就是采用Reactor模式。注意,上面的图中展示的是顺序处理每个事件,当然为了提高事件处理速度,可以通过多线程或者线程池的方式来处理事件。
在Proactor模式中,当检测到有事件发生时,会新起一个异步操作,然后交由内核线程去处理,当内核线程完成IO操作之后,发送一个通知告知操作已完成,可以得知,异步IO模型采用的就是Proactor模式。
Buffer(缓冲区):
JavaNIOBuffers用于和NIOChannel交互。我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels;
Buffer本质上就是一块内存区;
一个Buffer有三个属性是必须掌握的,分别是:capacity容量、position位置、limit限制。
Buffer的常见方法
Bufferclear()
Bufferflip()
Bufferrewind()
Bufferposition(intnewPosition)
Buffer的使用方式/方法介绍:
分配缓冲区(AllocatingaBuffer):
ByteBufferbuf=ByteBuffer.allocate(28);//以ByteBuffer为例子
写入数据到缓冲区(WritingDatatoaBuffer)
写数据到Buffer有两种方法:
1.从Channel中写数据到Buffer
intbytesRead=inChannel.read(buf);//readintobuffer.
2.通过put写数据:
buf.put(127);
一个buffer主要由position,limit,capacity三个变量来控制读写的过程。此三个变量的含义见如下表格:
参数
写模式
读模式
position
当前写入的单位数据数量。
当前读取的单位数据位置。
limit
代表最多能写多少单位数据和容量是一样的。
代表最多能读多少单位数据,和之前写入的单位数据量一致。
capacity
buffer容量
Buffer常见方法:
flip():写模式转换成读模式
rewind():将position重置为0,一般用于重复读。
clear():清空buffer,准备再次被写入(position变成0,limit变成capacity)。
compact():将未读取的数据拷贝到buffer的头部位。
mark()、reset():mark可以标记一个位置,reset可以重置到该位置。
Buffer常见类型:ByteBuffer、MappedByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。
channel常见类型:FileChannel、DatagramChannel(UDP)、SocketChannel(TCP)、ServerSocketChannel(TCP)
Channel:
JavaNIO中的SocketChannel是一个连接到TCP网络套接字的通道。
可以通过以下2种方式创建SocketChannel:
打开SocketChannel下面是SocketChannel的打开方式:
SocketChannelsocketChannel=SocketChannel.open();
关闭SocketChannel
当用完SocketChannel之后调用SocketChannel.close()关闭SocketChannel:socketChannel.close();
从SocketChannel读取数据
要从SocketChannel中读取数据,调用一个read()的方法之一。
ByteBufferbuf=ByteBuffer.allocate(48);
intbytesRead=socketChannel.read(buf);
非阻塞模式
可以设置SocketChannel为非阻塞模式(non-blockingmode).设置之后,就可以在异步模式下调用connect(),read()和write()了。
如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法。
像这样:
socketChannel.configureBlocking(false);
while(!socketChannel.finishConnect()){
//wait,ordosomethingelse...
JavaNIOChannel通道和流非常相似,主要有以下几点区别:
l通道可以读也可以写,流一般来说是单向的(只能读或者写,所以之前我们用流进行IO操作的时候需要分别创建一个输入流和一个输出流)。
l通道可以异步读写。
l通道总是基于缓冲区Buffer来读写。
JavaNIO中最重要的几个Channel的实现:
lFileChannel:用于文件的数据读写
lDatagramChannel:用于UDP的数据读写
lSocketChannel:用于TCP的数据读写,一般是客户端实现
lServerSocketChannel:允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel,一般是服务器实现
类层次结构
Selector(选择器):
Selector一般称为选择器,当然你也可以翻译为多路复用器。
它是JavaNIO核心组件中的一个,用于检查一个或多个NIOChannel(通道)的状态是否处于可读、可写。
如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。
使用Selector的好处在于:使用更少的线程来就可以来处理通道了,相比使用多个线程,避免了线程上下文切换带来的开销。
Selector(选择器)的使用方法介绍
Selector的创建
Selectorselector=Selector.open();
注册Channel到Selector(Channel必须是非阻塞的)
channel.configureBlocking(false);
SelectionKeykey=channel.register(selector,Selectionkey.OP_READ);
SelectionKey介绍
一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
从Selector中选择channel(SelectingChannelsviaaSelector)
选择器维护注册过的通道的集合,并且这种注册关系都被封装在SelectionKey当中.
停止选择的方法
wakeup()方法和close()方法。
文件I/O基石:Path:
创建一个Path
File和Path之间的转换,File和URI之间的转换
移除Path中的冗余项
Files类:
Files.exists()检测文件路径是否存在
Files.createFile()创建文件
Files.createDirectories()和Files.createDirectory()创建文件夹
Files.delete()方法可以删除一个文件或目录
Files.copy()方法可以吧一个文件从一个地址复制到另一个位置
获取文件属性
遍历一个文件夹
Files.walkFileTree()遍历整个目录
在linux没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序。在大数据、高并发、集群等一些名词唱得火热之年代,select和poll的用武之地越来越有限,风头已经被epoll占尽。
select的缺点:
在linux内核头文件中,有这样的定义:
#define__FD_SETSIZE1024
相比select模型,poll使用链表保存文件描述符,因此没有了监视文件数量的限制,但其他三个缺点依然存在。
拿select模型为例,假设我们的服务器需要支持100万的并发连接,则在__FD_SETSIZE为1024的情况下,则我们至少需要开辟1k个进程才能实现100万的并发连接。
因此,基于select模型的服务器程序,要达到10万级别的并发访问,是一个很难完成的任务。
epoll的实现机制与select/poll机制完全不同,上面所说的select的缺点在epoll上不复存在。
设想一下如下场景:
有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻,通常只有几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况)。如何实现这样的高并发?
在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般只能处理几千的并发连接。
epoll的设计和实现与select完全不同。
epoll通过在Linux内核中申请一个简易的文件系统
(文件系统一般用什么数据结构实现?B+树)
把原先的select/poll调用分成了3个部分:
1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3)调用epoll_wait收集发生的事件的连接
如此一来,要实现上面说是的场景,只需要在进程启动时建立一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除连接。同时,epoll_wait的效率也非常高,因为调用epoll_wait时,并没有一股脑的向操作系统复制这100万个连接的句柄数据,内核也不需要去遍历全部的连接。
下面来看看Linux内核具体的epoll机制实现思路。
structeventpoll{
....
/*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/
structrb_rootrbr;
/*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/
structlist_headrdlist;
};
而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。
在epoll中,对于每一个事件,都会建立一个epitem结构体,如下所示:
structepitem{
structrb_noderbn;//红黑树节点
structlist_headrdllink;//双向链表节点
structepoll_filefdffd;//事件句柄信息
structeventpoll*ep;//指向其所属的eventpoll对象
structepoll_eventevent;//期待发生的事件类型
当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。
通过红黑树和双链表数据结构,并结合操作系统底层回调机制,造就了epoll的高效
epoll的用法
第一步:epoll_create()系统调用。此调用返回一个句柄,之后所有的使用都依靠这个句柄来标识。
第二步:epoll_ctl()系统调用。通过此调用向epoll对象中添加、删除、修改感兴趣的事件,返回0标识成功,返回-1表示失败。
第三部:epoll_wait()系统调用。通过此调用收集收集在epoll监控中已经发生的事件。
15、网络编程中设计并发服务器,使用多进程与多线程,请问有什么区别?1,进程:
子进程是父进程的复制品。
子进程获得父进程数据空间、堆和栈的复制品。2,线程:
相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
线程执行开销小,但不利于资源管理和保护;而进程正相反。
同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
SMP的全称是"对称多处理"(SymmetricalMulti-Processing)技术,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。
对于TCP连接:
1.服务器端
1)创建套接字create;
2)绑定端口号bind;
3)监听连接listen;
4)接受连接请求accept,并返回新的套接字;
5)用新返回的套接字recv/send;
6)关闭套接字。
2.客户端
1)创建套接字create;
2)发起建立连接请求connect;
3)发送/接收数据send/recv;
4)关闭套接字。
TCP总结:
Server端:create–bind–listen–accept–recv/send–close
Client端:create——-conncet——send/recv——close.
对于UDP连接:
1.服务器端:
3)接收/发送消息recvfrom/sendto;
2.客户端:
2)发送/接收消息sendto/recvfrom;
3)关闭套接字.
UDP总结:
Server端:create—-bind—-recvfrom/sendto—-close
Client端:create—-sendto/recvfrom—-close.
函数原型intrecv(_In_SOCKETs,_Out_char*buf,_In_intlen,_In_intflags);
TransferControlProtocol。
UserDatagramProtocol。
TCP:
一种面向连接(连接导向)的、可靠的、基于字节流的传输层(Transportlayer)通信协议。
特点:
面向连接;
点到点的通信;
高可靠性;
占用系统资源多、效率低;
UDP:
一种无连接的、提供面向事务的简单不可靠信息传送服务的传输层通信协议。
特点:
非面向连接
传输不可靠,可能丢失
发送不管对方是否准备好,接收方收到也不确认
可以广播发送
非常简单的协议,开销小
TCP—传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
如果A与B两个进程通信,如果仅是两次连接。
可能出现的一种情况
B收到报文后,会向A发起连接。此时两次握手完毕
B会认为已经建立了连接可以通信,B会一直等到A发送的连接请求
而A对失效的报文回复自然不会处理。
因此会陷入B忙等的僵局,造成资源的浪费。
从上图可以看到:底层的东西已经被内核实现了,即我们通常意义上的内核协议栈(传输层,网络层,链路层)
最上面的Application(应用层)是我们用户所要实现的,它是属于用户进程的一部分,工作在用户空间,那么用户空间的程序要想访问内核,使用内核的服务,就需要一个接口,去访问所需要的服务
对于网络编程来说,这个接口就是套接口(Socket)。
Socket:可以看作用户进程和内核网络协议栈编程(交互)接口
Socket:不仅可以在同一台主机上进行通信,也可以在网络上不同的主机间进行通信,也可以异构(软硬件平台不同)进行通信(手机qq和PC机上的qq进行通信,手机的系统是ARM,而PC机是x86)
IP地址用来标志一台计算机,但是一台计算机上可能提供多种网络应用程序,使用端口来区分这些应用程序。
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。
端口范围0---65535,16位整数。
由于TCP/IP传输层的两个协议TCP和UDP是完全独立的两个软件模块,因此各自的端口号也相互独立,如TCP有一个255号端口,UDP也可以有一个255号端口,二者并不冲突。
URL:UniformResourceLocator,统一资源定位器;俗称“网址”,如:
由4部分组成:
l存放资源的主机域名:www.baidu.com;
l端口号:80;
l资源文件名:index.html#aacansu=bjsxt;
URL是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
基于TCP协议的Socket编程的主要步骤
服务器端(server):
1.构建一个ServerSocket实例,指定本地的端口。这个socket就是用来监听指定端口的连接请求的。
2.重复如下几个步骤:
a.调用socket的accept()方法来获得下面客户端的连接请求。通过accept()方法返回的socket实例,建立了一个和客户端的新连接。
b.通过这个返回的socket实例获取InputStream和OutputStream,可以通过这两个stream来分别读和写数据。
c.结束的时候调用socket实例的close()方法关闭socket连接。
客户端(client):
1.构建Socket实例,通过指定的远程服务器地址和端口来建立连接。
2.通过Socket实例包含的InputStream和OutputStream来进行数据的读写。
3.操作结束后调用socket实例的close方法,关闭。
//服务器监听请求;
ServerSocketss=newServerSocket(9999);
//接受请求:创建了socket;
Socketsocket=ss.accept();
详见课上示例。
//客户端向服务器端发送请求;
Socketsocket=newSocket("127.0.0.1",9999);
//建好连接后,开始传输数据;
OutputStreamos=socket.getOutputStream();
基于UDP协议的Socket编程的主要步骤
1.构造DatagramSocket实例,指定本地端口。
2.通过DatagramSocket实例的receive方法接收DatagramPacket.DatagramPacket中间就包含了通信的内容。
3.通过DatagramSocket的send和receive方法来收和发DatagramPacket.
1.构造DatagramSocket实例。
2.通过DatagramSocket实例的send和receive方法发送DatagramPacket报文。
3.结束后,调用DatagramSocket的close方法关闭。
(1)客户端向服务器端传送对象信息;
DatagramSocketds=newDatagramSocket(9999);
//构建数据包;
Scannerinput=newScanner(System.in);
System.out.println("请输入用户名:");
Stringusername=input.nextLine();
System.out.println("请输入密码:");
Stringpassword=input.nextLine();
//事先要创建好User类;
Useruser=newUser(username,password);
//字节流;在内存开辟缓冲区;
ByteArrayOutputStreambaos=newByteArrayOutputStream();
ObjectOutputStreamoos=newObjectOutputStream(baos);
oos.writeObject(user);
bytebuf[]=baos.toByteArray();
DatagramPacketdp=newDatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),8888);//DatagramPacket只用byte[]数组;
//发送数据;
ds.send(dp);
(2)服务器端从客户端接收对象信息;
//服务器定义DatagramSocket以接收数据;
DatagramSocketds=newDatagramSocket(8888);
//定义一个数据包来接收数据;
//数据包里是byte数组,所以还得定义一个byte数组;
bytebuf[]=newbyte[1024];
DatagramPacketdp=newDatagramPacket(buf,buf.length);
//接收客户端发过来的数据;
ds.receive(dp);
//用字节流和对象流读取对象信息;
ByteArrayInputStreambais=newByteArrayInputStream(buf);
ObjectInputStreamois=newObjectInputStream(bais);
Useruser=(User)ois.readObject();
System.out.println(user);
NIOvsBIO之间的理念上面的区别(NIO将阻塞交给了后台线程执行)
IO是面向流的,NIO是面向缓冲区的
JavaBIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;
NIO则能前后移动流中的数据,因为是面向缓冲区的BIO流是阻塞的,NIO流是不阻塞的JavaIO的各种流是阻塞的。
这意味着,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。
该线程在此期间不能再干任何事情了JavaNIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。
NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
选择器
JavaNIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
lChannels
lBuffers
lSelectors
基本上,所有的IO在NIO中都从一个Channel开始。
Channel有点象流。
数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中。
Channel的实现:
(涵盖了UDP和TCP网络IO,以及文件IO)
lFileChannel
lDatagramChannel
lSocketChannel
lServerSocketChannel
读数据:
intbytesRead=inChannel.read(buf);
写数据:
intbytesWritten=inChannel.write(buf);
Buffer
Buffer实现:(byte,char、short,int,long,float,double)
lByteBuffer
lCharBuffer
lDoubleBuffer
lFloatBuffer
lIntBuffer
lLongBuffer
lShortBuffer
Buffer使用
l读数据flip()方法:
buf.flip();
将Buffer从写模式切换到读模式调用flip()方法会将position设回0,并将limit设置成之前position的值。
l(char)buf.get()
读取数据
lBuffer.rewind()
将position设回0,所以你可以重读Buffer中的所有数据limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)
lBuffer.mark()方法,
可以标记Buffer中的一个特定position。
之后可以通过调用Buffer.reset()方法,恢复到Buffer.mark()标记时的position
一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。
lclear()方法:
清空整个缓冲区。position将被设回0,limit被设置成capacity的值
lcompact()方法:
只会清除已经读过的数据;任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
将position设到最后一个未读元素正后面,limit被设置成capacity的值写数据buf.put(127);
Buffer的三个属性
capacity:
l表示容量
lBuffer的一个固定的大小值;
lBuffer满了需要将其清空才能再写;
lByteBuffer.allocate(48);该buffer的capacity为48byteCharBuffer.allocate(1024);该buffer的capacity为1024个char
position:
含义取决于Buffer处在读模式还是写模式(初始值为0,写或者读操作的当前位置)
写数据时,初始的position值为0;
其值最大可为capacity-1
将Buffer从写模式切换到读模式,position会被重置为0
limit:
含义取决于Buffer处在读模式还是写模式(写limit=capacity;读limit等于最多可以读取到的数据)
写模式下,limit等于Buffer的capacity切换Buffer到读模式时,limit表示你最多能读到多少数据;
Selector
Selector允许单线程处理多个Channel。
如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。
例如,在一个聊天服务器中。
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。
一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
使用
创建:
Selectorselector=Selector.open();注册通道:channel.configureBlocking(false);//与Selector一起使用时,Channel必须处于非阻塞模式
//这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式(而套接字通道都可以)
SelectionKeykey=channel.register(selector,Selectionkey.OP_READ);//第二个参数表明Selector监听Channel时对什么事件感兴趣
//SelectionKey.OP_CONNECTSelectionKey.OP_ACCEPTSelectionKey.OP_READSelectionKey.OP_WRITE//可以用或操作符将多个兴趣组合一起
SelectionKey包含了interest集合、ready集合、Channel、Selector、附加的对象(可选)
intinterestSet=key.interestOps();
可以进行类似interestSet&SelectionKey.OP_CONNECT的判断
select():
阻塞到至少有一个通道在你注册的事件上就绪了
selectNow():
不会阻塞,不管什么通道就绪都立刻返回
selectedKeys():
访问“已选择键集(selectedkeyset)”中的就绪通道
close():
使用完selector需要用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效
SetselectedKeys=selector.selectedKeys();
IteratorkeyIterator=selectedKeys.iterator();
while(keyIterator.hasNext()){
SelectionKeykey=keyIterator.next();
if(key.isAcceptable()){
//aconnectionwasacceptedbyaServerSocketChannel.
}elseif(key.isConnectable()){
//aconnectionwasestablishedwitharemoteserver.
}elseif(key.isReadable()){
//achannelisreadyforreading
}elseif(key.isWritable()){
//achannelisreadyforwriting
keyIterator.remove();//注意这里必须手动remove;
表明该selectkey我已经处理过了;
HTTP协议是HyperTextTransferProtocol(超文本传输协议)的缩写,是用于从万维网(WWW:WorldWideWeb)服务器传输超文本到本地浏览器的传送协议。
HTTP是一个基于TCP/IP通信协议来传递数据(HTML文件,图片文件,查询结果等)。
HTTP工作原理
HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。
Web服务器有:Apache服务器,Nginx,IIS服务器(InternetInformationServices)等。
Web服务器根据接收到的请求后,向客户端发送响应信息。
HTTP默认端口号为80,但是你也可以改为8080或者其他端口。
HTTP三点注意事项:
lHTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
lHTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
客户端发送一个HTTP请求到服务器的请求消息包括以下格式:
l请求行(requestline)
l请求头部(header)
l空行
l请求数据
四个部分组成
下图给出了请求报文的一般格式。
客户端请求:
GET/hello.txtHTTP/1.1
User-Agent:curl/7.16.3libcurl/7.16.3OpenSSL/0.9.7lzlib/1.2.3
Host:www.example.com
Accept-Language:en,mi
HTTP响应也由四个部分组成,分别是:
l状态行
l消息报头
l响应正文
HTTP/1.1200OK
Date:Mon,27Jul200912:28:53GMT
Server:Apache
Last-Modified:Wed,22Jul200919:15:56GMT
ETag:"34aa387-d-1568eb00"
Accept-Ranges:bytes
Content-Length:51
Vary:Accept-Encoding
Content-Type:text/plain
根据HTTP标准,HTTP请求可以使用多种请求方法。
HTTP1.0定义了三种请求方法:GET,POST和HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS,PUT,DELETE,TRACE和CONNECT方法。
序号
方法
描述
1
GET
请求指定的页面信息,并返回实体主体。
2
HEAD
类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
3
POST
向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。
POST请求可能会导致新的资源的建立和/或已有资源的修改。
4
PUT
从客户端向服务器传送的数据取代指定的文档的内容。
5
DELETE
请求服务器删除指定的页面。
6
CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
7
OPTIONS
允许客户端查看服务器的性能。
8
TRACE
回显服务器收到的请求,主要用于测试或诊断。
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(serverheader)用以响应浏览器的请求。
HTTP状态码的英文为HTTPStatusCode。
下面是常见的HTTP状态码:
l200-请求成功
l301-资源(网页等)被永久转移到其它URL
l404-请求的资源(网页等)不存在
l500-内部服务器错误
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:
HTTP状态码分类
分类
分类描述
1**
信息,服务器收到请求,需要请求者继续执行操作
2**
成功,操作被成功接收并处理
3**
重定向,需要进一步的操作以完成请求
4**
客户端错误,请求包含语法错误或无法完成请求
5**
服务器错误,服务器在处理请求的过程中发生了错误
HTTP状态码列表:
HTTP状态码列表
状态码
状态码英文名称
中文描述
100
Continue
101
SwitchingProtocols
切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
200
OK
请求成功。一般用于GET与POST请求
201
Created
已创建。成功请求并创建了新的资源
202
Accepted
已接受。已经接受请求,但未处理完成
203
Non-AuthoritativeInformation
204
NoContent
无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205
ResetContent
重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206
PartialContent
部分内容。服务器成功处理了部分GET请求
300
MultipleChoices
多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301
MovedPermanently
永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302
Found
临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303
SeeOther
查看其它地址。与301类似。使用GET和POST请求查看
304
NotModified
未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305
UseProxy
使用代理。所请求的资源必须通过代理访问
306
Unused
已经被废弃的HTTP状态码
307
TemporaryRedirect
临时重定向。与302类似。使用GET请求重定向
400
BadRequest
客户端请求的语法错误,服务器无法理解
401
Unauthorized
请求要求用户的身份认证
402
PaymentRequired
保留,将来使用
403
Forbidden
服务器理解请求客户端的请求,但是拒绝执行此请求
404
NotFound
服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405
MethodNotAllowed
客户端请求中的方法被禁止
406
NotAcceptable
服务器无法根据客户端请求的内容特性完成请求
407
ProxyAuthenticationRequired
408
RequestTime-out
409
Conflict
服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突
410
Gone
客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411
LengthRequired
服务器无法处理客户端发送的不带Content-Length的请求信息
412
PreconditionFailed
客户端请求信息的先决条件错误
413
RequestEntityTooLarge
由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414
Request-URITooLarge
请求的URI过长(URI通常为网址),服务器无法处理
415
UnsupportedMediaType
服务器无法处理请求附带的媒体格式
416
Requestedrangenotsatisfiable
客户端请求的范围无效
417
ExpectationFailed
服务器无法满足Expect的请求头信息
500
InternalServerError
服务器内部错误,无法完成请求
501
NotImplemented
服务器不支持请求的功能,无法完成请求
502
BadGateway
充当网关或代理的服务器,从远端服务器接收到了一个无效的请求
503
ServiceUnavailable
由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504
GatewayTime-out
充当网关或代理的服务器,未及时从远端服务器获取请求
505
HTTPVersionnotsupported
服务器不支持请求的HTTP协议的版本,无法完成处理
Content-Type,内容类型,一般是指网页中存在的Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些Asp网页点击的结果却是下载到的一个文件或一张图片的原因。
文件扩展名
Content-Type(Mime-Type)
.*(二进制流,不知道下载文件类型)
application/octet-stream
.tif
image/tiff
.001
application/x-001
.301
application/x-301
.323
text/h323
.906
application/x-906
.907
drawing/907
.a11
application/x-a11
.acp
audio/x-mei-aac
.ai
application/postscript
.aif
audio/aiff
.aifc
.aiff
.anv
application/x-anv
.asa
text/asa
.asf
video/x-ms-asf
.asp
text/asp
.asx
.au
audio/basic
.avi
video/avi
.awf
application/vnd.adobe.workflow
.biz
text/xml
.bmp
application/x-bmp
.bot
application/x-bot
.c4t
application/x-c4t
.c90
application/x-c90
.cal
application/x-cals
.cat
application/vnd.ms-pki.seccat
.cdf
application/x-netcdf
.cdr
application/x-cdr
.cel
application/x-cel
.cer
application/x-x509-ca-cert
.cg4
application/x-g4
.cgm
application/x-cgm
.cit
application/x-cit
.class
java/*
.cml
.cmp
application/x-cmp
.cmx
application/x-cmx
.cot
application/x-cot
.crl
application/pkix-crl
.crt
.csi
application/x-csi
.css
text/css
.cut
application/x-cut
.dbf
application/x-dbf
.dbm
application/x-dbm
.dbx
application/x-dbx
.dcd
.dcx
application/x-dcx
.der
.dgn
application/x-dgn
.dib
application/x-dib
.dll
application/x-msdownload
.doc
application/msword
.dot
.drw
application/x-drw
.dtd
.dwf
Model/vnd.dwf
application/x-dwf
.dwg
application/x-dwg
.dxb
application/x-dxb
.dxf
application/x-dxf
.edn
application/vnd.adobe.edn
.emf
application/x-emf
.eml
message/rfc822
.ent
.epi
application/x-epi
.eps
application/x-ps
.etd
application/x-ebx
.exe
.fax
image/fax
.fdf
application/vnd.fdf
.fif
application/fractals
.fo
.frm
application/x-frm
.g4
.gbr
application/x-gbr
.
application/x-
.gif
image/gif
.gl2
application/x-gl2
.gp4
application/x-gp4
.hgl
application/x-hgl
.hmr
application/x-hmr
.hpg
application/x-hpgl
.hpl
application/x-hpl
.hqx
application/mac-binhex40
.hrf
application/x-hrf
.hta
application/hta
.htc
text/x-component
.htm
text/html
.html
.htt
text/webviewhtml
.htx
.icb
application/x-icb
.ico
image/x-icon
application/x-ico
.iff
application/x-iff
.ig4
.igs
application/x-igs
.iii
application/x-iphone
.img
application/x-img
.ins
application/x-internet-signup
.isp
.IVF
video/x-ivf
.java
.jfif
image/jpeg
.jpe
application/x-jpe
.jpeg
.jpg
application/x-jpg
.js
application/x-javascript
.jsp
.la1
audio/x-liquid-file
.lar
application/x-laplayer-reg
.latex
application/x-latex
.lavs
audio/x-liquid-secure
.lbm
application/x-lbm
.lmsff
audio/x-la-lms
.ls
.ltr
application/x-ltr
.m1v
video/x-mpeg
.m2v
.m3u
audio/mpegurl
.m4e
video/mpeg4
.mac
application/x-mac
.man
application/x-troff-man
.math
.mdb
application/msaccess
application/x-mdb
.mfp
application/x-shockwave-flash
.mht
.mhtml
.mi
application/x-mi
.mid
audio/mid
.midi
.mil
application/x-mil
.mml
.mnd
audio/x-musicnet-download
.mns
audio/x-musicnet-stream
.mocha
.movie
video/x-sgi-movie
.mp1
audio/mp1
.mp2
audio/mp2
.mp2v
video/mpeg
.mp3
audio/mp3
.mp4
.mpa
video/x-mpg
.mpd
application/vnd.ms-project
.mpe
.mpeg
video/mpg
.mpg
.mpga
audio/rn-mpeg
.mpp
.mps
.mpt
.mpv
.mpv2
.mpw
.mpx
.mtx
.mxp
application/x-mmxp
.net
image/pnetvue
.nrf
application/x-nrf
.nws
.odc
text/x-ms-odc
.out
application/x-out
.p10
application/pkcs10
.p12
application/x-pkcs12
.p7b
application/x-pkcs7-certificates
.p7c
application/pkcs7-mime
.p7m
.p7r
application/x-pkcs7-certreqresp
.p7s
application/pkcs7-signature
.pc5
application/x-pc5
.pci
application/x-pci
.pcl
application/x-pcl
.pcx
application/x-pcx
application/pdf
.pdx
application/vnd.adobe.pdx
.pfx
.pgl
application/x-pgl
.pic
application/x-pic
.pko
application/vnd.ms-pki.pko
.pl
application/x-perl
.plg
.pls
audio/scpls
.plt
application/x-plt
.png
image/png
application/x-png
.pot
application/vnd.ms-powerpoint
.ppa
.ppm
application/x-ppm
.pps
.ppt
application/x-ppt
.pr
application/x-pr
.prf
application/pics-rules
.prn
application/x-prn
.prt
application/x-prt
.ps
.ptn
application/x-ptn
.pwz
.r3t
text/vnd.rn-realtext3d
.ra
audio/vnd.rn-realaudio
.ram
audio/x-pn-realaudio
.ras
application/x-ras
.rat
application/rat-file
.rdf
.rec
application/vnd.rn-recording
.red
application/x-red
.rgb
application/x-rgb
.rjs
application/vnd.rn-realsystem-rjs
.rjt
application/vnd.rn-realsystem-rjt
.rlc
application/x-rlc
.rle
application/x-rle
.rm
application/vnd.rn-realmedia
.rmf
application/vnd.adobe.rmf
.rmi
.rmj
application/vnd.rn-realsystem-rmj
.rmm
.rmp
application/vnd.rn-rn_music_package
.rms
application/vnd.rn-realmedia-secure
.rmvb
application/vnd.rn-realmedia-vbr
.rmx
application/vnd.rn-realsystem-rmx
.rnx
application/vnd.rn-realplayer
.rp
image/vnd.rn-realpix
.rpm
audio/x-pn-realaudio-plugin
.rsml
application/vnd.rn-rsml
.rt
text/vnd.rn-realtext
.rtf
application/x-rtf
.rv
video/vnd.rn-realvideo
.sam
application/x-sam
.sat
application/x-sat
.sdp
application/sdp
.sdw
application/x-sdw
.sit
application/x-stuffit
.slb
application/x-slb
.sld
application/x-sld
.slk
drawing/x-slk
.smi
application/smil
.smil
.smk
application/x-smk
.snd
.sol
text/plain
.sor
.spc
.spl
application/futuresplash
.spp
.ssm
application/streamingmedia
.sst
application/vnd.ms-pki.certstore
.stl
application/vnd.ms-pki.stl
.stm
.sty
application/x-sty
.svg
.swf
.tdf
application/x-tdf
.tg4
application/x-tg4
.tga
application/x-tga
application/x-tif
.tiff
.tld
.top
drawing/x-top
.torrent
application/x-bittorrent
.tsd
.txt
.uin
application/x-icq
.uls
text/iuls
.vcf
text/x-vcard
.vda
application/x-vda
.vdx
application/vnd.visio
.vml
.vpg
application/x-vpeg005
.vsd
application/x-vsd
.vss
.vst
application/x-vst
.vsw
.vsx
.vtx
.vxml
.wav
audio/wav
.wax
audio/x-ms-wax
.wb1
application/x-wb1
.wb2
application/x-wb2
.wb3
application/x-wb3
.wbmp
image/vnd.wap.wbmp
.wiz
.wk3
application/x-wk3
.wk4
application/x-wk4
.wkq
application/x-wkq
.wks
application/x-wks
.wm
video/x-ms-wm
.wma
audio/x-ms-wma
.wmd
application/x-ms-wmd
.wmf
application/x-wmf
.wml
text/vnd.wap.wml
.wmv
video/x-ms-wmv
.wmx
video/x-ms-wmx
.wmz
application/x-ms-wmz
.wp6
application/x-wp6
.wpd
application/x-wpd
.wpg
application/x-wpg
.wpl
application/vnd.ms-wpl
.wq1
application/x-wq1
.wr1
application/x-wr1
.wri
application/x-wri
.wrk
application/x-wrk
.ws
application/x-ws
.ws2
.wsc
text/scriptlet
.wsdl
.wvx
video/x-ms-wvx
.xdp
application/vnd.adobe.xdp
.xdr
.xfd
application/vnd.adobe.xfd
.xfdf
application/vnd.adobe.xfdf
.xhtml
.xls
application/vnd.ms-excel
application/x-xls
.xlw
application/x-xlw
.xml
.xpl
.xq
.xql
.xquery
.xsd
.xsl
.xslt
.xwd
application/x-xwd
.x_b
application/x-x_b
.sis
application/vnd.symbian.install
.sisx
.x_t
application/x-x_t
.ipa
application/vnd.iphone
.apk
application/vnd.android.package-archive
.xap
application/x-silverlight-app
协议本质是双方约定好的一种传输规则,为了让传输数据的双方节点能建立连接,按照约定去传输和解析数据