53. Socket服务三次握手的示例

作者: 厚土火焱 | 来源:发表于2017-09-15 18:35 被阅读182次

    什么是三次握手呢?这是服务器和客户端之间沟通的过程。
    首先,客户端对服务器发送了条信息。
    然后,服务端对客户端说,我收到了。
    最后,客户端对服务端说,好的,我知道你收到了。
    怎么样?这就是三次握手。哈哈!

    服务端,我们先声明本地要监听的地址和端口。

    netListent, err := net.Listen("tcp", "localhost:7373")
    

    有开始就有结束,当主函数运行结束时,需要释放资源。

    defer netListent.Close()
    

    在服务端做标记,表明现在开始等待客户端访问了。

    Log(time.Now().Format("2006-01-02 15:04:05.0000000"),"Waiting for client ...")
    

    监听是一个不停息的循环运行。所以使用 for{}

        for{
            conn, err := netListent.Accept()
            if err != nil{
                continue
            }
            //标记发生了一次连接
            Log(conn.RemoteAddr().String(), "tcp connect success")
            go handleConnection(conn)
        }
    

    每个从客户端请求的连接,都会在服务端生成一个 goroutine 协程去处理。
    go handleConnection(conn)
    服务端处理的过程是这样的。建立一个缓存,接收客户端信息。如果接收正确,就反馈给客户端说“我收到了”。如果客户端没有反应,就证明客户端“没有收到回执”。如果客户端有反馈,就说明“客户端收到回执”。
    最后,沟通结束,释放连接资源。defer conn.Close()

    //客户端连接处理
    func handleConnection(conn net.Conn) {
        buffer := make([]byte, 1024)
        for{
            //接收客户端信息
            msg, err := conn.Read(buffer)
            if err != nil{
                //接收错误,日志打印
                Log(conn.RemoteAddr().String(), "connection error: ", err)
                return
            }
            //接收正确,日志打印
            Log(conn.RemoteAddr().String(), "receive data: ", string(buffer[:msg]))
            //反馈给客户端
            bufferReturn := "我收到了"
            msgR, err2 := conn.Write([]byte(bufferReturn))
            //确认客户端未收到回执
            if err2 != nil{
                Log(conn.RemoteAddr().String(), "没有收到回执")
                return
            }
            //确认客户端收到回执
            msg, err = conn.Read(buffer)
            Log(conn.RemoteAddr().String(), "客户端收到回执", string(buffer[:msg]), "客户收到了", msgR, ";实际发送了", len(bufferReturn))
        }
        defer conn.Close()
    }
    

    其中应用到的日志记录和错误处理。

    //日志记录
    func Log(i ...interface{}) {
        fmt.Println(i...)
        return
    }
    //错误处理
    func CheckErr(err error) {
        if err != nil{
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
    

    完整的服务端代码示例

    /**
    * MySocketProtocolServer
    * @Author:  Jian Junbo
    * @Email:   junbojian@qq.com
    * @Create:  2017/9/15 9:07
    * Copyright (c) 2017 Jian Junbo All rights reserved.
    *
    * Description:  服务端 Socket 信息接收
    */
    package main
    
    import (
        "fmt"
        "net"
        "os"
        "time"
    )
    
    func main() {
        //监听服务声明实例化
        netListent, err := net.Listen("tcp", "localhost:7373")
        CheckErr(err)
        defer netListent.Close()
        //标记开始服务
        Log(time.Now().Format("2006-01-02 15:04:05.0000000"),"Waiting for client ...")
        //服务监听
        for{
            conn, err := netListent.Accept()
            if err != nil{
                continue
            }
            //标记发生了一次连接
            Log(conn.RemoteAddr().String(), "tcp connect success")
            go handleConnection(conn)
        }
    }
    //客户端连接处理
    func handleConnection(conn net.Conn) {
        buffer := make([]byte, 1024)
        for{
            //接收客户端信息
            msg, err := conn.Read(buffer)
            if err != nil{
                //接收错误,日志打印
                Log(conn.RemoteAddr().String(), "connection error: ", err)
                return
            }
            //接收正确,日志打印
            Log(conn.RemoteAddr().String(), "receive data: ", string(buffer[:msg]))
            //反馈给客户端
            bufferReturn := "我收到了"
            msgR, err2 := conn.Write([]byte(bufferReturn))
            //确认客户端未收到回执
            if err2 != nil{
                Log(conn.RemoteAddr().String(), "没有收到回执")
                return
            }
            //确认客户端收到回执
            msg, err = conn.Read(buffer)
            Log(conn.RemoteAddr().String(), "客户端收到回执", string(buffer[:msg]), "客户收到了", msgR, ";实际发送了", len(bufferReturn))
        }
        defer conn.Close()
    }
    //日志记录
    func Log(i ...interface{}) {
        fmt.Println(i...)
        return
    }
    //错误处理
    func CheckErr(err error) {
        if err != nil{
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
    
    

    客户端的任务是发送信息给服务端,等待服务端的反馈。收到服务端的反馈后,再通知服务端说“ok”,表示已经知道服务端收到信息了。
    先要确定获取服务端的地址和通讯端口。

    server := "localhost:7373"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
    

    建立服务器连接

    conn, err := net.DialTCP("tcp", nil, tcpAddr)
    

    连接成功后,日志打印表示一下

    Log("connection success")
    

    发送信息到服务器,是一个函数 sender(conn) 最终完成的。

    func sender(conn *net.TCPConn) {
        myage := int(time.Now().Year())
        myage -= 1973
        words := "{\"ID\":\"i\",\"Name\":\"Joel\",\"Age\":\""+strconv.Itoa(myage)+"\",\"Programming Language\":\"go\"}"
        //msgBack, err := conn.Write(protocol.Enpack([]byte(words)))
        msgBack, err := conn.Write([]byte(words))
        if err != nil{
            Log(conn.RemoteAddr().String(), "Fatal error: ", err)
            os.Exit(1)
        }
        buffer := make([]byte, 1024)
        msg, err := conn.Read(buffer)
        Log(conn.RemoteAddr().String(), "服务器反馈: ", string(buffer[:msg]), msgBack,";实际发送了", len(words))
        conn.Write([]byte("ok"))
    }
    

    这个函数中,我们准备好了要发送的信息 words。
    写入信息到连接中

    msgBack, err := conn.Write([]byte(words))
    

    接收服务器对信息的反馈

    msg, err := conn.Read(buffer)
    

    在告诉服务器,它的反馈收到了。

    conn.Write([]byte("ok"))
    

    完整的客户端代码示例

    /**
    * MySocketProtocolClient
    * @Author:  Jian Junbo
    * @Email:   junbojian@qq.com
    * @Create:  2017/9/15 10:44
    * Copyright (c) 2017 Jian Junbo All rights reserved.
    *
    * Description:  客户端 Socket
    */
    package main
    
    import (
        "net"
        "fmt"
        "os"
        "time"
    )
    
    func main() {
        //获取服务器地址和端口
        server := "localhost:7373"
        tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
        if err != nil{
            Log(os.Stderr, "Fatal error: ", err)
            os.Exit(1)
        }
        //建立服务器连接
        conn, err := net.DialTCP("tcp", nil, tcpAddr)
        if err != nil{
            Log(conn.RemoteAddr().String(), os.Stderr, "Fatal error: ", err)
            os.Exit(1)
        }
        Log("connection success")
        sender(conn)
        fmt.Println("send over")
    }
    
    func sender(conn *net.TCPConn) {
        myage := int(time.Now().Year())
        myage -= 1973
        words := "{\"ID\":\"i\",\"Name\":\"Joel\",\"Age\":\""+strconv.Itoa(myage)+"\",\"Programming Language\":\"go\"}"
        msgBack, err := conn.Write([]byte(words))
        if err != nil{
            Log(conn.RemoteAddr().String(), "Fatal error: ", err)
            os.Exit(1)
        }
        buffer := make([]byte, 1024)
        msg, err := conn.Read(buffer)
        Log(conn.RemoteAddr().String(), "服务器反馈: ", string(buffer[:msg]), msgBack,";实际发送了", len(words))
        conn.Write([]byte("ok"))
    }
    //日志打印
    func Log(v ...interface{}) {
        fmt.Println(v...)
    }
    
    

    服务端运行结果

    127.0.0.1:50756 tcp connect success
    127.0.0.1:50756 receive data:  {"ID":"i","Name":"Joel","Age":"44","Programming Language":"go"}
    127.0.0.1:50756 客户端收到回执 ok 客户收到了 12 ;实际发送了 12
    127.0.0.1:50756 connection error:  read tcp 127.0.0.1:7373->127.0.0.1:50756: wsarecv: An existing connection was forcibly closed by the remote host.
    

    客户端运行结果

    connection success
    127.0.0.1:7373 服务器反馈:  我收到了 63 ;实际发送了 63
    send over
    

    现在通过网络传递的信息,没有特别的加工,没有自定义的通讯协议。如果要增加这个,建立自己的通讯协议后(封装 Enpack、解析 Depack),在客户端进行封装,在服务端进行解析。
    在当前的代码中做修改。
    服务端

    Log(conn.RemoteAddr().String(), "receive data: ", string(buffer[:msg]))
    
    改成 ↓
    
    tmpBuffer := make([]byte, 1024)
    tmpBuffer = protocol.Depack(append(tmpBuffer,buffer[:msg]...))
    Log(conn.RemoteAddr().String(), "receive data: ", string(tmpBuffer))
    

    客户端

    msgBack, err := conn.Write([]byte(words))
    ...
    Log(conn.RemoteAddr().String(), "服务器反馈: ", string(buffer[:msg]), msgBack,";实际发送了", len(words))
    
    改成 ↓
    
    msgBack, err := conn.Write(protocol.Enpack([]byte(words)))
    ...
    Log(conn.RemoteAddr().String(), "服务器反馈: ", string(buffer[:msg]), msgBack,";实际发送了", len(protocol.Enpack([]byte(words))))
    

    由于添加了协议后,发送内容增加了头部的一些信息,所以实际发送信息的长度也变化了。
    协议protocol的代码示例

    /**
    * protocol
    * @Author:  Jian Junbo
    * @Email:   junbojian@qq.com
    * @Create:  2017/9/14 11:49
    * Copyright (c) 2017 Jian Junbo All rights reserved.
    *
    * Description:  通讯协议处理
    */
    package protocol
    
    import (
        "bytes"
        "encoding/binary"
    )
    
    const (
        ConstHeader = "Headers"
        ConstHeaderLength = 7
        ConstMLength = 4
    )
    
    //封包
    func Enpack(message []byte) []byte {
        return append(append([]byte(ConstHeader), IntToBytes(len(message))...), message...)
    }
    
    //解包
    func Depack(buffer []byte) []byte {
        length := len(buffer)
    
        var i int
        data := make([]byte, 32)
        for i = 0; i < length; i++ {
    
            if length < i + ConstHeaderLength + ConstMLength{
                break
            }
            if string(buffer[i:i+ConstHeaderLength]) == ConstHeader {
                messageLength := ByteToInt(buffer[i+ConstHeaderLength : i+ConstHeaderLength+ConstMLength])
                if length < i+ConstHeaderLength+ConstMLength+messageLength {
                    break
                }
                data = buffer[i+ConstHeaderLength+ConstMLength : i+ConstHeaderLength+ConstMLength+messageLength]
            }
        }
    
        if i == length {
            return make([]byte, 0)
        }
    
        return data
    }
    
    //字节转换成整形
    func ByteToInt(n []byte) int {
        bytesbuffer := bytes.NewBuffer(n)
        var x int32
        binary.Read(bytesbuffer, binary.BigEndian, &x)
    
        return int(x)
    }
    
    //整数转换成字节
    func IntToBytes(n int) []byte {
        x := int32(n)
        bytesBuffer := bytes.NewBuffer([]byte{})
        binary.Write(bytesBuffer, binary.BigEndian, x)
        return bytesBuffer.Bytes()
    }
    
    

    增加了解析功能的服务端代码示例

    /**
    * MySocketProtocolServer
    * @Author:  Jian Junbo
    * @Email:   junbojian@qq.com
    * @Create:  2017/9/15 9:07
    * Copyright (c) 2017 Jian Junbo All rights reserved.
    *
    * Description:  服务端 Socket 信息接收
    */
    package main
    
    import (
        "fmt"
        "net"
        "os"
        "time"
        "protocol"
    )
    
    func main() {
        //监听服务声明实例化
        netListent, err := net.Listen("tcp", "localhost:7373")
        CheckErr(err)
        defer netListent.Close()
        //标记开始服务
        Log(time.Now().Format("2006-01-02 15:04:05.0000000"),"Waiting for client ...")
        //服务监听
        for{
            conn, err := netListent.Accept()
            if err != nil{
                continue
            }
            //标记发生了一次连接
            Log(conn.RemoteAddr().String(), "tcp connect success")
            go handleConnection(conn)
        }
    }
    //客户端连接处理
    func handleConnection(conn net.Conn) {
        buffer := make([]byte, 2048)
        for{
            //接收客户端信息
            msg, err := conn.Read(buffer)
            if err != nil{
                //接收错误,日志打印
                Log(conn.RemoteAddr().String(), "connection error: ", err)
                return
            }
            //接收正确,日志打印
            tmpBuffer := make([]byte, 1024)
            tmpBuffer = protocol.Depack(append(tmpBuffer,buffer[:msg]...))
            Log(conn.RemoteAddr().String(), "receive data: ", string(tmpBuffer))
            //反馈给客户端
            bufferReturn := "我收到了"
            msgR, err2 := conn.Write([]byte(bufferReturn))
            //确认客户端未收到回执
            if err2 != nil{
                Log(conn.RemoteAddr().String(), "没有收到回执")
                return
            }
            //确认客户端收到回执
            msg, err = conn.Read(buffer)
            Log(conn.RemoteAddr().String(), "客户端收到回执", string(buffer[:msg]), "客户收到了", msgR, ";实际发送了", len(bufferReturn))
        }
        defer conn.Close()
    }
    //日志记录
    func Log(i ...interface{}) {
        fmt.Println(i...)
        return
    }
    //错误处理
    func CheckErr(err error) {
        if err != nil{
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
    
    

    增加了封装的客户端代码示例

    /**
    * MySocketProtocolClient
    * @Author:  Jian Junbo
    * @Email:   junbojian@qq.com
    * @Create:  2017/9/15 10:44
    * Copyright (c) 2017 Jian Junbo All rights reserved.
    *
    * Description:  客户端 Socket
    */
    package main
    
    import (
        "net"
        "fmt"
        "os"
        "time"
        "strconv"
        "protocol"
    )
    
    func main() {
        //获取服务器地址和端口
        server := "localhost:7373"
        tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
        if err != nil{
            Log(os.Stderr, "Fatal error: ", err)
            os.Exit(1)
        }
        //建立服务器连接
        conn, err := net.DialTCP("tcp", nil, tcpAddr)
        if err != nil{
            Log(conn.RemoteAddr().String(), os.Stderr, "Fatal error: ", err)
            os.Exit(1)
        }
        Log("connection success")
        sender(conn)
        fmt.Println("send over")
    }
    
    func sender(conn *net.TCPConn) {
        myage := int(time.Now().Year())
        myage -= 1973
        words := "{\"ID\":\"i\",\"Name\":\"Joel\",\"Age\":\""+strconv.Itoa(myage)+"\",\"Programming Language\":\"go\"}"
        msgBack, err := conn.Write(protocol.Enpack([]byte(words)))
        //msgBack, err := conn.Write([]byte(words))
        if err != nil{
            Log(conn.RemoteAddr().String(), "Fatal error: ", err)
            os.Exit(1)
        }
        buffer := make([]byte, 1024)
        msg, err := conn.Read(buffer)
        Log(conn.RemoteAddr().String(), "服务器反馈: ", string(buffer[:msg]), msgBack,";实际发送了", len(protocol.Enpack([]byte(words))))
        conn.Write([]byte("ok"))
    }
    //日志打印
    func Log(v ...interface{}) {
        fmt.Println(v...)
    }
    
    

    运行效果上只是客户端这里显示增加了几个字节

    connection success
    127.0.0.1:7373 服务器反馈:  我收到了 74 ;实际发送了 74
    send over
    

    相关文章

      网友评论

      本文标题:53. Socket服务三次握手的示例

      本文链接:https://www.haomeiwen.com/subject/hcissxtx.html