Tomcat源码学习一 Tomcat实现基石Socket

本文介绍了Tomcat作为Servlet容器的基础实现方式,重点讲解了如何使用Socket进行客户端和服务端之间的通信,包括创建ServerSocket和Socket的过程,以及Tomcat内部如何处理Socket连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tomcat源码学习一 Tomcat实现基石Socket

简述

Tomcat是什么?相信做Java Web的都应该用过Tomcat,它是Web轻量级的应用服务器,或者说是一个Servlet容器。 
通过浏览器(Http协议)请求我们的Servlet类配置的路径这个过程是怎样的?Tomcat通过传输层的Socket实现了一个ServerSocket类,等待浏览器的请求并将请求信息封装成Request实例,发送给相应的Servlet,并且将返回结果封装成Response返回给浏览器。
HttpServletRequest,HttpServletResponse 两个接口。相信大家都不会陌生,是Http协议的请求和响应。在servlet-api.jar包里这两个接口的实例是怎么来得。这就得在Tomcat源码中寻找答案了。

1.Socket简单实现

Socket又称套接字,是Tcp/Ip传输层的一种应用。下面模拟一个打电话场景.
/**
 * 电话服务端
 * step 1 创建serverSocket
 * step 2 绑定地址和端口
 * step 3 accept 等待客户端连接
 *
 */
public class CallServer {
    public static ServerSocket serverSocket;
    public static Socket socket;
    public static BufferedReader reader =null;
    public static PrintWriter writer = null;

    public CallServer(String host,int port) {
        try {
            //  创建serverSocket
            serverSocket = new ServerSocket();
            // 地址和端口
            SocketAddress endpoint = new InetSocketAddress(InetAddress.getByName(host),port);
            // 最大连接数
            int backlog = 5;
            //  绑定地址和端口
            serverSocket.bind(endpoint,5);
            // 阻塞式     等待客户端连接
            socket = serverSocket.accept();
            System.out.println("Server---------李四 已经连接 接收李四消息!");
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String msg=null;
            while((msg = reader.readLine())!=null){
                System.out.println(msg);
            }
            writer = new PrintWriter(socket.getOutputStream());
            writer.write("hello lisi");
            writer.flush();
            writer.close();
            reader.close();
            socket.close();
            serverSocket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        CallServer zhangsan = new CallServer("127.0.0.1",8080);
    }
}
/**
 * 电话客户端
 */
public class CallClient {
    private static Socket socket;
    public static BufferedReader reader =null;
    public static PrintWriter writer = null;

    public CallClient(String host,int port) {
        try {
            socket = new Socket();
            // 地址和端口
            SocketAddress endpoint = new InetSocketAddress(InetAddress.getByName(host),port);
            socket.connect(endpoint);
            System.out.println("Client---------已经接通张三 接收张三消息!");
            writer = new PrintWriter(socket.getOutputStream());
            writer.write("hello zhangsan");
            writer.flush();
            //关闭输出流单项关闭 输出流 不加输出流不关闭,server不会接受到消息
            socket.shutdownOutput();
            //关闭 输出流会直接关闭socket
            //writer.close();
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String msg=null;
            while((msg = reader.readLine())!=null){
                System.out.println(msg);
            }

            writer.close();
            reader.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        CallClient lisi = new CallClient("127.0.0.1",8080);

    }
}
注意:
1.在客户端或者服务端通过socket.shutdownOutput()都是单向关闭的,即关闭客户端的输出流并不会关闭服务端的输出流,所以是一种单方向的关闭流
2.通过socket.shutdownOutput()关闭输出流,但socket仍然是连接状态,连接并未关闭
3.如果直接关闭输入或者输出流,即:in.close()或者out.close(),会直接关闭socket

2.Tomcat Socket源码阅读

2.1 AbstractEndpoint.init()
抽象终端类 用来处理底层 socket 链接。它有三个实现类,分别是3中不同的连接方法。NioEndpoint,AprEndpoint,JIoEndpoint 首相看AbstractEndpoint初始化方法。
    // Endpoint 初始化
    public final void init() throws Exception {
        testServerCipherSuitesOrderSupport();
        if (bindOnInit) {
            //绑定端口 其实现serverSocket的bind
            bind();
            bindState = BindState.BOUND_ON_INIT;
        }
    }
2.2 JIoEndpoint.bind()
public void bind() throws Exception {
        //初始化接收器线程数
        if (acceptorThreadCount == 0) {
            acceptorThreadCount = 1;
        }
        // 初始化最大连接数
        if (getMaxConnections() == 0) {
            // User hasn't set a value - use the default
            setMaxConnections(getMaxThreadsInternal());
        }

        if (serverSocketFactory == null) {
            if (isSSLEnabled()) {
            //创建ssl serverSocketFactory
                serverSocketFactory =
                    handler.getSslImplementation().getServerSocketFactory(this);
            } else {
                serverSocketFactory = new DefaultServerSocketFactory(this);
            }
        }

        if (serverSocket == null) {
            try {
                if (getAddress() == null) {
                //创建 serverSocket
                    serverSocket = serverSocketFactory.createSocket(getPort(),
                            getBacklog());
                } else {
                    serverSocket = serverSocketFactory.createSocket(getPort(),
                            getBacklog(), getAddress());
                }
            } catch (BindException orig) {
                String msg;
                if (getAddress() == null)
                    msg = orig.getMessage() + " <null>:" + getPort();
                else
                    msg = orig.getMessage() + " " +
                            getAddress().toString() + ":" + getPort();
                BindException be = new BindException(msg);
                be.initCause(orig);
                throw be;
            }
        }

    }
2.3 Acceptor 接收器类
protected class Acceptor extends AbstractEndpoint.Acceptor {
        @Override
        public void run() {
            int errorDelay = 0;
            // 循环,直到接收到关闭命令为止
            while (running) {
                // 如果端点暂停,则循环
                while (paused && running) {
                    state = AcceptorState.PAUSED;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {

                    }
                }
                if (!running) {
                    break;
                }
                state = AcceptorState.RUNNING;
                try {
                    ////当前连接数
                    countUpOrAwaitConnection();
                    Socket socket = null;
                    try {
                        // 此处用来接收 请求 监听客户端连接
                        // /取出队列中的连接请求
                        socket = serverSocketFactory.acceptSocket(serverSocket);
                    } catch (IOException ioe) {
                        countDownConnection();
                        // 必要时引入延迟
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        throw ioe;
                    }
                    // 成功接受,重置错误延迟
                    errorDelay = 0;

                    // Configure the socket
                    if (running && !paused && setSocketOptions(socket)) {
                        // TODO 监听到连接后(即浏览器向服务器发起一次请求)
                        ////处理请求
                        // Hand this socket off to an appropriate processor
                        if (!processSocket(socket)) {
                            countDownConnection();
                            // 关闭 socket 链接
                            closeSocket(socket);
                        }
                    } else {
                        countDownConnection();
                        closeSocket(socket);
                    }
                } catch (IOException x) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), x);
                    }
                } catch (NullPointerException npe) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), npe);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
            state = AcceptorState.ENDED;
        }
    }

总结

Tomcat实现的基石是Socket,在这里只是简单的分析。也只是冰山一角。其中Socket编程还有很多知识,像Tcp/Ip协议,长连接,短连接等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值