为了方便区分,以下分别使用local,server,remote代表ss客户端,ss服务端,以及ss客户端请求访问的远程主机。

在shadowsocks中,无论对于local还是server,都需要建立两个socket:local_sock和remote_sock
对于local,local_sock用于和进程通讯,server_sock用于和server通讯。
对于server,local_sock用于和local通信,server_sock用于和remote通信。

上一次提到两者的事件处理均使用TCPRelayHandler,先来看一下handle_event函数。

# tcprelay.py

class TCPRelayHandler(object):
    def handle_event(self, sock, event):
        # handle all events in this handler and dispatch them to methods
        if self._stage == STAGE_DESTROYED:
            logging.debug('ignore handle_event: destroyed')
            return
        # order is important
        # remote_socket
        if sock == self._remote_sock:
            if event & eventloop.POLL_ERR:
                self._on_remote_error()
                if self._stage == STAGE_DESTROYED:
                    return
            if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
                self._on_remote_read()
                if self._stage == STAGE_DESTROYED:
                    return
            if event & eventloop.POLL_OUT:
                self._on_remote_write()
        # local_socket
        elif sock == self._local_sock:
            if event & eventloop.POLL_ERR:
                self._on_local_error()
                if self._stage == STAGE_DESTROYED:
                    return
            if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
                self._on_local_read()
                if self._stage == STAGE_DESTROYED:
                    return
            if event & eventloop.POLL_OUT:
                self._on_local_write()
        else:
            logging.warn('unknown socket')

该函数根据传入的socket参数来区别是local_sock,还是remote_sock,将read/write事件的处理分发到各个处理函数。

下面来具体看看这些事件处理函数。

remote_read

# tcprelay.py
class TCPRelayHandler(object):
    def _on_remote_read(self):
        # handle all remote read events
        data = None
        try:
            # 从remote_sock读取数据
            data = self._remote_sock.recv(BUF_SIZE)

        except (OSError, IOError) as e:
            if eventloop.errno_from_exception(e) in \
                    (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK):
                return
        if not data:
            self.destroy()
            return
        self._update_activity(len(data))
        # local,说明server回反了数据
        if self._is_local:
            # 解密数据
            data = self._encryptor.decrypt(data)
        # server,说明remote响应到达
        else:
            # 加密数据
            data = self._encryptor.encrypt(data)
        try:
            # 写到local_sock,对于server就是写回local,对于local就是写回进程
            self._write_to_sock(data, self._local_sock)
        except Exception as e:
            shell.print_exception(e)
            if self._config['verbose']:
                traceback.print_exc()
            # TODO use logging when debug completed
            self.destroy()

remote_write

# tcprelay.py

class TCPRelayHandler(object):
    def _on_remote_write(self):
        # 进入流传输阶段
        self._stage = STAGE_STREAM
        # 如果有需要写的数据
        if self._data_to_write_to_remote:
            data = b''.join(self._data_to_write_to_remote)
            self._data_to_write_to_remote = []
            self._write_to_sock(data, self._remote_sock)
        else:
            # 否则,更新状态
            self._update_stream(STREAM_UP, WAIT_STATUS_READING)

local_read

# tcprelay.py

class TCPRelayHandler(object):
    def _on_local_read(self):
        # handle all local read events and dispatch them to methods for
        # each stage
        if not self._local_sock:
            return

        is_local = self._is_local
        data = None
        try:
            # 尝试读取数据
            data = self._local_sock.recv(BUF_SIZE)
        except (OSError, IOError) as e:
            if eventloop.errno_from_exception(e) in \
                    (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK):
                return
        if not data:
            self.destroy()
            return
        #
        self._update_activity(len(data))
        if not is_local:
            # server的local_sock,表明为local发来数据
            # 将数据解密
            data = self._encryptor.decrypt(data)
            if not data:
                return
        # 在stream状态,此时已经建立起协议,传输的是向remote的请求细节,以及remote的响应
        if self._stage == STAGE_STREAM:
            # 客户端
            if self._is_local:
                # 将数据加密
                data = self._encryptor.encrypt(data)
            # 直接转发给remote
            self._write_to_sock(data, self._remote_sock)
            return

        # 处于init状态,处理协议细节
        elif is_local and self._stage == STAGE_INIT:
            # TODO check auth method
            # 给进程发送确认信息
            self._write_to_sock(b'\x05\00', self._local_sock)
            self._stage = STAGE_ADDR
            return

        # 处于connecting状态,处理协议细节
        elif self._stage == STAGE_CONNECTING:
            self._handle_stage_connecting(data)
        # local,等待进程发来请求数据,或者server,初始化状态,等待local发来数据
        elif (is_local and self._stage == STAGE_ADDR) or \
                (not is_local and self._stage == STAGE_INIT):
            # 客户端:接受进程的请求信息,服务器:接受客户端的请求信息
            self._handle_stage_addr(data)

local_write

# tcprelay.py

class TCPRelayHandler(object):
    def _on_local_write(self):
        # 如果有数据可写
        if self._data_to_write_to_local:
            data = b''.join(self._data_to_write_to_local)
            self._data_to_write_to_local = []
            # 写到本地sock
            self._write_to_sock(data, self._local_sock)
        else:
            # 更新流状态
            self._update_stream(STREAM_DOWN, WAIT_STATUS_READING)

ss源码学习--事件处理的更多相关文章

  1. ss源码学习--从协议建立到完成一次代理请求

    上一次介绍了ss源码中各个事件处理函数完成的工作,这次具体分析一下协议的建立以及请求数据的传输过程. 因为ss的local和server共用一个类以及一系列的事件处理函数,所以看起来稍显复杂.下面来将 ...

  2. ss源码学习--工作流程

    ss的local端和server端的工作流程相似,因此复用了TCPRelay类和TCPRelayHandler类. 两端均是使用TCPRelay类监听连接,并使用TCPRelayHandler类处理请 ...

  3. Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习

    package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.V ...

  4. Redis源码学习:字符串

    Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串, ...

  5. .NET Core 2.1 源码学习:看 SocketsHttpHandler 如何在异步方法中连接 Socket

    在 .NET Core 2.1 中,System.Net.Sockets 的性能有了很大的提升,最好的证明是 Kestrel 与 HttpClient 都改为使用 System.Net.Sockets ...

  6. Netty 源码学习——EventLoop

    Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...

  7. Spring5.0源码学习系列之Spring AOP简述

    前言介绍 附录:Spring源码学习专栏 在前面章节的学习中,我们对Spring框架的IOC实现源码有了一定的了解,接着本文继续学习Springframework一个核心的技术点AOP技术. 在学习S ...

  8. Java并发包源码学习系列:阻塞队列实现之PriorityBlockingQueue源码解析

    目录 PriorityBlockingQueue概述 类图结构及重要字段 什么是二叉堆 堆的基本操作 向上调整void up(int u) 向下调整void down(int u) 构造器 扩容方法t ...

  9. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

随机推荐

  1. mysql错误:Column count doesn't match value count at row 1

    mysql错误:Column count doesn't match value count at row 1 mysql错误:Column count doesn't match value cou ...

  2. windows 配置java环境变量

    https://www.cnblogs.com/cnwutianhao/p/5487758.html

  3. 同时安装python2和python3环境

    一.同时安装两个环境 https://www.cnblogs.com/zhengyihan1216/p/6011640.html 二.快速安装django: https://blog.csdn.net ...

  4. 使用Flex4画图形:圆、矩形、线

    <?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="ht ...

  5. Mapper的.xml文件的delete的参数问题

    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-// ...

  6. Linux定时任务 结合PHP实现实时监控

    首先说说cron,它是一个linux下的定时执行工具.根用户以外的用户可以使用 crontab 工具来配置 cron 任务. 所有用户定义的 crontab 都被保存在/var/spool/cron ...

  7. 机器学习进阶-直方图与傅里叶变换-傅里叶变换(高低通滤波) 1.cv2.dft(进行傅里叶变化) 2.np.fft.fftshift(将低频移动到图像的中心) 3.cv2.magnitude(计算矩阵的加和平方根) 4.np.fft.ifftshift(将低频和高频移动到原来位置) 5.cv2.idft(傅里叶逆变换)

    1. cv2.dft(img, cv2.DFT_COMPLEX_OUTPUT) 进行傅里叶变化 参数说明: img表示输入的图片, cv2.DFT_COMPLEX_OUTPUT表示进行傅里叶变化的方法 ...

  8. Spring boot 启动配置原理

    配置在META-INF/spring.factories 有几个主要的类 ApplicationContextInitializer    创建SpringAplication SpringAppli ...

  9. Android虚拟机与Java虚拟机 两种虚拟机的比较

    在Android的体系框架中有一部分叫做Android Runtime,即Android运行时环境,这个环境包括了两个部分,一个是Android的核心类库,还有一个就是Dalvik虚拟机了. Andr ...

  10. ORACLE问题定位基本方法

    在使用ORACLE过程中经常会碰到启动或者访问失败的问题.碰到这些问题该如何解决? 1.仔细阅读报错提示信息,不要扫一眼感觉似曾相识,凭经验就开始上手解决.因为相同的现象可能是不同的原因引发的. 2. ...