用 make-network-process 网络编程的几个例子

make-network-process 支持 TCP/UDP/Unix Domain Socket,我们在用 url.el、LSP 和 Emacs Server 底层可能都会用到这个函数,我给它写了 4 个例子,每一个协议一个例子,最后一个例子是关于 Server 的,而不是 Client。

HTTP 客户端 (TCP)

请求 http://example.com/

(let ((proc (make-network-process
             :name "http-client"
             :buffer "*http-client*"
             :host "example.com"
             :service 80)))
  (process-send-string
   proc
   (concat
    "GET / HTTP/1.1\r\n"
    "Host: example.com\r\n"
    "Connection: close\r\n"
    "\r\n"))
  (display-buffer (process-buffer proc)))

:host 写域名或者 IP 地址,比如 example.com 或者 93.184.216.34。:service 填端口号或者协议名称,比如数字 80 或者字符串 http。

DNS 客户端 (UDP)

向 114.114.114.114 询问 example.com 的 IP 地址

(let ((proc (make-network-process
             :name "dns-client"
             :buffer "*dns-client*"
             :host "114.114.114.114"
             :service 53
             :type 'datagram
             :coding 'binary)))
  (process-send-string
   proc
   (unibyte-string
    0 0 1 0
    0 1 0 0
    0 0 0 0
    7 ?e ?x ?a ?m ?p ?l ?e
    3 ?c ?o ?m
    0
    0 1 0 1))
  (accept-process-output proc 3)
  (require 'bindat)
  (bindat-unpack
   '((ip ip))
   (with-current-buffer (process-buffer proc)
     (set-buffer-multibyte nil)
     (substring (buffer-string) -4))))
;; => ((ip . [93 184 216 34]))

:type 'datagram 表示用 UDP,不然默认用 TCP。:coding 'binary 表示不编码数据。

Emacs Server 客户端 (Unix Domain Socket)

使用 Emacs Server 计算 (+ 1 2)

(let ((proc (make-network-process
             :name "emacsclient"
             :buffer "*emacsclient*"
             :family 'local
             :service (expand-file-name
                       server-name
                       server-socket-dir))))
  (process-send-string
   proc
   (concat "-eval "
           (server-quote-arg (format "%S" '(+ 1 2)))
           "\n"))
  (accept-process-output proc)
  (display-buffer (process-buffer proc)))

:family 'local 表示用 Unix Domain Socket,不然默认用 IP v4/6。:service 不再是 TCP/UDP 端口了,而是 Socket 文件的路径。

HTTP 服务器

报告时间的 HTTP 服务器:

~ $ curl localhost:8000
Wed Dec 18 23:45:18 2019

(make-network-process
 :name "http-server"
 :server t
 :service 8000
 :sentinel
 (lambda (process event)
   (cond
    ((string-prefix-p "open from" event)
     (process-send-string process
                          (concat "HTTP/1.1 200 OK\r\n"
                                  "Content-Type: text/plain\r\n"
                                  "Content-Length: 24\r\n"
                                  "\r\n"
                                  (current-time-string))))
    ((string= "connection broken by remote peer\n" event)
     (kill-buffer (process-buffer process))))))

:server t 表示 Server,不然默认是 Client。服务器每次接收连接时都会创建一个新的 Process,专门处理这个连接。

13 个赞

我想用Emacs lisp实现一个socks或者http代理,因为我想用emacs实现一下抓包的功能。

这个楼主有什么想法吗?

客户端还是服务器端?Emacs 的 url.el 支持 socks 和 http 代理(也就是作为客户端),我前几日实现过 socks 4 和 http 代理服务器:

我还没搞明白 socks 5 和怎么用 http CONNECT 处理 https,所以没实现。

不清楚什么意思,Emacs 顶多就能获得 TCP/UDP 包的数据部分,其它都获取不了。

简单来说就是把emacs当作其他程序的代理而不是emacs使用其他代理。

使用场景:emacs监听某个端口,然后其他程序设置代理地址时使用了emacs监听的那个端口,此时emacs是作为服务端的。之后emacs需要把来自其他程序的数据转发到真正的地址,收到数据后再返回给程序。

我的最终目的是获取一些应用程序的用户数据,我不知道怎么用emacs直接读内存,但是我觉得可以通过程序从服务端获取数据时直接截取。特别是可以在手机上设置代理。

直接读内存这是有指针的语言干的事。Elisp恐怕不行

是的,所以需要换个思路。再打个比方就是把emacs当作fiddler用,捕获一下其他应用程序的收发数据。

哦,实现 http 或 socks 代理服务器,然后 Emacs 就知道了,比如通过 http 代理访问 http://example.com ,Emacs 收到应用发来的:

GET http://example.com/ HTTP/1.1
...

Emacs 返回给应用:

HTTP/1.1 200 OK
...

用专门的代理服务器软件,然后用 tcpdump 分析

$ sudo tcpdump -i any port 1087

但是要解密 TLS(如 HTTPS)的话,可以试试专门的软件,我刚刚试了下 proxyman,可以看到 HTTPS 的内容。

请教一个问题,在win10上,make-network-process函数为什么不支持指定 :type 参数为 datatgramstream呢?

错误提示: (error “Unsupported connection type”)

特地在 Windows 机器上安装了 Emacs 试了下,遇到同样问题。搜索发现一个十来年也未解决的报告: #9586 The Windows port does not support datagram sockets

谢谢。从#9586的描述看,短时间不能解决。

感谢楼主的例子,解决了我的问题。

1 个赞