1读写操作
1.1 write和read注意事项
1.2 并发读写
并发读写只两方面:
1.读操作和写操作是并发执行的
2.可能出现多个goroutine同时读或写
3.因此在go中,要使用goroutine完成,同一个连接的并发读或写操作是goroutine并发安全的。指的是同时存在多个goroutine并发的读写,之间是不会相互影响的,这个在实操中,主要针对write操作,conn.write()增加了锁操作
- 读操作和写操作并发执行
- 可能出现多个goroutine同时读或写
服务端
//并发的读和写操作 全双工
func TcpServerRWConcurrency() {
address := ":5678" //any ip or version
//A.基于某个地址进行监听
listen, err := net.Listen(tcp, address) //端口省略 随机端口 address := "127.0.0.1:"
if err != nil {
log.Fatalln(err)
}
//关闭监听
defer listen.Close()
log.Printf("%s server is listening on %s\n", tcp, listen.Addr())
//B.接受连接请求
//循环接受
for {
//阻塞接受
conn, err := listen.Accept()
if err != nil {
log.Println(err)
}
go HandleConnConcurrency(conn)
}
}
func HandleConnConcurrency(conn net.Conn) {
//处理连接,读写
//日志连接的远程地址(client addr)
log.Printf("aceept from %s\n", conn.RemoteAddr())
//A.关闭连接
defer conn.Close()
wg := sync.WaitGroup{}
//并发写
wg.Add(1)
go SerWrite(conn, &wg, "123")
//多并发写
wg.Add(1)
go SerWrite(conn, &wg, "456")
wg.Add(1)
go SerWrite(conn, &wg, "789")
//并发读
wg.Add(1)
go SerRead(conn, &wg)
wg.Wait()
}
func SerWrite(conn net.Conn, wg *sync.WaitGroup, data string) {
defer wg.Done()
for {
//向客户端发送数据 write
wn, err := conn.Write([]byte(data + "send some data from server " + "\n"))
if err != nil {
log.Println(err)
}
log.Println("向客户端发送的长度为:", wn)
time.Sleep(200 * time.Millisecond)
}
}
func SerRead(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
for {
//从客户端读取数据 read
buf := make([]byte, 1024)
rn, err := conn.Read(buf)
if err != nil {
log.Println(err)
}
log.Println("receive from client data are: ", string(buf[:rn]))
}
}
客户端
func TcpClientRWConcurrency() {
address := "127.0.0.1:5678"
//A.建立连接
conn, err := net.DialTimeout(tcp, address, time.Second)
if err != nil {
log.Println(err)
return
}
//保证关闭
defer conn.Close()
log.Printf("connection is establish,client addr is %s\n", conn.LocalAddr())
wg := sync.WaitGroup{}
//并发写
wg.Add(1)
go CliWrite(conn, &wg)
//并发读
wg.Add(1)
go CliRead(conn, &wg)
wg.Wait()
}
func CliWrite(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
for {
//向客户端发送数据 write
wn, err := conn.Write([]byte("send some data from client " + "\n"))
if err != nil {
log.Println(err)
}
log.Println("向客户端发送的长度为:", wn)
time.Sleep(500 * time.Millisecond)
}
}
func CliRead(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
for {
//从客户端读取数据 read
buf := make([]byte, 1024)
rn, err := conn.Read(buf)
if err != nil {
log.Println(err)
}
log.Println("receive from server data are: ", string(buf[:rn]))
}
}
- 并发读写安全,指的是数据完整,顺序并不保证
2 格式化消息
2:06 sc
7 20 sc
服务端
//格式化消息
func TcpServerFormat() {
address := ":5678" //any ip or version
//A.基于某个地址进行监听
listen, err := net.Listen(tcp, address) //端口省略 随机端口 address := "127.0.0.1:"
if err != nil {
log.Fatalln(err)
}
//关闭监听
defer listen.Close()
log.Printf("%s server is listening on %s\n", tcp, listen.Addr())
//B.接受连接请求
//循环接受
for {
//阻塞接受
conn, err := listen.Accept()
if err != nil {
log.Println(err)
}
go HandleConnFormat(conn)
}
}
func HandleConnFormat(conn net.Conn) {
//处理连接,读写
//日志连接的远程地址(client addr)
log.Printf("aceept from %s\n", conn.RemoteAddr())
//A.关闭连接
defer conn.Close()
wg := sync.WaitGroup{}
//并发写
wg.Add(1)
go SerWriteFormat(conn, &wg)
wg.Wait()
}
func SerWriteFormat(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
for {
//发送端
//发送数据前进行编码
//创建需要传递的数据
type Message struct {
Id uint `json:"id,omitempty"`
Code string `json:"code,omitempty"`
Content string `json:"content,omitempty"`
}
message := Message{
Id: uint(rand.Int()),
Code: "SERVER-STARDEND ",
Content: "message form server",
}
//编码后数据展示
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
if err := encoder.Encode(message); err != nil {
log.Println(err)
continue
}
log.Println(buf.String())
/*
//1 JSON 文本编码
//创建编码器,向编码器写信息
encoder := json.NewEncoder(conn)
//利用编码器进行编码
//encode成功后,会写入至conn,已经完成了conn.Write()
if err := encoder.Encode(message); err != nil {
log.Println(err)
continue
}
*/
log.Println("MESSAGE WAS SEND!!")
//2 GOB 二进制编码
//encoder := gob.NewEncoder() 其他不变
time.Sleep(time.Second)
}
}
客户端
//格式化消息
func TcpClientFormat() {
address := "127.0.0.1:5678"
//A.建立连接
conn, err := net.DialTimeout(tcp, address, time.Second)
if err != nil {
log.Println(err)
return
}
//保证关闭
defer conn.Close()
log.Printf("connection is establish,client addr is %s\n", conn.LocalAddr())
wg := sync.WaitGroup{}
//并发读
wg.Add(1)
go CliReadFormat(conn, &wg)
wg.Wait()
}
func CliReadFormat(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
for {
//从服务端读取数据 read
type Message struct {
Id uint `json:"id,omitempty"`
Code string `json:"code,omitempty"`
Content string `json:"content,omitempty"`
}
message := Message{}
//接受数据后解码
//1 JSON 解码
//创建解码器
decoder := json.NewDecoder(conn)
//利用解码器进行解码
//解码操作,从conn中读取内容,成功后会将解码后结果,赋值到 message 变量中
if err := decoder.Decode(&message); err != nil {
log.Println(err)
continue
}
log.Println(message)
//2 GOB 解码
//decoder := gob.NewDecoder(conn). 其他不变
}
}
3 短连接 长连接
- 短链接示意图
- 长连接示意图
3.1 短链接
- 服务端
//短连接
func TcpServerShort() {
address := ":5678" //any ip or version
//A.基于某个地址进行监听
listen, err := net.Listen(tcp, address) //端口省略 随机端口 address := "127.0.0.1:"
if err != nil {
log.Fatalln(err)
}
//关闭监听
defer listen.Close()
log.Printf("%s server is listening on %s\n", tcp, listen.Addr())
//B.接受连接请求
//循环接受
for {
//阻塞接受
conn, err := listen.Accept()
if err != nil {
log.Println(err)
}
go HandleConnShort(conn)
}
}
func HandleConnShort(conn net.Conn) {
//处理连接,读写
//日志连接的远程地址(client addr)
log.Printf("aceept from %s\n", conn.RemoteAddr())
//A.关闭连接
defer conn.Close()
wg := sync.WaitGroup{}
//并发写
wg.Add(1)
go SerWriteShort(conn, &wg)
wg.Wait()
}
func SerWriteShort(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
//发送端
type Message struct {
Id uint `json:"id,omitempty"`
Code string `json:"code,omitempty"`
Content string `json:"content,omitempty"`
}
message := Message{
Id: uint(rand.Int()),
Code: "SERVER-STARDEND ",
Content: "message form server",
}
encoder := gob.NewEncoder(conn)
//Encode成功后,会写入到conn,已经完成了conn.Write()
if err := encoder.Encode(message); err != nil {
log.Println(err)
}
log.Println("message was send")
log.Println("link will be close")
return
}
- 客户端
//短连接
func TcpClientShort() {
address := "127.0.0.1:5678"
//A.建立连接
conn, err := net.DialTimeout(tcp, address, time.Second)
if err != nil {
log.Println(err)
return
}
//保证关闭
defer conn.Close()
log.Printf("connection is establish,client addr is %s\n", conn.LocalAddr())
wg := sync.WaitGroup{}
//并发读
wg.Add(1)
go CliReadShort(conn, &wg)
wg.Wait()
}
func CliReadShort(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
type Message struct {
Id uint `json:"id,omitempty"`
Code string `json:"code,omitempty"`
Content string `json:"content,omitempty"`
}
message := Message{}
for {
//从服务端读取数据 read
//2 GOB 解码
decoder := gob.NewDecoder(conn)
//利用解码器进行解码
//解码操作,从conn中读取内容,成功后会将解码后结果,赋值到 message 变量中
err := decoder.Decode(&message)
//错误为 io.EOF时,表示连接被给关闭
if err != nil && errors.Is(err, io.EOF) {
log.Println(err)
log.Println("link was closed!")
break
}
log.Println(message)
}
}
3.2 长连接的心跳检测![请添加图片描述](https://img-blog.csdnimg.cn/9ea86173ab9e46f48384088c10c1fac2.png)
开启服务 多开几个客户端,关闭其中某些客户端
服务器端检测时,会主动断开连接。
- 服务端
func TcpServerHB() {
address := ":5678" //any ip or version
//A.基于某个地址进行监听
listen, err := net.Listen(tcp, address) //端口省略 随机端口 address := "127.0.0.1:"
if err != nil {
log.Fatalln(err)
}
//关闭监听
defer listen.Close()
log.Printf("%s server is listening on %s\n", tcp, listen.Addr())
//B.接受连接请求
//循环接受
for {
//阻塞接受
conn, err := listen.Accept()
if err != nil {
log.Println(err)
}
go HandleConnHB(conn)
}
}
func HandleConnHB(conn net.Conn) {
//处理连接,读写
//日志连接的远程地址(client addr)
log.Printf("aceept from %s\n", conn.RemoteAddr())
//A.关闭连接
defer func() {
log.Println("current connection be closed!")
conn.Close()
}()
wg := sync.WaitGroup{}
//独立goroutine,在连接建立后,周期发送ping
//发送ping
wg.Add(1)
go SerPing(conn, &wg)
wg.Wait()
}
func SerPing(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
//启动接收 pong 为什么放在这里面?是因为ping停止的时候 pong也应该停止
ctx, cancel := context.WithCancel(context.Background())
go SerReadPong(conn, ctx)
//ping失败次数
const maxPingNum = 3
pingErrCounter := 0
//周期性的发送
//利用 time.Ticker
ticker := time.NewTicker(2 * time.Second)
for t := range ticker.C {
pingMsg := MessageHB{
Id: uint(rand.Int()),
Code: "PING-SERVER",
Time: t,
}
encoder := gob.NewEncoder(conn)
//Encode成功后,会写入到conn,已经完成了conn.Write()
if err := encoder.Encode(pingMsg); err != nil {
log.Println(err)
//连接有问题的情况
//累加错误计数器
pingErrCounter++
//判断是否达到上限
if pingErrCounter == maxPingNum {
//心跳失败
//终止接收 pong 的处理
cancel()
return
}
}
log.Printf("ping send to %s,ping id is %d ", conn.RemoteAddr(), pingMsg.Id)
}
}
func SerReadPong(conn net.Conn, ctx context.Context) {
for {
//处理ping结束
select {
case <-ctx.Done():
return
default:
message := MessageHB{}
decoder := gob.NewDecoder(conn)
err := decoder.Decode(&message)
//错误为 io.EOF时,表示连接被给关闭
if err != nil && errors.Is(err, io.EOF) {
log.Println(err)
break
}
//判断是否为pong类型
if message.Code == "PONG-CLIENT" {
log.Printf("receive ping from %s,ping id is %s", conn.RemoteAddr(), message.Content)
}
}
}
}
- 客户端
type MessageHB struct {
Id uint `json:"id,omitempty"`
Code string `json:"code,omitempty"`
Content string `json:"content,omitempty"`
Time time.Time `json:"time,omitempty"`
}
func TcpClientHB() {
address := "127.0.0.1:5678"
//A.建立连接
conn, err := net.DialTimeout(tcp, address, time.Second)
if err != nil {
log.Println(err)
return
}
//保证关闭
defer conn.Close()
log.Printf("connection is establish,client addr is %s\n", conn.LocalAddr())
wg := sync.WaitGroup{}
//并发读
wg.Add(1)
go CliReadPing(conn, &wg)
wg.Wait()
}
func CliReadPing(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
message := MessageHB{}
for {
decoder := gob.NewDecoder(conn)
err := decoder.Decode(&message)
//错误为 io.EOF时,表示连接被给关闭
if err != nil && errors.Is(err, io.EOF) {
log.Println(err)
break
}
//判断是否为ping类型
if message.Code == "PING-SERVER" {
log.Println("receive ping from", conn.RemoteAddr())
CliWritePong(conn, message)
}
}
}
func CliWritePong(conn net.Conn, pingMsg MessageHB) {
pongMsg := MessageHB{
Id: uint(rand.Int()),
Code: "PONG-CLIENT",
Content: fmt.Sprintf("pingID :%d", pingMsg.Id),
Time: time.Now(),
}
encoder := gob.NewEncoder(conn)
//Encode成功后,会写入到conn,已经完成了conn.Write()
if err := encoder.Encode(pongMsg); err != nil {
log.Println(err)
return
}
log.Println("pong was send to ", conn.RemoteAddr())
return
}
4 连接池
4.1 核心结构
//连接池接口
type Pool interface {
//获取连接
Get() (net.Conn, error) //interface{}==any
//放回连接
Put(net.Conn) error
//释放池,关闭连接
Release() error
//有效连接长度
Len() int
}
//连接工厂接口
type ConnFactory interface {
//生产连接
Factory() (net.Conn, error)
//关闭连接
Close(net.Conn) error
//Ping
Ping(net.Conn) error
}
//连接池配置
type PoolConfig struct {
//最小连接数,至少保持多少个有效连接
MinConnNum int
//最大连接数,池中最多支持多少连接
MaxConnNum int
//最大空闲连接数,池中最多有多少可用的连接
MaxIdleNum int
//空闲连接超时时间,多久后空闲连接会被释放
IdleTimeOut time.Duration
//连接工厂
Factory ConnFactory
}
//空闲连接类型(管理的连接)
type IdleConn struct {
//连接本身
conn net.Conn
//放入池子的时间,用于判断是否空闲超时
putTime time.Time
}
//连接池结构
type TcpPool struct {
//配置信息
config PoolConfig
//运行时信息
//使用连接数量
openingConnNum int
//空闲连接链表
idleList chan *IdleConn
//并发安全锁
mu sync.RWMutex
}
4.2 生产工厂的实现
//TCP连接工厂类型
type TcpConnFactory struct{}
//产生连接方法
func (*TcpConnFactory) Factory(addr string) (net.Conn, error) {
//校验参数合理性
if addr == "" {
return nil, errors.New("addr is empty!")
}
//建立连接
conn, err := net.DialTimeout("tcp", addr, time.Second*5)
if err != nil {
return nil, err
}
//return
return conn, nil
}
//关闭连接
func (*TcpConnFactory) Close(conn net.Conn) error {
return conn.Close()
}
func (*TcpConnFactory) Ping(conn net.Conn) error {
return nil
}
4.3 完善连接池基本结构
//连接池结构实现连接池(Pool)接口
func (*TcpPool) Get() (net.Conn, error) {
return nil, nil
}
func (*TcpPool) Put(conn net.Conn) error {
return nil
}
func (*TcpPool) Release() error {
return nil
}
func (*TcpPool) Len() int {
return 0
}
4.4 创建连接池函数
//创建TcpPool对象
func NewTcpPool(addr string, poolConfig PoolConfig) (*TcpPool, error) {
//1.校验参数
if addr == "" {
return nil, errors.New("The addr is empty!")
}
//校验工厂存在
if poolConfig.Factory == nil {
return nil, errors.New("factory is not exists!")
}
//最大连接数
if poolConfig.MaxConnNum == 0 {
//a return错误
//return nil, errors.New("max conn num is zeor!")
//b 人为修改一个合理的值
poolConfig.MaxConnNum = defaultMaxConnNum
}
//初始化连接数
if poolConfig.InitConnNum == 0 {
poolConfig.InitConnNum = defaultInitConnNum
} else if poolConfig.InitConnNum > poolConfig.MaxConnNum {
poolConfig.InitConnNum = poolConfig.MaxConnNum
}
//合理化最大空闲连接数
if poolConfig.MaxIdleNum == 0 {
poolConfig.MaxIdleNum = poolConfig.InitConnNum
} else if poolConfig.MaxIdleNum > poolConfig.MaxConnNum {
poolConfig.MaxIdleNum = poolConfig.MaxConnNum
}
//2.初始化TcpPool对象
pool := TcpPool{
config: poolConfig,
openingConnNum: 0,
idleList: make(chan *IdleConn, poolConfig.MaxIdleNum),
mu: sync.RWMutex{},
}
//3.初始化连接
//根据initConnNum配置来创建
for i := 0; i < poolConfig.InitConnNum; i++ {
conn, err := pool.config.Factory.Factory(addr)
if err != nil {
//通常意味着连接初始化失败
//释放可能已经存在的连接
pool.Release()
return nil, err
}
//连接创建成功
//加入到空闲连接队列中
pool.idleList <- &IdleConn{
conn: conn,
putTime: time.Now(),
}
}
return &pool, nil
}
4.5 从连接池中获取连接
func (pool *TcpPool) Get() (net.Conn, error) {
//1.锁定
pool.mu.Lock()
defer pool.mu.Unlock()
//2.获取空闲连接,若没有则创建链接
for {
select {
//2.1获取空闲连接
case idleConn, ok := <-pool.idleList:
//判断channel是否被关闭
if !ok {
return nil, errors.New("idle list is closed!")
}
//判断连接是否超时
if pool.config.IdleTimeOut > 0 { //设置了超时时间
//putime + IdleTimeOut 是否在 now 之前
if idleConn.putTime.Add(pool.config.IdleTimeOut).Before(time.Now()) {
//关闭连接,继续查找下一个连接
_ = pool.config.Factory.Close(idleConn.conn)
log.Println("空闲连接超时!")
continue
}
}
//判断连接是否可用
if err := pool.config.Factory.Ping(idleConn.conn); err != nil {
//ping失败,连接不可用
//关闭连接,继续查找
_ = pool.config.Factory.Close(idleConn.conn)
continue
}
//找到可用空闲连接
log.Println("get conn from idle")
//使用的连接计数
pool.openingConnNum++
//返回连接
return idleConn.conn, nil
//2.2创建连接
default:
//a.判断是否还可以继续创建
//基于开放的连接是否已经达到了连接池最大的连接数
if pool.openingConnNum >= pool.config.MaxConnNum {
return nil, errors.New("max opening connection")
//另一种方案 阻塞 意思:只要有连接被放回就可以执行第一个case,再此之前 一直default中continue循环
//continue
}
//b.创建连接
conn, err := pool.config.Factory.Factory(pool.addr)
if err != nil {
return nil, err
}
//c正确创建了连接
log.Println("get conn from factory")
//使用连接计数
pool.openingConnNum++
//返回连接
return conn, nil
}
}
}
4.6 将连接放回连接池
func (pool *TcpPool) Put(conn net.Conn) error {
//1.锁
pool.mu.Lock()
defer pool.mu.Unlock()
//2.校验
if conn == nil {
return errors.New("connection is not exist!")
}
//判断空闲连接列表是否存在
if pool.idleList == nil {
//关闭连接
_ = pool.config.Factory.Close(conn)
return errors.New("idlelist is not exist!")
}
//3.放回连接
select {
//放回连接
case pool.idleList <- &IdleConn{
conn: conn,
putTime: time.Now(),
}:
//只要可以发送成功,任务完成
//更新开放的连接数量
pool.openingConnNum--
//fmt.Println("运行时连接数量", pool.openingConnNum, "len(pool.idleList)", len(pool.idleList))
return nil
//关闭连接
default:
_ = pool.config.Factory.Close(conn)
return nil
}
}
4.7 释放连接池
//释放连接池
func (pool *TcpPool) Release() error {
//1.并发锁
pool.mu.Lock()
defer pool.mu.Unlock()
//2.确定连接池是否被释放
if pool.idleList == nil {
return nil
}
//3.关闭Idlelist
close(pool.idleList)
//4.释放全部空闲连接
//继续接收已关闭channel中的元素
for idleConn := range pool.idleList {
//关闭连接
_ = pool.config.Factory.Close(idleConn.conn)
}
log.Println("已经释放所有连接!")
return nil
}
4.8 总结
5 TCP黏包
5.1 Header方案的粘包解决实现
- 定义编码器解码器
//定义编码器(发送端)
type Encoder struct {
//编码结束后,写入目标
w io.Writer
}
//创建编码器函数
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{
w: w,
}
}
//编码,将编码的结果 写入 w io.write
//binary(int32(13))[]byte("package data.")
//定义编码器
func (enc Encoder) Encode(message string) error {
//1.获取message的长度
l := int32(len(message))
//构建一个数据包缓存
buf := new(bytes.Buffer)
//2.在数据包中写入长度
//需要二进制的写入操作,需要将数据以bit的形式写入
if err := binary.Write(buf, binary.LittleEndian, l); err != nil {
return err
}
//3.将数据主体body写入
//if err := binary.Write(buf, binary.LittleEndian, []byte(message)); err != nil {
// return err
//}
if _, err := buf.Write([]byte(message)); err != nil {
return err
}
//4.利用io.Write发送数据 从 buf.Bytes()写入底层的数据流
if _, err := enc.w.Write(buf.Bytes()); err != nil {
return err
}
return nil
}
//定义解码器(接收端)
type Decoder struct {
r io.Reader
}
//创建decoder
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{
r: r,
}
}
//从Reader中读取内容,解码
//binary(int32(13))[]byte("package data.")
func (dec *Decoder) Decode(message *string) error {
//1.读取前四个字节,读取heder
header := make([]byte, 4)
hn, err := dec.r.Read(header)
if err != nil {
return err
}
if hn != 4 {
return errors.New("header is not enough!")
}
//2.将前四个字节转化为int32类型,确定了body长度
var l int32
//headerBuf := bytes.NewBuffer(header) //为什么要转化为buffer?因为binary.Read()第一个参数需要 io.Reader类型
newReader := bytes.NewReader(header)
if err := binary.Read(newReader, binary.LittleEndian, &l); err != nil {
return err
}
//3.读取body
body := make([]byte, l)
bn, err := dec.r.Read(body)
if err != nil {
return err
}
if bn != int(l) {
return errors.New("body is not enough")
}
//4.设置message
*message = string(body)
return nil
}
- 客户端
func TcpClientCoder() {
address := "127.0.0.1:5678"
//A.建立连接
conn, err := net.DialTimeout(tcp, address, time.Second)
if err != nil {
log.Println(err)
return
}
//保证关闭
defer conn.Close()
log.Printf("connection is establish,client addr is %s\n", conn.LocalAddr())
//B.从服务端接受数据
for {
decoder := NewDecoder(conn)
var msg string
err := decoder.Decode(&msg)
if err != nil {
log.Println(err)
break
}
log.Println("receive data: ", msg)
}
}
- 服务端
func TcpServerCoder() {
address := ":5678" //any ip or version
//A.基于某个地址进行监听
listen, err := net.Listen(tcp, address) //端口省略 随机端口 address := "127.0.0.1:"
if err != nil {
log.Fatalln(err)
}
//关闭监听
defer listen.Close()
log.Printf("%s server is listening on %s\n", tcp, listen.Addr())
//B.接受连接请求
//循环接受
for {
//阻塞接受
conn, err := listen.Accept()
if err != nil {
log.Println(err)
}
go HandleConnCoder(conn)
}
}
func HandleConnCoder(conn net.Conn) {
//处理连接,读写
//日志连接的远程地址(client addr)
log.Printf("aceept from %s\n", conn.RemoteAddr())
//A.关闭连接
defer func() {
log.Println("current connection be closed!")
conn.Close()
}()
// 连续发送数据
//data := "package data."
data := []string{
"package data.",
"package.",
"package data data",
"pack",
}
for i := 0; i < 50; i++ {
//创建编码器
encoder := NewEncoder(conn)
if err := encoder.Encode(data[rand.Intn(len(data))]); err != nil {
log.Println(err)
}
}
}
6 Tcp专用方法
- 服务端
//TCP特定方法
func TcpServerSpecial() {
//1. 建立监听
// 获取本地地址(监听地址)
laddr, err := net.ResolveTCPAddr("tcp", ":5678")
if err != nil {
log.Fatalln(err)
}
tcpListener, err := net.ListenTCP("tcp", laddr)
if err != nil {
log.Fatalln(err)
}
defer tcpListener.Close()
//2. 接收连接
for {
tcpConn, err := tcpListener.AcceptTCP()
if err != nil {
log.Println(err)
continue
}
//3. 处理连接
go handleConnSpecial(tcpConn)
}
}
func handleConnSpecial(conn *net.TCPConn) {
//defer conn.Close()
log.Printf("accept from %s\n", conn.RemoteAddr())
//设置连接属性
conn.SetKeepAlive(true)
//写数据
data := "message data."
wn, err := conn.Write([]byte(data))
if err != nil {
log.Println(err)
return
}
log.Println("data len is ", wn)
}
- 客户端
//TCP特定方法
func TcpClientSpecial() {
//1.建立连接
raddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:5678")
if err != nil {
log.Fatalln(err)
}
tcpCoon, err := net.DialTCP("tcp", nil, raddr) //第二个参数 localaddr一般为nil
if err != nil {
log.Fatalln(err)
}
//关闭连接
defer tcpCoon.Close()
log.Println("connection is establish,client addr is ", tcpCoon.LocalAddr())
//2.读数据
buf := make([]byte, 1024)
for {
readn, err := tcpCoon.Read(buf)
if err != nil {
log.Println(err)
return
}
fmt.Println("读取内容为:", string(buf[:readn]), "长度为:", readn)
}
}
6.1 tcp连接属性设置
7 UDP程序设计
7.1 编写udp客户端服务端
- 服务端
func UDPServerBasic() {
//1.解析地址
udpAddr, err := net.ResolveUDPAddr("udp", ":9876")
if err != nil {
log.Fatalln(err)
}
//2.监听地址
udpConn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("%s server is listening on %s\n", "udp", udpConn.LocalAddr().String())
defer udpConn.Close()
//3.读
buf := make([]byte, 1024)
rn, raddr, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Fatalln(err)
}
log.Printf("receive %s from %s", string(buf[:rn]), raddr.String())
//4.写
data := []byte("received:" + string(buf[:rn]))
wn, err := udpConn.WriteToUDP(data, raddr) //没有建立连接所以用 WriteToUDP,使用这个是因为无目标地址
if err != nil {
log.Fatalln(err)
}
log.Printf("send %s(%d) to %s ", string(data), wn, raddr.String())
}
- 客户端
func UDPClientBasic() {
//1.建立连接
raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")
if err != nil {
log.Fatalln(raddr)
}
udpConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatalln(err)
}
//2.写
data := []byte("Go UDP program")
wn, err := udpConn.Write(data) //14行已经建立好连接 所以用write(方法)
if err != nil {
log.Fatalln(err)
}
log.Printf("send %s(%d) to %s ", string(data), wn, raddr.String())
//3.读
buf := make([]byte, 1024)
rn, raddr, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Fatalln(err)
}
log.Printf("receive %s from %s", string(buf[:rn]), raddr.String())
}
7.2 已连接和未连接的udp连接
- 读操作有兼容性,但还是推荐按规则使用
udpConn, err := net.ListenUDP("udp", udpAddr)
wn, err := udpConn.WriteToUDP(data, raddr) //没有建立连接所以用 WriteToUDP,使用这个是因为无目标地址
以上不会报错
但使用udpConn.Write()会报错
udpConn, err := net.DialUDP("udp", nil, raddr)
wn, err := udpConn.Write(data) //14行已经建立好连接 所以用write(方法)
以上不会报错
但是用udpConn.WriteToUDP()会报错
7.3 对等的服务端和客户端
- 服务端
//udp服务端Peer
func UDPServerPeer() {
//1.解析地址
udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876") //需要拿到确切地址
if err != nil {
log.Fatalln(err)
}
//2.监听地址
udpConn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("%s server is listening on %s\n", "udp", udpConn.LocalAddr().String())
defer udpConn.Close()
//远程地址
raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:6789")
if err != nil {
log.Fatalln(err)
}
//3.读
buf := make([]byte, 1024)
rn, raddr, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Fatalln(err)
}
log.Printf("receive %s from %s", string(buf[:rn]), raddr.String())
//4.写
data := []byte("received:" + string(buf[:rn]))
wn, err := udpConn.WriteToUDP(data, raddr) //没有建立连接所以用 WriteToUDP,使用这个是因为无目标地址
if err != nil {
log.Fatalln(err)
}
log.Printf("send %s(%d) to %s ", string(data), wn, raddr.String())
}
- 客户端
//udp客户端Peer
func UDPClientPeer() {
//1.解析地址
udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:6789") //需要拿到确切地址,不能和服务端地址一致
if err != nil {
log.Fatalln(err)
}
//2.监听地址
udpConn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("%s server is listening on %s\n", "udp", udpConn.LocalAddr().String())
defer udpConn.Close()
//远程地址
raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")
if err != nil {
log.Fatalln(err)
}
//2.写
data := []byte("Go UDP program")
wn, err := udpConn.WriteToUDP(data, raddr)
if err != nil {
log.Fatalln(err)
}
log.Printf("send %s(%d) to %s ", string(data), wn, raddr.String())
//3.读
buf := make([]byte, 1024)
rn, raddr, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Fatalln(err)
}
log.Printf("receive %s from %s", string(buf[:rn]), raddr.String())
}
7.4 多播编程
-接收端
//多播接收端
func UDPReceiverMulticast() {
//1.多播监听地址
address := "224.1.1.2:6789"
gAddr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
log.Fatalln(err)
}
//2.多播监听
udpConn, err := net.ListenMulticastUDP("udp", nil, gAddr)
if err != nil {
log.Fatalln(err)
}
//3. 接收数据
//4. 循环接收
buf := make([]byte, 1024)
for {
rn, addr, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Println(err)
}
log.Printf("received \"%s\" from %s\n", string(buf[:rn]), addr.String())
}
}
- 发送端
//多播发送端
func UDPSenderMulticast() {
//1.建立UDP多播组链接
address := "224.1.1.2:6789"
rAddr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
log.Fatalln(err)
}
udpConn, err := net.DialUDP("udp", nil, rAddr)
if err != nil {
log.Fatalln(err)
}
//2.发送内容
//循环发送
for {
data := fmt.Sprintf("[%s]:%s", time.Now().Format("03:04:05.000"), "hello")
wn, err := udpConn.Write([]byte(data))
if err != nil {
fmt.Println(err)
}
log.Printf("send \"%s\"(%d) to %s\n", string(data), wn, rAddr.String())
time.Sleep(time.Second)
}
}
7.5 广播编程
- 服务端
//广播接收端
func UDPReceiverBroadcast() {
//1.广播监听地址
laddr, err := net.ResolveUDPAddr("udp", ":6789")
if err != nil {
log.Fatalln(err)
}
//2.广播监听
udpConn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatalln(err)
}
//3.接收数据
//4.处理数据
//4. 循环接收
buf := make([]byte, 1024)
for {
rn, addr, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Println(err)
}
log.Printf("received \"%s\" from %s\n", string(buf[:rn]), addr.String())
}
}
- 客户端
//广播发送端
func UDPSenderBroadcast() {
//1.监听地址
//2.建立连接
laddr, err := net.ResolveUDPAddr("udp", ":9876")
if err != nil {
log.Fatalln(err)
}
udpConn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatalln(err)
}
//3.发送数据
rAddress := "127.0.1.255:6789" //192.168.50.255
raddr, err := net.ResolveUDPAddr("udp", rAddress)
if err != nil {
log.Fatalln(err)
}
//循环发送
for {
data := fmt.Sprintf("[%s]:%s", time.Now().Format("03:04:05.000"), "hello")
wn, err := udpConn.WriteToUDP([]byte(data), raddr)
if err != nil {
fmt.Println(err)
}
log.Printf("send \"%s\"(%d) to %s\n", string(data), wn, raddr.String())
time.Sleep(time.Second)
}
}
7.6文件传输
- 接收端
//UDP文件传输
func UDPFileServer() {
//1.建立udp连接
lAddress := ":5678"
laddr, err := net.ResolveUDPAddr("udp", lAddress)
if err != nil {
log.Fatalln(err)
}
udpConn, err := net.ListenUDP("udp", laddr)
if err != nil {
log.Fatalln(udpConn)
}
defer udpConn.Close()
fmt.Printf("%s server is listening on %s\n", "udp", udpConn.LocalAddr().String())
//2.接收文件名并确认
buf := make([]byte, 4*1024)
rn, raddr, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Fatalln(err)
}
fileName := string(buf[:rn])
if _, err := udpConn.WriteToUDP([]byte("filename ok"), raddr); err != nil {
log.Fatalln(err)
}
//3.接收文件内容,并写入文件
//打开(创建)文件
file, err := os.Create(fileName)
if err != nil {
log.Fatalln(err)
}
defer file.Close()
//网络读取
i := 0
for {
//一次读取
rn, _, err := udpConn.ReadFromUDP(buf)
if err != nil {
log.Fatalln(err)
}
if _, err = file.Write(buf[:rn]); err != nil {
log.Fatalln(err)
}
i++
log.Println("file write some content!:", i)
}
fmt.Println("接收完毕")
}
- 发送端
//文件传输(上传)
func UDPFileClient() {
//1.获取文件信息
filename := "./data/无畏.mp3"
//打开文件
file, err := os.Open(filename)
if err != nil {
log.Fatalln(err)
}
//关闭文件
defer file.Close()
//获取文件信息
fileInfo, err := file.Stat()
if err != nil {
log.Fatalln(err)
}
log.Println("send file size is ", fileInfo.Size())
//2.连接服务器
raddress := "127.0.0.1:5678" //192.168.50.131
raddr, err := net.ResolveUDPAddr("udp", raddress)
if err != nil {
log.Fatalln(err)
}
udpConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatalln(err)
}
//defer udpConn.Close()
//3.发送文件名
if _, err := udpConn.Write([]byte(fileInfo.Name())); err != nil {
log.Fatalln(err)
}
//4.服务端确认
buf := make([]byte, 1024*4)
rn, err := udpConn.Read(buf)
if err != nil {
log.Fatalln(err)
}
//判断是否为文件名正确接收响应
if "filename ok" != string(buf[:rn]) {
log.Fatalln("接收文件名不对!")
}
//5.发送文件内容
//读取文件内容,利用连接发送到服务端
i := 0
for {
//读取文件内容
rn, err := file.Read(buf)
if err != nil {
if err == io.EOF { // io.EOF错误表示文件读取完毕
break
}
log.Fatalln(err)
}
//发送到服务端
if _, err := udpConn.Write(buf[:rn]); err != nil {
log.Fatalln(err)
}
i++
}
log.Println(i)
//文件发送完成
log.Println("file send complete.")
//等待的测试
}
8 网络轮询器
8.1阻塞io模型
- 网络io
//网络IO(系统调用syscall的IO)阻塞
func BIONet() {
addr := "127.0.0.1:5678"
wg := sync.WaitGroup{}
//1.模拟读,体会读的阻塞状态
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
conn, _ := net.Dial("tcp", addr)
defer conn.Close()
buf := make([]byte, 1024)
//注意:两次时间的间隔
log.Println("start read.: ", time.Now().Format("03:04:05.000"))
n, _ := conn.Read(buf)
log.Println("content", string(buf[:n]), time.Now().Format("03:04:05.000"))
}(&wg)
//2.模拟写
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
listen, _ := net.Listen("tcp", addr)
defer listen.Close()
for {
conn, _ := listen.Accept()
go func(conn net.Conn) {
defer conn.Close()
log.Println("connceted!")
//阻塞时常
time.Sleep(3 * time.Second)
conn.Write([]byte("BLOCKING I/O"))
}(conn)
}
}(&wg)
wg.Wait()
}
- channel阻塞
//Channel(Go自管理的IO)的阻塞
func BIOChannel() {
// 0初始化数据
wg := sync.WaitGroup{}
// IO channel
ch := make(chan struct{})
//1.模拟读,体会读的阻塞状态
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
//注意:两次时间的间隔
log.Println("start read.: ", time.Now().Format("03:04:05.000"))
content := <-ch //IO read receive
log.Println("content", content, time.Now().Format("03:04:05.000"))
}(&wg)
//2.模拟写
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
//阻塞时长
time.Sleep(time.Second)
ch <- struct{}{} //Write send
}(&wg)
wg.Wait()
}
8.2 非阻塞
- conn
//网络IO(系统调用syscall的IO)非阻塞
func NIONet() {
addr := "127.0.0.1:5678"
wg := sync.WaitGroup{}
//1.模拟读,体会读的阻塞状态
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
conn, _ := net.Dial("tcp", addr)
defer conn.Close()
buf := make([]byte, 1024)
//注意:两次时间的间隔
log.Println("start read.: ", time.Now().Format("03:04:05.000"))
//设置截至时间
conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
n, _ := conn.Read(buf)
log.Println("content", string(buf[:n]), time.Now().Format("03:04:05.000"))
}(&wg)
//2.模拟写
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
listen, _ := net.Listen("tcp", addr)
defer listen.Close()
for {
conn, _ := listen.Accept()
go func(conn net.Conn) {
defer conn.Close()
log.Println("connceted!")
//阻塞时常
time.Sleep(3 * time.Second)
conn.Write([]byte("BLOCKING I/O"))
}(conn)
}
}(&wg)
wg.Wait()
}
- channel
//Channel(Go自管理的IO)的非阻塞
func NIOChannel() {
// 0初始化数据
wg := sync.WaitGroup{}
// IO channel
ch := make(chan struct{ id int })
//1.模拟读,体会读的阻塞状态
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
//注意:两次时间的间隔
log.Println("start read.: ", time.Now().Format("03:04:05.000"))
content := struct {
id int
}{}
for {
time.Sleep(time.Millisecond * 500)
select {
case s := <-ch:
log.Println("content", s.id, time.Now().Format("03:04:05.000"))
default:
log.Println("no content", content, time.Now().Format("03:04:05.000"))
}
}
}(&wg)
//2.模拟写
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
//阻塞时长
time.Sleep(time.Second)
ch <- struct{ id int }{id: 42} //Write send
}(&wg)
wg.Wait()
}
- channel conn
//网络IO(系统调用syscall的IO)非阻塞
func NIONetChannel() {
addr := "127.0.0.1:5678"
wg := sync.WaitGroup{}
//1.模拟读,体会读的阻塞状态
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
conn, _ := net.Dial("tcp", addr)
defer conn.Close()
//注意:两次时间的间隔
log.Println("start read.: ", time.Now().Format("03:04:05.000"))
//** 独立goroutine完成Read操作,将结果send到channel中
wgwg := sync.WaitGroup{}
chRead := make(chan []byte)
buf := make([]byte, 1024)
wgwg.Add(1)
go func() {
defer wgwg.Done()
n, _ := conn.Read(buf)
chRead <- buf[:n]
}()
time.Sleep(time.Millisecond * 100) //是为了让上面的协程执行 将conn读到的数据放入至chRead管道
//** select+default实现非阻塞操作
data := []byte{}
select {
case data = <-chRead:
default:
}
log.Println("content:", string(data), time.Now().Format("03:04:05.000"))
wgwg.Wait()
}(&wg)
//2.模拟写
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
listen, _ := net.Listen("tcp", addr)
defer listen.Close()
conn, _ := listen.Accept()
go func(conn net.Conn) {
defer conn.Close()
log.Println("connceted!")
//阻塞时常
//time.Sleep(3 * time.Second)
conn.Write([]byte("BLOCKING I/O"))
}(conn)
}(&wg)
wg.Wait()
}
8.3 信号驱动和异步io模型
8.4 多路复用io模型