上一篇写了clowwindy很早的一个版本的实现,那个版本比较简陋,和现在最新的版本差别还是不小。目前的版本使用了更高效的IO复用,定义了状态机,代码也复杂一点。
“TCPRelay”
这个在绝对是shadowsocks最核心的模块。那么这个模块是做什么的呢?模块的中文是TCP流量重传
。
在sslocal
的作用是把浏览器传来的流量加密传给ssserver
代理,并且把ssserver
传来的流量解密后传给浏览器。
在ssserver
的作用是把sslocal
传来的流量解密然后传给remote,比如google.com,把remote传来的数据加密传给sslocal
。
即tcprelay负责把两个sock的数据通过加密解密后传输。
tcprelay handler
以sslocal
举例,监听了1080端口后,就会起一个TCP Relay
实例。浏览器访问google.com时,就会触发tcprelay生成一个TCP Relay Handler
实例。源码的注释如下:
|
|
在tcprelay类中,handle_event
为loop触发的,如果是accept事件,则建立连接,然后创建TCPRelayHandler
,即handler是维护了accept的那个连接的通信。在hanlder内部,accept的连接一定是local sock的连接,所以在handler中连接了local sock和remote sock。
|
|
其中self._fd_to_handlers
作用是TCPRelay
类能够维护多个TCPRelayHandler
类的实例,在sock
不为_server_socket
即不是accept事件的时候,可以从_fd_to_handlers
中取到对应的handler实例去处理。为什么在TCPRelay
类处理socket事件呢?因为它是eventloop的handler。这种层层handle的关系也是网络编程中需要花时间去掌握的技巧。
状态机
在TCPRelayHandler中,local socket和remote sock的状态会发送改变,每个状态的下一步怎么做,这些通过状态机来维护。
|
|
ss的状态机是到了一个阶段就把self._stage
转变为下一个阶段。比如在解析好了addr后就立刻变成了DNS状态;DNS解析完成后立刻变成CONNECTING状态。
和状态机搭配使用的是数据流的上行和下行。
|
|
stream分为STREAM_DOWN
和STREAM_UP
,每个流又分为WAIT_STATUS_READING,WAIT_STATUS_READING,WAIT_STATUS_READWRITING
三种状态。
在_update_stream
函数中,把某个stream的状态更改后,根据更新到的status来修改epoll需要关注的事件。比如在当_downstream_status
为WAIT_STATUS_WRITING
即remote_sock
到local_sock
写的数据流在写的时候,_local_sock
的epoll就监听可写。
|
|
连接到remote
使用非阻塞socket进行连接,使用connect
函数,然后加入epoll中,等到可写事件触发后,便确定是连接上了。
连接的地方为:
|
|
确定连接上的地方:
|
|
“EventLoop网络编程”
这是ss的网络通讯核心模块。它封装了epoll
,kqueue
,select
三者,并且为后两者实现了类似epoll的接口。比如使用select.select封装的epoll接口。
封装epoll接口
|
|
其中MODE为POLL_IN,POLL_OUT
。
|
|
因为都是只有一个位为1的16进制数,所以可以做&
操作,一定要求完全一致才可以&
为1。
eventloop编程模式
采用业界比较通用的loop编程模式。即在一个线程内由epoll作为处理事件和分发事件的核心。如下server的代码,把tcp servers加入到epoll中,最后loop.run()
启动服务。
|
|
真正做事情的类需要实现handle_event
方法,然后把自己加入到epoll中就可以了。比如asyncdns
类,实现的handle_event
就是解析dns的结果,并且触发tcprelayhandler
设置的回调函数。
在ss中,tcprelay和asyncdns都实现了add_to_loop
方法,
muduo
使用的也是loop
的编程模式。但是muduo
使用的是多线程跑IO,而在ss中使用的是单线程,但是场景不同吧,muduo追求的是高性能网络库,可是ss对高并发连接没有那么多要求。
看后续有没有时间和心力为ss增加loop+多线程
提高性能。
总结
最后,对读者感到抱歉,本人写文章不太流畅,只能贴一堆代码,会慢慢锻炼的 :(。