为了方便区分,以下分别使用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. Objective-C中Block的常见用法

    typedef int(^AddValue)(int,int); int main(int argc, const char * argv[]) { @autoreleasepool { //1:NS ...

  2. C#内存管理和垃圾回收机制

    数据类型 垃圾回收机制 一.数据类型 C#中的数据类型分为值类型 (Value type) 和引用类型(reference type), 值  类 型: 所有的值类型都集成自 System.Value ...

  3. 1. @ModelAttribute注解

    添加@ModelAttribute修饰的方法,在每个目标方法调用前都会执行该方法. 一般情况下,在form表单修改的时,某项字段规定为不可更改,就需要使用该注解标注的方法,根据id的获取与否,来从数据 ...

  4. Android Data Binding Library

    Data Binding Library Data Binding Library是一个支持库,允许您使用声明格式(而不是编程)将布局中的UI组件与应用程序中的数据源绑定. 布局通常在调用UI框架方法 ...

  5. liunx poi excel下载内容乱码本地tomcat正常

    结论:在jsp中加上out.clear即可(前提保证生成的excel在服务器上是正确的,只是浏览器传输才出现乱码). dowload.jsp完整代码 <%@ page language=&quo ...

  6. 【ASP.NET 进阶】判断访问网站的客户端是PC还是手机

    主要就是通过客户端传递的User-agent来判断访问网站的客户端是PC还是手机,.NET中就是Request.ServerVariables["HTTP_USER_AGENT"] ...

  7. PHP 获取文件扩展名的五种方式

    第一种 substr(strrchr("http://www.xxx.com/public/abc.jpg", '.'), 1); string strrchr('string', ...

  8. mac 管理员权限变成了普通权限处理方法

    在更换账户名称的时候出了这个问题.设置的时候不会显示用户名,没有电脑的管理权限了,找到如下方法解决的,试了可行. http://blog.csdn.net/vickylizy/article/deta ...

  9. 爬虫--Scrapy-参数等级和请求传参

    日志等级 日志等级(种类): ERROR:错误 WARNING:警告 INFO:一般信息 DEBUG:调试信息(默认) 指定输入某一中日志信息: settings:LOG_LEVEL = ‘ERROR ...

  10. java ftp主动模式与被动模式

    首先介绍一下主动模式与被动模式: 1.PORT(主动模式) ftpClient.enterLocalActiveMode(); PORT中文为主动模式,工作的原理:FTP客户端连接到FTP服务器的21 ...