BIO时期
1.客户端发起请求到服务器时,会先到达内核经历三次握手获得连接。
2.内核会给连接分配一个文件描述符:fd1,fd2
3.开启对应线程1/进程1来进行文件描述符fd1内容的读取。(read 命令)
socket在早期是阻塞(blocking)的。(当线程1读取fd1时如果数据还没有到,那这个read命令就不能返回,而是会阻塞着一直等到数据到达,读到了数据才能返回)
所以早期的tomcat7.0 之前的版本也是阻塞的。使用线程读网卡上的连接时,如果连接有数据就拿着数据返回,没有就只能将线程阻塞着后面的方法不能执行。
如果来了新的连接就只能开新的线程进行处理,在JVM中开辟线程的成本(线程栈的默认大小是1MB),且每个CPU核心在单一时间片上只能有一个线程在进行处理。
假设当前正在执行的线程A的数据没到,阻塞了。但是线程B的数据到了,却因为当前的CPU时间片被线程A占有导致CPU浪费。而且线程多了,CPU在线程之间来回切换也会造成浪费。
NIO时期
BIO时期因为硬件性能被浪费,所以内核升级了
在socket命令增加了SOCK_NONBLOCK 类型
使用man命令查看内核可以调用的函数和工具,man是linux中最常见的帮助命令
首先:yum -y install man man-pages
man 2(2类是查看内核相关的)socket
调用socket()方法时会返回一个文件描述符
就能看到socket命令的相关介绍
SOCKET(2) Linux Programmer’s Manual SOCKET(2)
NAME
socket - create an endpoint for communication
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
DESCRIPTION
socket() creates an endpoint for communication and returns a descriptor.
The domain argument specifies a communication domain; this selects the protocol family which will be used for communication. These families are defined in <sys/socket.h>.
The currently understood formats include:
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device netlink(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK Appletalk ddp(7)
AF_PACKET Low level packet interface packet(7)
The socket has the indicated type, which specifies the communication semantics. Currently defined types are:
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an
entire packet with each input system call.
SOCK_RAW Provides raw network protocol access.
SOCK_RDM Provides a reliable datagram layer that does not guarantee ordering.
SOCK_PACKET Obsolete and should not be used in new programs; see packet(7).
Some socket types may not be implemented by all protocol families; for example, SOCK_SEQPACKET is not implemented for AF_INET.
Since Linux 2.6.27, the type argument serves a second purpose: in addition to specifying a socket type, it may include the bitwise OR of any of the following values, to mod-
ify the behavior of socket():
SOCK_NONBLOCK Set the O_NONBLOCK file status flag on the new open file description. Using this flag saves extra calls to fcntl(2) to achieve the same result.
SOCK_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful.
The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol
family, in which case protocol can be specified as 0. However, it is possible that many protocols may exist, in which case a particular protocol must be specified in this
manner. The protocol number to use is specific to the “communication domain” in which communication is to take place; see protocols(5). See getprotoent(3) on how to map
protocol name strings to protocol numbers.
Sockets of type SOCK_STREAM are full-duplex byte streams, similar to pipes. They do not preserve record boundaries. A stream socket must be in a connected state before any
data may be sent or received on it. A connection to another socket is created with a connect(2) call. Once connected, data may be transferred using read(2) and write(2)
calls or some variant of the send(2) and recv(2) calls. When a session has been completed a close(2) may be performed. Out-of-band data may also be transmitted as
described in send(2) and received as described in recv(2).
The communications protocols which implement a SOCK_STREAM ensure that data is not lost or duplicated. If a piece of data for which the peer protocol has buffer space can-
not be successfully transmitted within a reasonable length of time, then the connection is considered to be dead. When SO_KEEPALIVE is enabled on the socket the protocol
checks in a protocol-specific manner if the other end is still alive. A SIGPIPE signal is raised if a process sends or receives on a broken stream; this causes naive pro-
cesses, which do not handle the signal, to exit. SOCK_SEQPACKET sockets employ the same system calls as SOCK_STREAM sockets. The only difference is that read(2) calls will
return only the amount of data requested, and any data remaining in the arriving packet will be discarded. Also all message boundaries in incoming datagrams are preserved.
可以看到 socker 方法中要求传入一个 int type (指定socket类型)
其中有一种叫做 SOCK_NONBLOCK (非阻塞)类型。所以文件描述符就有了nonblock
当有了非阻塞之后就不需要给每个连接开一个线程了,可以用一个线程来轮询处理很多文件描述符的内容。先调用read fd1,如果fd1有数据就处理。处理完调用fd9。如果没有就直接返回,开始调用fd9,不会进行阻塞等待数据到来…
这样就避免了阻塞等待浪费CPU也避免了线程切换带来的浪费。
这个轮询发生在用户态,因为遍历文件描述符和结果处理都是用户空间自己完成所以也称作 同步非阻塞 NIO