TCP/IP
Posted on
Apr 29, 2018
网络是分布式系统之间通信的媒介,掌握网络编程自然变成了程序猿们居家旅行必备技能之一了,与直接使用应用层的 http
协议不同,我们一般使用传输层的协议例如 tcp
进行编程,掌握好了 tcp
编程,我们甚至能写一套类似http
这样的应用层协议,是不是很兴奋呢,那今天就让我们从最简单的例子出发,掌握 tcp/ip
编程的精髓吧。
一般来说大部分网络通信模型是 c/s
模型,也有部分是 p2p
模型,今天我们先了解一下 c/s
模型的编程使用吧。
客户端要想与服务器通信,前提必须是服务器已经启动并且监听了某个端口了。如果服务器没有监听而客户端直接连接的话,就会报类似下面这样的错误:
dial tcp 127.0.0.1:8080: connect: connection refused
也就是说,服务器端要调用 net.Listen
接口对特定端口进行监听,监听会返回一个 Listener
,服务器一旦监听到来自客户端的请求,就会调用 Accept
来接受该请求,Accept
会返回一个 net.Conn
,以后服务器就可以使用 net.Conn
与客户端进行通信。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type Listener interface { Accept() (Conn, error) Close() error Addr() Addr } type Conn interface { Read(b []byte ) (n int , err error) Write(b []byte ) (n int , err error) Close() error LocalAddr() Addr RemoteAddr() Addr SetDeadline(t time.Time) error SetReadDeadline(t time.Time) error SetWriteDeadline(t time.Time) error }
server.go 一般来说,服务器需要处理来自多个客户端的请求,所以我们使用goroutine来处理不同客户端的请求,这样就达到一个并发的目的。
服务器处理客户端请求的主要的流程如下:Listen => Accept => Read & Write => Close
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package main import ( "log" "net" "fmt" "bufio" "strings" ) func main () { fmt.Println("Launching server..." ) l, err := net.Listen("tcp" , ":8080" ) if err != nil { log.Fatal(err) } defer l.Close() for { conn, err := ln.Accept() if err != nil { log.Fatal(err) } go handleConn(conn) } } func handleConn (c net.Conn) { for { m, _ := bufio.NewReader(c).ReadString('\n' ) fmt.Print("Message Received:" , string (m)) newMsg := strings.ToUpper(m) c.Write([]byte (newMsg + "\n" )) } }
client.go 一般来说连接都是由客户端主动发起的,客户端调用 net.Dial
向服务器发起连接行为,在正常情况下,则也返回一个 net.Conn
,注意到客户端和服务器都是通过 net.Conn
进行通信的,linux基础好的同学,就知道它的本质就是一个套接字(socket),而套接字的本质则是一个文件,所以我们可以对待正常文件一样去使用它,例如使用 fmt.Fprintf
来对套接字写入数据。
主要的流程如下:Dial => Read & Write => Close
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "os" "log" "net" "fmt" "bufio" ) func main () { c, e := net.Dial("tcp" , "localhost:8080" ) if e != nil { log.Fatal(e) } reader := bufio.NewReader(os.Stdin) for { fmt.Print("Text to send: " ) text, err := reader.ReadString('\n' ) if err != nil { log.Fatal(err) } fmt.Fprintf(c, text + "\n" ) m, e := bufio.NewReader(c).ReadString('\n' ) if e != nil { log.Fatal(e) } fmt.Print("Message from server: " + m) } }
运行 先对两个源文件进行编译。
在一个命令行中输入:
在另一个命令行中输入: