IO多路复用
1.Selector
当执行IO读写操作时,通过先将数据放入缓冲区,然后由一个统一selelctor来监控缓冲区,等缓冲区就绪后会通知我们的程序
public static void main(String[] args) throws Exception{ ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); ServerSocket serverSocket = serverSocketChannel.socket(); serverSocketChannel.configureBlocking(false); serverSocket.bind(new InetSocketAddress("127.0.0.1", 8088)); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while(true) { selector.select(); Iterator iter = selector.selectedKeys().iterator(); while(iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); if(key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept(); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); System.out.println("Connected: " + channel.socket().getRemoteSocketAddress()); } if(key.isReadable()) { ByteBuffer byteBuffer = ByteBuffer.allocate(512); SocketChannel socketChannel = (SocketChannel) key.channel(); socketChannel.read(byteBuffer); byteBuffer.flip(); System.out.println("server received message: " + getString(byteBuffer)); byteBuffer.clear(); String message = "server sending message " + System.currentTimeMillis(); System.out.println("server sending message: " + message); byteBuffer.put(message.getBytes()); byteBuffer.flip(); socketChannel.write(byteBuffer); } iter.remove(); } } }
2.epoll
epoll是linux下io一种处理方式,用户首先创建一个epoll的fd(文件描述符),然后将要关注的io的fd注册进我们刚创建的epoll的fd中,然后执行epoll_wati
int main(void) { int epfd,nfds; struct epoll_event ev,events[2]; //ev用于注册事件,数组用于返回要处理的事件 epfd = epoll_create(1); //只需要监听一个描述符——标准输入 ev.data.fd = STDIN_FILENO; ev.events = EPOLLIN|EPOLLET; //监听读状态同时设置ET模式 epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev); //注册epoll事件 for(;;) { nfds = epoll_wait(epfd, events, 2, -1); for(int i = 0; i < nfds; i++) { if(events[i].data.fd==STDIN_FILENO) printf("ok ok ok!\n"); } } }
3.kqueue
用法上跟epoll类似
struct kevent changes[FD_NUM]; struct kevent events[FD_NUM]; EV_SET(&changes[0], STDIN_FILENO, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, &STDIN_FILENO); EV_SET(&changes[1], STDIN_FILENO, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, &STDIN_FILENO); int nev = kevent(kq, changes, FD_NUM, events, FD_NUM, NULL); // 已经就绪的文件描述符数量 for(int i=0; i<nev; i++){ struct kevent event = events[i]; int ready_fd = *((int *)event.udata); if(ready_fd == STDIN_FILENO){ } }