[
  {
    "path": "README.md",
    "content": "# marlon-tools\nAutomatically exported from code.google.com/p/marlon-tools\n\n**dnsproxy**\n\nA simple DNS proxy server, support wilcard hosts, IPv6, cache.\n\nInstructions:\n```\n1. Edit /etc/hosts, add:\n127.0.0.1 *.local\n-2404:6800:8005::62 *.blogspot.com\n\n2. startup dnsproxy (here using Google DNS server as delegating server):\n$ sudo python dnsproxy.py -s 8.8.8.8\n\n3. Then set system dns server as 127.0.0.1, you can verify it by dig:\n$ dig test.local\nThe result should contain 127.0.0.1.\n\n```\n\nUsage:\n```\ndnsproxy.py [options]\n\nOptions:\n  -h, --help            show this help message and exit\n  -f <file>, --hosts-file=<file>\n                        specify hosts file, default /etc/hosts\n  -H HOST, --host=HOST  specify the address to listen on\n  -p PORT, --port=PORT  specify the port to listen on\n  -s SERVER, --server=SERVER\n                        specify the delegating dns server\n  -C, --no-cache        disable dns cache\n```\n"
  },
  {
    "path": "tools/adjustsrt/adjustsrt.py",
    "content": "#!/usr/bin/env python\n# coding: utf-8\nfrom optparse import OptionParser\n\nimport re\nimport sys\nimport time\n\ndef main():\n    parser = OptionParser(usage='%prog -d delay [src_srt] [dest_srt]', description=u'调整srt字幕文件的延时')\n    parser.add_option('-d', '--delay', dest='delay', metavar='DELAY', help=u'延时时间(秒),负数表示提前')\n    opts, args = parser.parse_args(sys.argv[1:])\n\n    if not opts.delay or len(args) > 2:\n        parser.print_help()\n        sys.exit(1)\n    fin, fout = sys.stdin, sys.stdout\n    try:\n        if len(args) >= 1:\n            fin = open(args[0], 'r')\n        if len(args) == 2:\n            fout = open(args[1], 'w')\n        delay(fin, fout, float(opts.delay))\n    except IOError as e:\n        print >> sys.stderr, u'找不到文件: %s' % e.filename\n        sys.exit(2)\n    finally:\n        fin.close()\n        fout.close()\n\nclass Time(object):\n    M_SEC = 1000\n    M_MIN = 60 * 1000\n    M_HOUR = 3600 * 1000\n    def __init__(self, hours=0, mins=0, secs=0, millis=0):\n        self.millis = hours*self.M_HOUR + mins*self.M_MIN + secs*self.M_SEC + millis\n\n    def __add__(self, secs):\n        millis = int(secs * self.M_SEC)\n        return Time(millis=self.millis + millis)\n\n    def __sub__(self, secs):\n        return self.__add__(-secs)\n\n    def __str__(self):\n        secs, millis = self.millis/1000, self.millis%1000\n        mins, secs = secs/60, secs%60\n        hours, mins = mins/60, mins%60\n        return '%02d:%02d:%02d,%03d' % (hours, mins, secs, millis)\n\nRE_TIME = re.compile(r'^(\\d{1,2}):(\\d{1,2}):(\\d{1,2}),(\\d{1,3})')\ndef parse_time(time_str):\n    m = RE_TIME.match(time_str)\n    if m:\n        return Time(int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)))\n    return None\n\nRE_TIME_LINE = re.compile(r'^(\\d{1,2}:\\d{1,2}:\\d{1,2},\\d{1,3}) --> (\\d{1,2}:\\d{1,2}:\\d{1,2},\\d{1,3})(\\s*)$')\n\ndef delay(fin, fout, delay):\n    for line in fin.readlines():\n        m = RE_TIME_LINE.match(line)\n        if m:\n            s_time = parse_time(m.group(1)) + delay\n            e_time = parse_time(m.group(2)) + delay\n            fout.write('%s --> %s%s' % (s_time, e_time, m.group(3)))\n        else:\n            fout.write(line)\n\nif __name__ == '__main__':\n    main()\n\n"
  },
  {
    "path": "tools/dnsproxy/dnsparser.py",
    "content": "# coding: utf-8\nfrom cStringIO import StringIO\nimport struct\n\nDNS_FLAG_QR = 0x8000\nDNS_FLAG_RD = 0x0100\nDNS_FLAG_RA = 0x0080\n\nDNS_TYPE_A = 1\nDNS_TYPE_AAAA = 28\nDNS_CLASS_IN = 1\n\n'''\n一个简单的DNS解析器。\n\n解析：\nmsg = DNSMessage.parse(message data)\n\n序列化：\ndata = msg.serialize()\n\n\nauthor: marlonyao<yaolei135@gmail.com>\n'''\nclass DNSMessageHeader(object):\n    def __init__(self, id, flag, qd_count, an_count, ns_count, ar_count):\n        self.id = id\n        self.flag = flag\n        self.qd_count = qd_count\n        self.an_count = an_count\n        self.ns_count = ns_count\n        self.ar_count = ar_count\n\n    @staticmethod\n    def parse(message):\n        id, flag = struct.unpack('!HH', message.read(4))\n        qd_count, an_count, ns_count, ar_count = struct.unpack('!HHHH', message.read(8))\n        return DNSMessageHeader(id, flag, qd_count, an_count, ns_count, ar_count)\n\n    def serialize(self, message, memoize=None):\n        message.write(struct.pack('!HHHHHH', self.id, self.flag,\n                       self.qd_count, self.an_count, self.ns_count, self.ar_count))\n\n    def __str__(self):\n        return 'id: %s, flag: %s, qd_count: %s, an_count: %s, ns_count: %s, ar_count: %s' % (\n            self.id, self.flag, self.qd_count, self.an_count, self.ns_count, self.ar_count\n        )\n\nclass DNSMessageQuestion(object):\n    def __init__(self, qname, qtype, qclass):\n        self.qname = qname\n        self.qtype = qtype\n        self.qclass = qclass\n\n    @staticmethod\n    def parse(message):\n        qname = parse_domain_name(message)\n        qtype, qclass = struct.unpack('!HH', message.read(4))\n        return DNSMessageQuestion(qname, qtype, qclass)\n\n    def serialize(self, message, memoize):\n        unparse_domain_name(self.qname, message, memoize)\n        message.write(struct.pack('!HH', self.qtype, self.qclass))\n\n    def __str__(self):\n        return 'qname: %s, qtype: %s, qclass: %s' % (self.qname, self.qtype, self.qclass)\n\n    def __repr__(self):\n        return str(self)\n\nclass DNSMessageRecord(object):\n    def __init__(self, name, type_, class_, ttl, rdata):\n        self.name = name\n        self.type_ = type_\n        self.class_ = class_\n        self.ttl = ttl\n        self.rdata = rdata\n\n    @staticmethod\n    def parse(message):\n        name = parse_domain_name(message)\n        type_, class_, ttl, rd_len = struct.unpack('!HHIH', message.read(10))\n        return DNSMessageRecord(name, type_, class_, ttl, message.read(rd_len))\n\n    def serialize(self, message, memoize):\n        unparse_domain_name(self.name, message, memoize)\n        message.write(struct.pack('!HHIH%ss'%len(self.rdata),\n                    self.type_, self.class_, self.ttl,\n                    len(self.rdata), self.rdata,\n                ))\n\n    def __str__(self):\n        return \"name: %s, type: %s, class: %s, ttl: %s, rdata: %s\" % (\n            self.name, self.type_, self.class_, self.ttl, self.rdata\n        )\n\n    def __repr__(self):\n        return str(self)\n\nclass DNSMessage(object):\n    def __init__(self, header, questions=None, answers=None, authorities=None, additionals=None):\n        self.header = header\n        self.questions = questions or []\n        self.answers = answers or []\n        self.authorities = authorities or []\n        self.additionals = additionals or []\n\n    @staticmethod\n    def parse(data):\n        message = StringIO(data)\n        header = DNSMessageHeader.parse(message)\n        questions = []\n        for i in range(0, header.qd_count):\n            quest = DNSMessageQuestion.parse(message)\n            questions.append(quest)\n        answers = []\n        for i in range(0, header.an_count):\n            answer = DNSMessageRecord.parse(message)\n            answers.append(answer)\n        authorities = []\n        for i in range(0, header.ns_count):\n            authority = DNSMessageRecord.parse(message)\n            authorities.append(authority)\n        additionals = []\n        for i in range(0, header.ar_count):\n            additional = DNSMessageRecord.parse(message)\n            additionals.append(additional)\n        return DNSMessage(header, questions, answers, authorities, additionals)\n\n    def serialize(self):\n        'serialize to network bytes, not considering name compression'\n        message = StringIO()\n        memoize = {}\n        self.header.serialize(message, memoize)\n        for s in self.questions:\n            s.serialize(message, memoize)\n        for s in self.answers:\n            s.serialize(message, memoize)\n        for s in self.authorities:\n            s.serialize(message, memoize)\n        for s in self.additionals:\n            s.serialize(message, memoize)\n        return message.getvalue()\n\n    def __str__(self):\n        return 'header: %s\\n' % self.header +\\\n               'questions: %s\\n' % self.questions +\\\n               'answers: %s\\n' % self.answers +\\\n               'authorities: %s\\n' % self.authorities +\\\n               'additionals: %s\\n' % self.additionals\n\n    def __repr__(self):\n        return str(self)\n\ndef _parse_domain_labels(message):\n    labels = []\n    len = ord(message.read(1))\n    while len > 0:\n        if len >= 64:   # domain name compression\n            len = len & 0x3f\n            offset = (len << 8) + ord(message.read(1))\n            mesg = StringIO(message.getvalue())\n            mesg.seek(offset)\n            labels.extend(_parse_domain_labels(mesg))\n            return labels\n        else:\n            labels.append(message.read(len))\n            len = ord(message.read(1))\n    return labels\ndef parse_domain_name(message):\n    return '.'.join(_parse_domain_labels(message))\n\ndef unparse_domain_name(name, message, memoize):\n    labels = name.split('.')\n    for i, label in enumerate(labels):\n        qname = '.'.join(labels[i:])\n        if qname in memoize:\n            offset = (memoize[qname] & 0x3fff) + 0xc000\n            message.write(struct.pack('!H', offset))\n            break\n        else:\n            memoize[qname] = message.tell()\n            #print 'add to memoize: %s, %s' % (qname, message.tell())\n            message.write(struct.pack('!B%ss' % len(label), len(label), label))\n    else:\n        # write last ending zero\n        message.write('\\x00')\n\n"
  },
  {
    "path": "tools/dnsproxy/dnsproxy.py",
    "content": "#!/usr/bin/env python\n# coding: utf-8\nfrom SocketServer import BaseRequestHandler, ThreadingUDPServer\nfrom cStringIO import StringIO\nimport os\nimport socket\nimport struct\nimport time\n\n'''\nA simple DNS proxy server, support wilcard hosts, IPv6, cache. Usage:\n\nEdit /etc/hosts, add:\n    127.0.0.1 *.local\n    2404:6800:8005::62 *.blogspot.com\nstartup dnsproxy(here use Google DNS server as delegating server):\n$ sudo python dnsproxy.py -s 8.8.8.8\n\nThen set system dns server as 127.0.0.1, you can verify it by dig:\n$ dig test.local\n\nThe result should contains 127.0.0.1.\n\nauthor: marlonyao<yaolei135@gmail.com>\n'''\ndef main():\n    import optparse, sys\n    parser = optparse.OptionParser()\n    parser.add_option('-f', '--hosts-file', dest='hosts_file', metavar='<file>', default='/etc/hosts', help='specify hosts file, default /etc/hosts')\n    parser.add_option('-H', '--host', dest='host', default='127.0.0.1', help='specify the address to listen on')\n    parser.add_option('-p', '--port', dest='port', default=53, type='int', help='specify the port to listen on')\n    parser.add_option('-s', '--server', dest='dns_server', metavar='SERVER', help='specify the delegating dns server')\n    parser.add_option('-C', '--no-cache', dest='disable_cache', default=False, action='store_true', help='disable dns cache')\n\n    opts, args = parser.parse_args()\n    if not opts.dns_server:\n        parser.print_help()\n        sys.exit(1)\n    dnsserver = DNSProxyServer(opts.dns_server, disable_cache=opts.disable_cache, host=opts.host, port=opts.port, hosts_file=opts.hosts_file)\n    dnsserver.serve_forever()\n\nclass Struct(object):\n    def __init__(self, **kwargs):\n        for name, value in kwargs.items():\n            setattr(self, name, value)\n\ndef parse_dns_message(data):\n    message = StringIO(data)\n    message.seek(4)     # skip id, flag\n    c_qd, c_an, c_ns, c_ar = struct.unpack('!4H', message.read(8))\n    # parse question\n    question = parse_dns_question(message)\n    for i in range(1, c_qd):    # skip other question\n        parse_dns_question(message)\n    records = []\n    for i in range(c_an+c_ns+c_ar):\n        records.append(parse_dns_record(message))\n    return Struct(question=question, records=records)\n\ndef parse_dns_question(message):\n    qname = parse_domain_name(message)\n    qtype, qclass = struct.unpack('!HH', message.read(4))\n    end_offset = message.tell()\n    return Struct(name=qname, type_=qtype, class_=qclass, end_offset=end_offset)\n\ndef parse_dns_record(message):\n    parse_domain_name(message)      # skip name\n    message.seek(4, os.SEEK_CUR)    # skip type, class\n    ttl_offset = message.tell()\n    ttl = struct.unpack('!I', message.read(4))[0]\n    rd_len = struct.unpack('!H', message.read(2))[0]\n    message.seek(rd_len, os.SEEK_CUR)     # skip rd_content\n    return Struct(ttl_offset=ttl_offset, ttl=ttl)\n\ndef _parse_domain_labels(message):\n    labels = []\n    len = ord(message.read(1))\n    while len > 0:\n        if len >= 64:   # domain name compression\n            len = len & 0x3f\n            offset = (len << 8) + ord(message.read(1))\n            mesg = StringIO(message.getvalue())\n            mesg.seek(offset)\n            labels.extend(_parse_domain_labels(mesg))\n            return labels\n        else:\n            labels.append(message.read(len))\n            len = ord(message.read(1))\n    return labels\n\ndef parse_domain_name(message):\n    return '.'.join(_parse_domain_labels(message))\n\ndef addr_p2n(addr):\n    try:\n        return socket.inet_pton(socket.AF_INET, addr)\n    except:\n        return socket.inet_pton(socket.AF_INET6, addr)\n\nDNS_TYPE_A = 1\nDNS_TYPE_AAAA = 28\nDNS_CLASS_IN = 1\n\nclass DNSProxyHandler(BaseRequestHandler):\n    def handle(self):\n        reqdata, sock = self.request\n        req = parse_dns_message(reqdata)\n        q = req.question\n        if q.type_ in (DNS_TYPE_A, DNS_TYPE_AAAA) and (q.class_ == DNS_CLASS_IN):\n            for packed_ip, host in self.server.host_lines:\n                if q.name.endswith(host):\n                    # header, qd=1, an=1, ns=0, ar=0\n                    rspdata = reqdata[:2] + '\\x81\\x80\\x00\\x01\\x00\\x01\\x00\\x00\\x00\\x00'\n                    rspdata += reqdata[12:q.end_offset]\n                    # answer\n                    rspdata += '\\xc0\\x0c'   # pointer to domain name\n                    # type, 1 for ip4, 28 for ip6\n                    if len(packed_ip) == 4:\n                        rspdata += '\\x00\\x01'   # 1 for ip4\n                    else:\n                        rspdata += '\\x00\\x1c'   # 28 for ip6\n                    # class: 1, ttl: 2000(0x000007d0)\n                    rspdata += '\\x00\\x01\\x00\\x00\\x07\\xd0'\n                    rspdata += '\\x00' + chr(len(packed_ip)) # rd_len\n                    rspdata += packed_ip\n                    sock.sendto(rspdata, self.client_address)\n                    return\n\n        # lookup cache\n        if not self.server.disable_cache:\n            cache = self.server.cache\n            cache_key = (q.name, q.type_, q.class_)\n            cache_entry = cache.get(cache_key)\n            if cache_entry:\n                rspdata = update_ttl(reqdata, cache_entry)\n                if rspdata:\n                    sock.sendto(rspdata, self.client_address)\n                    return\n        rspdata = self._get_response(reqdata)\n        if not self.server.disable_cache:\n            cache[cache_key] = Struct(rspdata=rspdata, cache_time=int(time.time()))\n        sock.sendto(rspdata, self.client_address)\n\n    def _get_response(self, data):\n        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for the remote DNS server\n        sock.connect((self.server.dns_server, 53))\n        sock.sendall(data)\n        sock.settimeout(60)\n        rspdata = sock.recv(65535)\n        sock.close()\n        return rspdata\n\ndef update_ttl(reqdata, cache_entry):\n    rspdata, cache_time = cache_entry.rspdata, cache_entry.cache_time\n    rspbytes = bytearray(rspdata)\n    rspbytes[:2] = reqdata[:2]          # update id\n    current_time = int(time.time())\n    time_interval = current_time - cache_time\n    rsp = parse_dns_message(rspdata)\n    for record in rsp.records:\n        if record.ttl <= time_interval:\n            return None\n        rspbytes[record.ttl_offset:record.ttl_offset+4] = struct.pack('!I', record.ttl-time_interval)\n    return str(rspbytes)\n\ndef load_hosts(hosts_file):\n    'load hosts config, only extract config line contains wildcard domain name'\n    def wildcard_line(line):\n        parts = line.strip().split()[:2]\n        if len(parts) < 2: return False\n        if not parts[1].startswith('*'): return False\n        try:\n            packed_ip = addr_p2n(parts[0])\n            return packed_ip, parts[1][1:]\n        except:\n            return None\n    with open(hosts_file) as hosts_in:\n        hostlines = []\n        for line in hosts_in:\n            hostline = wildcard_line(line)\n            if hostline:\n                hostlines.append(hostline)\n        return hostlines\n\nclass DNSProxyServer(ThreadingUDPServer):\n    def __init__(self, dns_server, disable_cache=False, host='127.0.0.1', port=53, hosts_file='/etc/hosts'):\n        self.dns_server = dns_server\n        self.hosts_file = hosts_file\n        self.host_lines = load_hosts(hosts_file)\n        self.disable_cache = disable_cache\n        self.cache = {}\n        ThreadingUDPServer.__init__(self, (host, port), DNSProxyHandler)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/pingonline/pingonline.py",
    "content": "#!/usr/bin/env python\n# coding: utf-8\nimport cookielib\nimport optparse\nimport re\nimport sys\nimport urllib\nimport urllib2\n\nRE_TOKEN = re.compile(r'<input type=\"hidden\" name=\"token\" value=\"(\\w+)\"')\nRE_IPOUT = re.compile(r'<pre>(.*?)<br />', re.DOTALL)\nRE_IPADDR=re.compile(r'\\(([\\w.:]*)\\)')\n\nPING_URL = 'http://www.subnetonline.com/pages/network-tools/online-ping-ipv4.php'\nPING6_URL = 'http://www.subnetonline.com/pages/ipv6-network-tools/online-ipv6-ping.php'\n\ndef ping(host, v6=False, count=4, ttl=255, size=32, only_ip=False):\n    url = PING_URL\n    if v6:\n        url = PING6_URL\n    cj = cookielib.LWPCookieJar()\n    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))\n    urllib2.install_opener(opener)\n    resp = urllib2.urlopen(url)\n    content = resp.read()\n    #print content\n    # get token\n    m = RE_TOKEN.search(content)\n    if m:\n        token = m.group(1)\n    else:\n        print >> sys.stderr, 'error: cannot find token'\n        return None\n    # post data\n    data = {\n        'host': host,\n        'token': token,\n        'count': str(count),\n        'ttl': str(ttl),\n        'size': str(size),\n    }\n    resp = urllib2.urlopen(url, data=urllib.urlencode(data))\n    content = resp.read()\n    #print content\n    m = RE_IPOUT.search(content)\n    if m:\n        content = m.group(1)[:-1]          # remove the last '\\n'\n    else:\n        print >> sys.stderr, 'error: cannot find output'\n        return None\n    if not only_ip:\n        return content\n    else:\n        m = RE_IPADDR.search(content)\n        if m:\n            ip = m.group(1)\n            return ip\n        else:\n            print >> sys.stderr, 'error: cannot find ip address'\n            return None\n\n\ndef main():\n    parser = optparse.OptionParser(usage=u'%prog [-hp6] [-c count] [-t ttl] [-s packetsize] destination')\n    parser.add_option('-c', dest='count', type='int', help='Stop after sending count ECHO_REQUEST packets.', default=4)\n    parser.add_option('-t', dest='ttl', type='int', help='Set the IP Time to Live.', default=255)\n    parser.add_option('-s', dest='packetsize', type='int', help='Specifies the number of data bytes to be sent.', default=32)\n    parser.add_option('-6', dest='ipv6', action='store_true', help='Specifies whether use IPv6', default=False)\n    parser.add_option('-p', dest='only_ip', action='store_true', default=False, help='only output ipv6 address, can be used lookup ipv6 address according by host.')\n\n    opts, args = parser.parse_args()\n    if len(args) != 1:\n        parser.print_help()\n        sys.exit(1)\n    res = ping(args[0], count=opts.count, ttl=opts.ttl, size=opts.packetsize, only_ip=opts.only_ip, v6=opts.ipv6)\n    if res:\n        print res\n    elif res is None:\n        sys.exit(2)\n    else:\n        print >> sys.stderr, '%s not reachable' % args[0]\n        sys.exit(3)\n\nif __name__ == '__main__':\n    main()\n\n"
  },
  {
    "path": "tools/tcpmon/TCPMonitor.java",
    "content": "import java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.InetSocketAddress;\nimport java.net.ServerSocket;\nimport java.net.Socket;\nimport java.net.SocketException;\nimport java.net.UnknownHostException;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\n\n/**\n * <p>TCP监视器，它在本地监听端口，将请求转发到远端服务器，并将请求返回。</p>\n * \n * <p><code>java TCPMonitor -a :8080 localhost:8000</code><br> 监听本地的8080端口，将请求转发给本机的8000端口，并将所有请求响应输出到标准错误输出。</p>\n * \n * <p><code>java TCPMonitor :8080 work:22</code><br> 监听本地的8080端口，并将请求转发给work主机的22端口。</p>\n * \n * @author marlonyao<yaolei135@gmail.com>\n */\nclass TCPMonitor {\n\tpublic static void main (String[] args) throws IOException {\n\t\tOptions options = null;\n\t\ttry {\n\t\t\toptions = parseArgs(args);\n\t\t\tif (options.help) {\n\t\t\t\tusage();\n\t\t\t\tSystem.exit(0);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tusage();\n\t\t\tSystem.exit(-1);\n\t\t}\n\n\t\tServerSocket serverSocket = new ServerSocket();\n\t\tserverSocket.setReuseAddress(true);\n\t\tserverSocket.bind(new InetSocketAddress(options.host, options.port));\n\t\tSystem.out.println(\"server started on \" + serverSocket.getLocalSocketAddress());\n\t\tExecutor executor = Executors.newFixedThreadPool(options.threadCount);\n\t\twhile(true) {\n\t\t\tSocket sock = serverSocket.accept();\n\t\t\tSystem.out.println(\"accept connection: \" + sock.getRemoteSocketAddress());\n\t\t\texecutor.execute(new MonitorThread(sock, options.remoteHost, options.remotePort, options.dumpRequest, options.dumpResponse));\n\t\t}\n\t}\n\n\tstatic class Options {\n\t\tboolean help;\n\t\tboolean dumpRequest;\n\t\tboolean dumpResponse;\n\t\tint threadCount = 10;\n\n\t\tString host = \"localhost\";\n\t\tint port;\n\t\tString remoteHost;\n\t\tint remotePort;\n\t}\n\n\tprivate static void usage() {\n\t\tSystem.err.print(\n\t\t\t\t\"java TCPMonitor [options] [host]:port remote_host:remote_port\\n\" +\n\t\t\t\t\"        -h, --help             print this help\\n\" +\n\t\t\t\t\"        -r, --dump-request     dump request to stderr\\n\" +\n\t\t\t\t\"        -s, --dump-response    dump response to stderr\\n\" +\n\t\t\t\t\"        -a, --dump-all         dump request and response to stderr\\n\" +\n\t\t\t\t\"        -n, --threads=N        thread count, default is 10\\n\");\n\t}\n\n\tprivate static Options parseArgs(String[] args) {\n\t\tOptions options = new Options();\n\t\tint i;\n\t\t// parse options\n\t\tfor (i = 0; i < args.length; i++) {\n\t\t\tif (!args[i].startsWith(\"-\")) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (args[i].equals(\"-h\") || args[i].equals(\"--help\")) {\n\t\t\t\toptions.help = true;\n\t\t\t} else if (args[i].equals(\"-r\") || args[i].equals(\"--dump-request\")) {\n\t\t\t\toptions.dumpRequest = true;\n\t\t\t} else if (args[i].equals(\"-s\") || args[i].equals(\"--dump-response\")) {\n\t\t\t\toptions.dumpResponse = true;\n\t\t\t} else if (args[i].equals(\"-a\") || args[i].equals(\"--dump-all\")) {\n\t\t\t\toptions.dumpRequest = true;\n\t\t\t\toptions.dumpResponse = true;\n\t\t\t} else if (args[i].equals(\"-n\") || args[i].startsWith(\"--threads=\")) {\n\t\t\t\tif (args[i].equals(\"-n\")) {\n\t\t\t\t\toptions.threadCount = Integer.parseInt(args[++i]);\n\t\t\t\t} else {\n\t\t\t\t\toptions.threadCount = Integer.parseInt(args[i].substring(\"--threads=\".length()));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new RuntimeException(\"unknown option '\" + args[i] + \"'\");\n\t\t\t}\n\t\t}\n\t\t// parse remainder\n\t\tString localPart = args[i++];\n\t\tString[] bits = localPart.split(\":\");\n\t\tif (bits[0].length() > 0) {\n\t\t\toptions.host = bits[0];\n\t\t}\n\t\toptions.port = Integer.parseInt(bits[1]);\n\n\t\tString remotePart = args[i++];\n\t\tbits = remotePart.split(\":\");\n\t\toptions.remoteHost = bits[0];\n\t\toptions.remotePort = Integer.parseInt(bits[1]);\n\n\t\treturn options;\n\t}\n}\n\nclass MonitorThread implements Runnable {\n\tstatic final int BUFLEN = 1024;\n\tstatic final byte[] EOF = new byte[0];\t\t// flag the end of channel \n\n\tSocket lsock;\n\tString rhost;\n\tint rport;\n\tboolean dumpRequest;\n\tboolean dumpResponse;\n\n\tSocket rsock;\n\tBlockingQueue<byte[]> lrchannel;\t\t\t// channel between read lsock and write rsock\n\tBlockingQueue<byte[]> rlchannel;\t\t\t// channel between read rsock and write lsock\n\n\tvolatile boolean shutdownRequested;\n\tThread readLSockThread;\n\tThread writeRSockThread;\n\tThread readRSockThread;\n\tThread writeLSockThread;\n\n\tpublic MonitorThread(Socket sock, String remoteHost, int remotePort, boolean dumpRequest, boolean dumpResponse) {\n\t\tthis.lsock = sock;\n\t\tthis.rhost = remoteHost;\n\t\tthis.rport = remotePort;\n\t\tthis.dumpRequest = dumpRequest;\n\t\tthis.dumpResponse = dumpResponse;\n\t}\n\n\tpublic void run() {\n\t\ttry {\n\t\t\trsock = new Socket(rhost, rport);\n\t\t} catch (UnknownHostException e) {\n\t\t\tSystem.out.println(\"unknown host: \" + rhost);\n\t\t\treturn;\n\t\t} catch (IOException e) {\n\t\t\tSystem.out.println(e);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tlrchannel = new ArrayBlockingQueue<byte[]>(10);\n\t\t\trlchannel = new ArrayBlockingQueue<byte[]>(10);\n\n\t\t\treadLSockThread = new Thread(new ReadLSockThread());\n\t\t\treadLSockThread.start();\n\t\t\twriteRSockThread = new Thread(new WriteRSockThread());\n\t\t\twriteRSockThread.start();\n\t\t\treadRSockThread = new Thread(new ReadRSockThread());\n\t\t\treadRSockThread.start();\n\t\t\twriteLSockThread = new Thread(new WriteLSockThread());\n\t\t\twriteLSockThread.start();\n\n\t\t\treadLSockThread.join();\n\t\t\twriteRSockThread.join();\n\t\t\treadRSockThread.join();\n\t\t\twriteLSockThread.join();\n\t\t\tSystem.out.println(\"connection closed: \" + lsock.getRemoteSocketAddress());\n\t\t} catch (InterruptedException e) {\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\t}\n\n\t// close all socks and stop all threads\n\tprivate void shutdown() {\n\t\ttry {\n\t\t\tif (shutdownRequested)\n\t\t\t\treturn;\n\t\t\tshutdownRequested = true;\n\t\t\tif (!lsock.isClosed())\n\t\t\t\tlsock.close();\n\t\t\tif (!rsock.isClosed())\n\t\t\t\trsock.close();\n\t\t\treadLSockThread.interrupt();\n\t\t\twriteRSockThread.interrupt();\n\t\t\treadRSockThread.interrupt();\n\t\t\twriteLSockThread.interrupt();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace(); // ignore this exception\n\t\t}\n\t}\n\n\tprivate void processException(Exception e) {\n\t\te.printStackTrace();\n\t\tshutdown();\n\t}\n\tprivate void processSocketClosed(SocketException e) {\n\t\t// this is a normal case, ignore it\n\t\t// System.err.println(\"lsock should be closed: \" + e);\n\t}\n\n\tclass ReadLSockThread implements Runnable {\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tbareRun();\n\t\t\t} catch (IOException e) {\n\t\t\t\tprocessException(e);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t}\n\t\t}\n\n\t\tprivate void bareRun() throws IOException, InterruptedException {\n\t\t\tInputStream in = null;\n\t\t\ttry {\n\t\t\t\tin = lsock.getInputStream();\n\t\t\t\tbyte[] buf = new byte[BUFLEN];\n\t\t\t\tint len;\n\t\t\t\twhile ((len = in.read(buf)) != -1) {\n\t\t\t\t\tbyte[] copy = new byte[len];\n\t\t\t\t\tSystem.arraycopy(buf, 0, copy, 0, len);\n\t\t\t\t\tlrchannel.put(copy);\n\t\t\t\t\tif (dumpRequest)\n\t\t\t\t\t\tSystem.err.print(new String(copy));\n\t\t\t\t}\n\t\t\t\tlrchannel.put(EOF);\t\t// flag the end of lchannel\n\t\t\t\tin.close();\n\t\t\t} catch (SocketException e) {\n\t\t\t\tprocessSocketClosed(e);\n\t\t\t} finally {\n\t\t\t\tlrchannel.put(EOF);\t\t// flag the end of lchannel\n\t\t\t\tif (in != null) in.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tclass WriteRSockThread implements Runnable {\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tbareRun();\n\t\t\t} catch (IOException e) {\n\t\t\t\tprocessException(e);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t}\n\t\t}\n\n\t\tprivate void bareRun() throws IOException, InterruptedException {\n\t\t\tOutputStream out = null;\n\t\t\ttry {\n\t\t\t\tout = rsock.getOutputStream();\n\t\t\t\twhile (true) {\n\t\t\t\t\tbyte[] buf = lrchannel.take();\n\t\t\t\t\tif (buf == EOF) break;\n\t\t\t\t\tout.write(buf);\n\t\t\t\t}\n\t\t\t} catch (SocketException e) {\n\t\t\t\tprocessSocketClosed(e);\n\t\t\t} finally {\n\t\t\t\tif (out != null) out.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tclass ReadRSockThread implements Runnable {\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tbareRun();\n\t\t\t} catch (IOException e) {\n\t\t\t\tprocessException(e);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t}\n\t\t}\n\n\t\tprivate void bareRun() throws IOException, InterruptedException {\n\t\t\tInputStream in = null;\n\t\t\ttry {\n\t\t\t\tin = rsock.getInputStream();\n\t\t\t\tbyte[] buf = new byte[BUFLEN];\n\t\t\t\tint len;\n\t\t\t\twhile ((len = in.read(buf)) != -1) {\n\t\t\t\t\tbyte[] copy = new byte[len];\n\t\t\t\t\tSystem.arraycopy(buf, 0, copy, 0, len);\n\t\t\t\t\trlchannel.put(copy);\n\t\t\t\t}\n\t\t\t} catch (SocketException e) {\n\t\t\t\tprocessSocketClosed(e);\n\t\t\t} finally {\n\t\t\t\t// flag the end of rlchannel\n\t\t\t\trlchannel.put(EOF);\n\t\t\t\t// rsock finished, closed it\n\t\t\t\tin.close();\n\t\t\t\trsock.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tclass WriteLSockThread implements Runnable {\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tbareRun();\n\t\t\t} catch (IOException e) {\n\t\t\t\tprocessException(e);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t}\n\t\t}\n\n\t\tprivate void bareRun() throws IOException, InterruptedException {\n\t\t\tOutputStream out = null;\n\t\t\ttry {\n\t\t\t\tout = lsock.getOutputStream();\n\t\t\t\twhile (true) {\n\t\t\t\t\tbyte[] buf = rlchannel.take();\n\t\t\t\t\tif (buf == EOF) break;\n\t\t\t\t\tout.write(buf);\n\t\t\t\t\tif (dumpResponse)\n\t\t\t\t\t\tSystem.err.print(new String(buf));\n\t\t\t\t}\n\t\t\t} catch (SocketException e) {\n\t\t\t\tprocessSocketClosed(e);\n\t\t\t} finally {\n\t\t\t\tif (out != null) out.close();\n\t\t\t\tlsock.close();\t\t// write finished, close it.\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "tools/tcpmon/TCPMonitorSelect.java",
    "content": "import java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.ServerSocket;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.ServerSocketChannel;\nimport java.nio.channels.SocketChannel;\nimport java.util.Iterator;\nimport java.util.Set;\n\n/**\n * <p>TCP监视器，它在本地监听端口，将请求转发到远端服务器，并将请求返回。和TCPMonitor功能一样，\n * 不同的是TCPMonitorSelect用的是非阻塞IO，所以它的性能理论上会高一些。</p>\n * \n * <p><code>java TCPMonitorSelect -a :8080 localhost:8000</code><br> 监听本地的8080端口，将请求转发给本机的8000端口，并将所有请求响应输出到标准错误输出。</p>\n * \n * <p><code>java TCPMonitorSelect :8080 work:22</code><br> 监听本地的8080端口，并将请求转发给work主机的22端口。</p>\n * \n * @author marlonyao<yaolei135@gmail.com>\n */\npublic class TCPMonitorSelect {\n\tprivate static final int BUFLEN = 1024;\n\t\n\tpublic static void main(String[] args) throws IOException {\n\t\tOptions options = null;\n\t\ttry {\n\t\t\toptions = parseArgs(args);\n\t\t\tif (options.help) {\n\t\t\t\tusage();\n\t\t\t\tSystem.exit(0);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tusage();\n\t\t\tSystem.exit(-1);\n\t\t}\n\t\t\n\t\tSelector selector = Selector.open();\n\t\t// initial server, start accept connections\n\t\tServerSocketChannel serverChannel = ServerSocketChannel.open();\n\t\tserverChannel.configureBlocking(false);\n\t\tServerSocket serverSocket = serverChannel.socket();\n\t\tserverChannel.socket().bind(new InetSocketAddress(options.host, options.port));\n\t\tSystem.out.println(\"server started on \" + serverSocket.getLocalSocketAddress());\n\t\tSelectionKey serverKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT);\n\t\tserverKey.attach(new ServerHandler(selector, serverChannel,\n\t\t\t\toptions.remoteHost, options.remotePort,\n\t\t\t\toptions.dumpRequest, options.dumpResponse));\n\t\t\n\t\twhile (true) {\n\t\t\tselector.select();\n\t\t\t\n\t\t\tSet<SelectionKey> keys = selector.selectedKeys();\n\t\t\tfor (Iterator<SelectionKey> itor = keys.iterator(); itor.hasNext();) {\n\t\t\t\tSelectionKey key = itor.next();\n\t\t\t\tHandler handler = (Handler) key.attachment();\n\t\t\t\thandler.execute(key);\n\t\t\t}\n\t\t\tkeys.clear();\n\t\t}\n\t}\n\n\tstatic class Options {\n\t\tboolean help;\n\t\tboolean dumpRequest;\n\t\tboolean dumpResponse;\n\n\t\tString host = \"localhost\";\n\t\tint port;\n\t\tString remoteHost;\n\t\tint remotePort;\n\t}\n\n\tprivate static void usage() {\n\t\tSystem.err.print(\n\t\t\t\t\"java TCPMonitorSelect [options] [host]:port remote_host:remote_port\\n\" +\n\t\t\t\t\"        -h, --help             print this help\\n\" +\n\t\t\t\t\"        -r, --dump-request     dump request to stderr\\n\" +\n\t\t\t\t\"        -s, --dump-response    dump response to stderr\\n\" +\n\t\t\t\t\"        -a, --dump-all         dump request and response to stderr\\n\"\n\t\t);\n\t}\t\n\n\tprivate static Options parseArgs(String[] args) {\n\t\tOptions options = new Options();\n\t\tint i;\n\t\t// parse options\n\t\tfor (i = 0; i < args.length; i++) {\n\t\t\tif (!args[i].startsWith(\"-\")) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (args[i].equals(\"-h\") || args[i].equals(\"--help\")) {\n\t\t\t\toptions.help = true;\n\t\t\t} else if (args[i].equals(\"-r\") || args[i].equals(\"--dump-request\")) {\n\t\t\t\toptions.dumpRequest = true;\n\t\t\t} else if (args[i].equals(\"-s\") || args[i].equals(\"--dump-response\")) {\n\t\t\t\toptions.dumpResponse = true;\n\t\t\t} else if (args[i].equals(\"-a\") || args[i].equals(\"--dump-all\")) {\n\t\t\t\toptions.dumpRequest = true;\n\t\t\t\toptions.dumpResponse = true;\n\t\t\t} else {\n\t\t\t\tthrow new RuntimeException(\"unknown option '\" + args[i] + \"'\");\n\t\t\t}\n\t\t}\n\t\t// parse remainder\n\t\tString localPart = args[i++];\n\t\tString[] bits = localPart.split(\":\");\n\t\tif (bits[0].length() > 0) {\n\t\t\toptions.host = bits[0];\n\t\t}\n\t\toptions.port = Integer.parseInt(bits[1]);\n\n\t\tString remotePart = args[i++];\n\t\tbits = remotePart.split(\":\");\n\t\toptions.remoteHost = bits[0];\n\t\toptions.remotePort = Integer.parseInt(bits[1]);\n\n\t\treturn options;\n\t}\n\t\n\tinterface Handler {\n\t\tvoid execute(SelectionKey key);\n\t}\n\t\n\t/*\n\t * process accept request.\n\t */\n\tstatic class ServerHandler implements Handler {\n\t\tprivate ServerSocketChannel serverChannel;\n\t\tprivate Selector selector;\n\t\tprivate String remoteHost;\n\t\tprivate int remotePort;\n\t\tprivate boolean dumpRequest;\n\t\tprivate boolean dumpResponse;\n\n\t\tpublic ServerHandler(Selector selector, ServerSocketChannel serverChannel,\n\t\t\t\tString remoteHost, int remotePort, boolean dumpRequest, boolean dumpResponse) {\n\t\t\tthis.selector = selector;\n\t\t\tthis.serverChannel = serverChannel;\n\t\t\tthis.remoteHost = remoteHost;\n\t\t\tthis.remotePort = remotePort;\n\t\t\tthis.dumpRequest = dumpRequest;\n\t\t\tthis.dumpResponse = dumpResponse;\n\t\t}\n\t\t\n\t\tpublic void execute(SelectionKey key) {\n\t\t\tSocketChannel lsockChannel = null;\n\t\t\ttry {\n\t\t\t\tlsockChannel = serverChannel.accept();\n\t\t\t\tSystem.out.println(\"accept connection: \" + lsockChannel.socket().getRemoteSocketAddress());\n\t\t\t\tSystem.out.flush();\n\t\t\t} catch (IOException e) {\n\t\t\t\tSystem.err.println(\"fail to accept connection\");\n\t\t\t\te.printStackTrace();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t// start client handler\n\t\t\tClientHandler handler = new ClientHandler(selector, lsockChannel, \n\t\t\t\t\tremoteHost, remotePort, dumpRequest, dumpResponse);\n\t\t\t// start connect to remote host\n\t\t\thandler.startConnect();\n\t\t}\n\t}\n\t\n\t/*\n\t * process loop: read lsock -> write rsock -> read rsock -> write lsock\n\t */\n\tstatic class ClientHandler implements Handler {\n\t\tprivate Selector selector;\n\t\tprivate String remoteHost;\n\t\tprivate int remotePort;\n\t\tprivate SocketChannel lsockChannel;\n\t\tprivate SocketChannel rsockChannel;\n\t\tprivate SelectionKey lsockKey;\n\t\tprivate SelectionKey rsockKey;\n\t\tprivate ByteBuffer lrBuffer;\t// buffer between read lsock and write rsock\n\t\tprivate ByteBuffer rlBuffer;\t// buffer between read rsock and write lsock\n\t\t\n\t\tprivate boolean dumpRequest;\n\t\tprivate boolean dumpResponse;\n\t\t\n\t\tpublic ClientHandler(Selector selector, SocketChannel lsockChannel, \n\t\t\t\tString remoteHost, int remotePort, boolean dumpRequest, boolean dumpResponse) {\n\t\t\tthis.selector = selector;\n\t\t\tthis.lsockChannel = lsockChannel;\n\t\t\tthis.remoteHost = remoteHost;\n\t\t\tthis.remotePort = remotePort;\n\t\t\tthis.lrBuffer = ByteBuffer.allocate(BUFLEN);\n\t\t\tthis.rlBuffer = ByteBuffer.allocate(BUFLEN);\n\t\t\t\n\t\t\tthis.dumpRequest = dumpRequest;\n\t\t\tthis.dumpResponse = dumpResponse;\n\t\t}\n\t\t\n\t\tpublic void startConnect() {\n\t\t\ttry {\n\t\t\t\t// connect rsock key\n\t\t\t\trsockChannel = SocketChannel.open();\n\t\t\t\trsockChannel.configureBlocking(false);\n\t\t\t\trsockChannel.connect(new InetSocketAddress(remoteHost, remotePort));\n\t\t\t\trsockKey = rsockChannel.register(selector, SelectionKey.OP_CONNECT);\n\t\t\t\trsockKey.attach(this);\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tcancel();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void execute(SelectionKey key) {\n\t\t\tif (!key.isValid())\n\t\t\t\treturn;\n\t\t\ttry {\n\t\t\t\tif (key.isReadable()) {\n\t\t\t\t\tif (key == lsockKey) {\n\t\t\t\t\t\treadLSock();\n\t\t\t\t\t} else {\n\t\t\t\t\t\treadRSock();\n\t\t\t\t\t}\n\t\t\t\t} else if (key.isWritable()) {\n\t\t\t\t\tif (key == lsockKey) {\n\t\t\t\t\t\twriteLSock();\n\t\t\t\t\t} else {\n\t\t\t\t\t\twriteRSock();\n\t\t\t\t\t}\n\t\t\t\t} else if (key.isConnectable()) {\n\t\t\t\t\tconnectRSock();\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tcancel();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void cancel() {\n\t\t\tif (lsockKey != null) {\n\t\t\t\tlsockKey.cancel();\n\t\t\t\ttry { lsockKey.channel().close(); } catch (IOException ioe) {}\n\t\t\t}\n\t\t\tif (rsockKey != null) {\n\t\t\t\trsockKey.cancel();\n\t\t\t\ttry { rsockKey.channel().close(); } catch (IOException ioe) {}\n\t\t\t}\n\t\t}\n\t\t\n\t\tprivate void readLSock() throws IOException {\n\t\t\tint n = lsockChannel.read(lrBuffer);\n\t\t\tif (n == -1) {\n\t\t\t\tlsockKey.interestOps(0);\n\t\t\t\trsockChannel.socket().shutdownOutput();\n\t\t\t} else {\n\t\t\t\tif (dumpRequest) {\n\t\t\t\t\tSystem.err.print(new String(lrBuffer.array(), 0, n));\n\t\t\t\t}\n\t\t\t\tlrBuffer.flip();\n\t\t\t\tlsockKey.interestOps(0);\n\t\t\t\trsockKey.interestOps(SelectionKey.OP_WRITE);\n\t\t\t}\n\t\t}\n\t\t\n\t\tprivate void writeRSock() throws IOException {\n\t\t\t/*int n = */rsockChannel.write(lrBuffer);\n\t\t\tif (lrBuffer.remaining() == 0) {\n\t\t\t\tlrBuffer.clear();\t\t// write finished\n\t\t\t\trsockKey.interestOps(SelectionKey.OP_READ);\n\t\t\t\tlsockKey.interestOps(SelectionKey.OP_READ);\n\t\t\t}\n\t\t}\n\t\t\n\t\tprivate void readRSock() throws IOException {\n\t\t\tint n = rsockChannel.read(rlBuffer);\n\n\t\t\tif (n == -1) {\n\t\t\t\tSystem.out.println(\"close connection: \" + lsockChannel.socket().getRemoteSocketAddress());\n\t\t\t\tSystem.out.flush();\n\t\t\t\t\n\t\t\t\trsockKey.cancel();\n\t\t\t\trsockChannel.close();\n\t\t\t\tlsockKey.interestOps(0);\n\t\t\t\tlsockKey.cancel();\n\t\t\t\tlsockChannel.close();\n\t\t\t} else {\n\t\t\t\trlBuffer.flip();\n\t\t\t\trsockKey.interestOps(0);\n\t\t\t\tlsockKey.interestOps(SelectionKey.OP_WRITE);\n\t\t\t}\n\t\t}\n\t\t\n\t\tprivate void writeLSock() throws IOException {\n\t\t\tint n = lsockChannel.write(rlBuffer);\n\t\t\tif (dumpResponse) {\n\t\t\t\tSystem.err.print(new String(rlBuffer.array(), rlBuffer.position()-n, n));\n\t\t\t}\n\t\t\tif (rlBuffer.remaining() == 0) {\n\t\t\t\trlBuffer.clear();\t\t// write finished\n\t\t\t\t\n\t\t\t\tlsockKey.interestOps(SelectionKey.OP_READ);\n\t\t\t\trsockKey.interestOps(SelectionKey.OP_READ);\n\t\t\t}\n\t\t}\n\t\t\n\t\tprivate void connectRSock() throws IOException {\n\t\t\trsockChannel.finishConnect();\n\t\t\t\n\t\t\tlsockChannel.configureBlocking(false);\n\t\t\tlsockKey = lsockChannel.register(selector, SelectionKey.OP_READ);\n\t\t\tlsockKey.attach(this);\n\t\t\t\n\t\t\trsockKey.interestOps(SelectionKey.OP_READ);\n\t\t}\n\t}\n}\n"
  }
]