2-11行声明了程序将要用到的包。需要注意,如果一个包被包含了但是没有用到,Go将认为这是一种错误,并强制删除没有用到的声明(还记得上次你想放弃并不耐烦地清理C++项目中STL包含进来的列表吗?)
1 package main
2 import (
3 "encoding/hex"
4 "flag"
5 "fmt"
6 "net"
7 "os"
8 "runtime"
9 "strings"
10 "time"
11 )
12-16行声明了用来表示命令行标识的全局变量。下面将看到如何解析它们。
12 var (
13 host *string = flag.String("host", "",
"target host or address")
14 port *string = flag.String("port", "0", "target port")
15 listen_port *string = flag.String("listen_port", "0",
"listen port")
16 )
17-20行我们看到了Go语言可变参数函数参数的语法。
17 func die(format string, v ...interface{}) {
18 os.Stderr.WriteString(fmt.Sprintf(format+"\n", v...))
19 os.Exit(1)
20 }
21-28行有两个函数用来启动十六进制导出和二进制日志器。区别仅在于日志名不同。
21 func connection_logger(data chan []byte, conn_n int,
local_info, remote_info string) {
22 log_name := fmt.Sprintf("log-%s-%04d-%s-%s.log",
format_time(time.Now()), conn_n, local_info, remote_info)
23 logger_loop(data, log_name)
24 }
25 func binary_logger(data chan []byte, conn_n int, peer string) {
26 log_name := fmt.Sprintf("log-binary-%s-%04d-%s.log",
format_time(time.Now()), conn_n, peer)
27 logger_loop(data, log_name)
28 }
29-43行是Go乐趣的开始,logger_loop函数创建一个日志文件,然后开始进入无限循环(35-42行)。36行代码等待来之管道data的消息。一个有意思的技巧在34行,defer操作符允许我们定义一个代码块,在函数域的末尾这个代码块一定会执行(类似于Java的 finally)。如果接收到的数据为空,函数退出。
29 func logger_loop(data chan []byte, log_name string) {
30 f, err := os.Create(log_name)
31 if err != nil {
32 die("Unable to create file %s, %v\n", log_name, err)
33 }
34 defer f.Close()
35 for {
36 b := <-data
37 if len(b) == 0 {
38 break
39 }
40 f.Write(b)
41 f.Sync()
42 }
43 }
44 func format_time(t time.Time) string {
45 return t.Format("2006.01.02-15.04.05")
46 }
47 func printable_addr(a net.Addr) string {
48 return strings.Replace(a.String(), ":", "-", -1)
49 }
50 type Channel struct {
51 from, to net.Conn
52 logger, binary_logger chan []byte
53 ack chan bool
54 }