上一节简单介绍了TCP以及socket等基本理论,今天我们尝试用go分别从客户端和服务端对其实现。
package main
import (
"net"
"fmt"
)
func main() {
fmt.Println("server has been start===>")
tcpAddr, _ := net.ResolveTCPAddr("tcp",":8000")
//服务器端一般不定位具体的客户端套接字
tcpListener, _ := net.ListenTCP("tcp",tcpAddr)
ConnMap := make(map[string]*net.TCPConn)
for{
tcpConn, _ := tcpListener.AcceptTCP()
defer tcpConn.Close()
ConnMap[tcpConn.RemoteAddr().String()] = tcpConn
fmt.Println("连接的客户端信息:",tcpConn.RemoteAddr().String())
}
}
其中类型TCPConn是net包中定义的类型,它实现了conn接口,用来表示服务端与客户端的连接。
type TCPConn struct {
conn
}
在任意时刻,计算机都可以有几条TCP连接处于打开状态,同样服务器也同时存在许多条TCP连接。从上一节内容可知,在client与server的通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。go中用TCPAddr来表示端点。
type TCPAddr struct {
IP IP
Port int
Zone string // IPv6 scoped addressing zone
}
go语言中可以通过方法ResolveTCPAddr(network, address string) (*TCPAddr, error) 来获得TCPAddr。参数network用来指定网络类型,可接受的参数为tcp、tcp4、tcp6,分别表示IP v4、IP v6和任意。
接下来就是设置允许套接字进行连接(listen)和等待连接(accept)。
package main
import (
"net"
"fmt"
"os"
"bufio"
)
var ch = make(chan int)
func main() {
tcpAddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8000")
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
fmt.Println("server is not starting")
return
}
defer conn.Close()
for {
inputReader := bufio.NewReader(os.Stdin)
input, err := inputReader.ReadString('\n')
if err == nil {
fmt.Printf("client send:%s", input)
}
//将从输入中读取的内容写入到连接中
b := []byte(input)
conn.Write(b)
select {
case <-ch:
fmt.Println("server error,please reconnecting")
return
default:
//不加default的话,那么<-ch会阻塞for,下一个输入就没法进行
}
}
}
client实现比服务端简单一点,不用监听等一系列动作,只需要设置好socket,进行连接就可以。go中方法DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)可以用来连接指定的socket。参数laddr为nil时,系统会自动选择一个socket作为client的TCPAddr。参数raddr为要连接的TCPAddr。如果连接成功,方法会返回一个TCPConn连接。
在连接成功后,就可以使用下面两种方法对数据进行读写。
func (c *conn) Write(b []byte) (int, error) {}
func (c *conn) Read(b []byte) (int, error) {}
由于conn实现了Write和Read接口,因此可以在TCPConn上,直接调用这两种方法。