网络编程

网络通信——tcp/ip协议

socket编程

Socket是应用层与TCP/IP协议族通信的中间软件抽象层。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket后面,对用户来说只需要调用Socket规定的相关函数,让Socket去组织符合指定的协议数据然后进行通信。

图解

### tcp协议

TCP/IP(Transmission Control Protocol/Internet Protocol) 即传输控制协议/网间协议,是一种面向连接(连接导向)的、可靠的、基于字节流的传输层(Transport layer)通信协议,因为是面向连接的协议,数据像水流一样传输,会存在黏包问题。

服务端

/**
 * Createby GoLand
 * User xzw [email protected]
 * Date 2021/8/8
 * Time 上午10:27
 */

package main

import (
   bufio
   fmt
   io
   log
   net

   imooc.com/ccmouse/learngo/net/protocol
)

func main() {
   listen, err := net.Listen(tcp, 127.0.0.1:8080) // 监听端口
   if err != nil {
      log.Fatal(listen failed, err:, err)
   }
   for {
      conn, err := listen.Accept() // 建立连接
      if err != nil {
         log.Println(accept failed, err:, err)
         continue
      }
      go process(conn) // 启动一个 goroutine 处理连接
   }
}

func process(conn net.Conn) {
   defer conn.Close()
   for {
      reader := bufio.NewReader(conn)

      msg, err := protocol.Decode(reader)
      if err == io.EOF {
         return
      }
      if err != nil {
         log.Println(decode msg failed, err:, err)
         return
      }
      fmt.Println(收到client发来的数据:, msg)
      data, err := protocol.Encode(msg)
      if err != nil {
         log.Println(encode msg failed, err:, err)
         return
      }
      conn.Write(data)
   }
}

客户端

/**
 * Createby GoLand
 * User xzw [email protected]
 * Date 2021/8/8
 * Time 上午10:35
 */

package main

import (
   bufio
   fmt
   io
   log
   net
   os
   strings

   imooc.com/ccmouse/learngo/net/protocol
)

func main() {
   conn, err := net.Dial(tcp, 127.0.0.1:8080)
   if err != nil {
      log.Fatal(err:, err)
   }
   defer conn.Close()
   inputReader := bufio.NewReader(os.Stdin)
   for {
      input, _ := inputReader.ReadString('\n') // 读取用户输入
      inputInfo := strings.Trim(input, \r\n)
      if strings.ToUpper(inputInfo) == Q { // 如果输入q就推出
         return
      }
      data, err := protocol.Encode(inputInfo)
      if err != nil {
         panic(err)
      }
      _, err = conn.Write(data) // 发送数据
      if err != nil {
         return
      }
      reader := bufio.NewReader(conn)
      msg, err := protocol.Decode(reader)
      if err == io.EOF {
         return
      }
      if err != nil {
         log.Println(decode msg failed, err:, err)
         return
      }
      fmt.Println(msg)
   }
}

粘包拆包

/**
 * Createby GoLand
 * User xzw [email protected]
 * Date 2021/8/8
 * Time 上午10:48
 */

package protocol

import (
   bufio
   bytes
   encoding/binary
)

// 将消息编码
func Encode(message string) ([]byte, error) {
   // 读取消息的长度,转换成int32类型(占4个字节)
   var length = int32(len(message))
   var pkg = new(bytes.Buffer)
   // 写入消息头
   err := binary.Write(pkg, binary.LittleEndian, length) // binary.LittleEndian 小端序,低地址位存放低位字节
   if err != nil {
      return nil, err
   }
   // 写入消息实体
   err = binary.Write(pkg, binary.LittleEndian, []byte(message))
   if err != nil {
      return nil, err
   }
   return pkg.Bytes(), nil
}

// 解码消息
func Decode(reader *bufio.Reader) (string, error) {
   // 读取消息的长度
   lengthByte, _ := reader.Peek(4) // 读取前 4 个字节的数据
   lengthBuff := bytes.NewBuffer(lengthByte)
   var length int32
   err := binary.Read(lengthBuff, binary.LittleEndian, &length)
   if err != nil {
      return , err
   }
   // Buffered返回缓冲中现有的可读取的字节数
   if int32(reader.Buffered()) < length+4 {
      return , err
   }
   // 读取真正的消息
   pack := make([]byte, int(4+length))
   _, err = reader.Read(pack)
   if err != nil {
      return , err
   }
   return string(pack[4:]), nil
}

udp协议

用户数据报协议(User Datagram Protocol,缩写为UDP),又称用户数据报文协议,是一个简单的面向数据报(package-oriented)的传输层协议。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以UDP有时候也被认为是不可靠的数据报协议)。UDP在IP数据报的头部仅仅加入了复用和数据校验。

服务端

/**
 * Createby GoLand
 * User xzw [email protected]
 * Date 2021/8/8
 * Time 上午11:36
 */

package main

import (
   log
   net
)

func main() {
   listen, err := net.ListenUDP(udp, &net.UDPAddr{
      IP:   net.IPv4(0, 0, 0, 0),
      Port: 30000,
   })
   if err != nil {
      log.Println(listen failed, err:, err)
      return
   }
   defer listen.Close()
   for {
      var data [1024]byte
      n, addr, err := listen.ReadFromUDP(data[:]) // 接收数据
      if err != nil {
         log.Println(read udp failed, err:, err)
         continue
      }
      log.Printf(data:%v addr:%v count:%v\n, string(data[:n]), addr, n)
      _, err = listen.WriteToUDP(data[:n], addr) // 发送数据
      if err != nil {
         log.Println(write to udp failed, err:, err)
         continue
      }
   }
}

客户端

/**
 * Createby GoLand
 * User xzw [email protected]
 * Date 2021/8/8
 * Time 上午11:43
 */

package main

import (
   fmt
   log
   net
)

func main() {
   socket, err := net.DialUDP(udp, nil, &net.UDPAddr{
      IP:   net.IPv4(0, 0, 0, 0),
      Port: 30000,
   })
   if err != nil {
      log.Println(连接服务器失败,err:, err)
      return
   }
   defer socket.Close()
   sendData := []byte(Hello server)
   _, err = socket.Write(sendData) // 发送数据
   if err != nil {
      log.Println(发送数据失败,err:, err)
      return
   }
   data := make([]byte, 4096)
   n, remoteAddr, err := socket.ReadFromUDP(data) // 接收数据
   if err != nil {
      log.Println(接收数据失败,err:, err)
      return
   }
   fmt.Printf(recv:%v addr:%v count:%v\n, string(data[:n]), remoteAddr, n)
}

最后更新于