Go-网络通信

2019-12-28  本文已影响0人  骑蜗上高速

TCP
TCP通信:面向连接的,可靠的数据包传输。
协议:一组规则,要求使用协议双方必须遵守协议内容。
网络分层架构:
OSI七层模型结构:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
TCP/IP四层模型:数据链路层、网络层、传输层、应用层。
各层功能:
1、链路层:ARP
源mac-目标mac
ARP协议作用:借助IP获取mac地址
2、网络层:IP
缘IP-目标IP
IP协议的作用:在网络环境中 唯一标示一台主机
IP地址本质:2机制。点分十进制IP地址(string)
3、传输层:TCP/UDP
port-在一台计算机上唯一标示一个进程
4、应用层:ftp、http、自定义
对数据进行封装、解封装
数据通知过程:
封装:应用层-传输层-网络层-链路层 。没有经过封装,不能在网络环境中传递。
解封装:链路层-网络层-传输层-应用层
通信过程:
1、mac地址(不需要用户指定)(ARP协议)->mac
2、IP地址(需要用户指定)-确定主机
3、port端口号(需要用户指定)-确定程序
1、不能使用系统占用的端口。2、5000+端口。我们使用。3、65535为端口上线。
socket:套接字
网络通信过程中,socket一定是成对出现的。

TCP通信过程
三次握手:
1、主动发起请求端,发送SYN
2、被动建立连接 请求端,应答ACK同时发送SYN
3、主动发起请求端,发送ACK
标志TCP三次握手建立完成,-----server:accept()返回。-----client:Dial()返回

四次挥手:
1、主动关闭连接请求端,发送FIN
2、被动关闭连接请求端,应答ACK
标志,半关闭完成。---close()
3、主动关连接请求端,发送FIN
4、被动关闭连接请求端,应答ACK
标志,四次挥手建立完成。---close()
TCP状态转换图


image.png image.png

代码示例(服务端):

package main
import (
    "fmt"
    "net"
)
func main() {
    //1、指定服务器通信协议,ip地址,port。创建一个用于监听的socket
    listener, err := net.Listen("tcp","127.0.0.1:8000")
    if err != nil {
        fmt.Println("net.Listen Err",err)
        return
    }
    defer listener.Close()
    fmt.Println("服务器正在等待客户端连接...")

    //2、阻塞监听客户端连接请求,成功建立连接,返回用于通信的socket
    conn, err := listener.Accept()
    if err != nil {
        fmt.Println("listener.Accept() Err",err)
        return
    }
    defer conn.Close()
    fmt.Println("服务器与客户端成功建立连接!!!")

    //3、读取客户端发送的数据
    buf := make([]byte,4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Println("conn.Read(buf) Err",err)
        return
    }  
    conn.Write(buf[:n])//读多少,写多少
    //4、处理数据
    fmt.Println("服务器读到的数据,",string(buf[:n]))
}

执行结果:

服务器正在等待客户端连接...
服务器与客户端成功建立连接!!!
服务器读到的数据, hello world!

客户端:

package main

import (
    "fmt"
    "net"
)

func main(){
    //指定服务器IP + port创建通信套接字
    conn, err := net.Dial("tcp","127.0.0.1:8000")
    if err != nil {
        fmt.Println("net.Dial err",err)
        return
    }
    defer conn.Close()
    //主动写数据给服务器
    _, err = conn.Write([]byte("Hello World"))
    if err != nil {
        fmt.Println("conn.Write err",err)
        return
    }
    //接收服务器回发数据
    buf := make([]byte,4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Println("conn.Write err",err)
        return
    }
    fmt.Println("服务器回发",string(buf[:n]))
}

并发服务器(服务端):

package main

import (
    "fmt"
    "net"
    "strings"
)
func HaldlerConn(conn net.Conn)  {
    defer conn.Close()
    //获取客户端连接地址
    addr := conn.RemoteAddr()
    fmt.Println(addr,"客户端连接成功!!")

    buf := make([]byte,4096)
    //循环获取客户端发送的数据
    for {
        n, err := conn.Read(buf)
        if "exit\n" == string(buf[:n]) || "exit\r\n" == string(buf[:n]) {
            fmt.Println("服务器接收到客户端退出请求!!服务器退出")
        }
        if n == 0 {
            fmt.Println("服务器监测到客户端已经关闭,断开连接!!")
            return
        }
        if err != nil {
            fmt.Println("conn.Read Err",err)
            return
        }
        fmt.Printf("服务器获取客户端%s的数据%s",addr.String(),string(buf[:n]))
        //答谢转小写,回发给客户端
        upperStrings := strings.ToUpper(string(buf[:n]))
        _, err = conn.Write([]byte(upperStrings))
        if err != nil {
            fmt.Println("conn.Write Err",err)
            return
        }
    }
}
func main () {
    //指定服务器通信协议,ip地址,port。创建一个用于监听的socket
    listener, err := net.Listen("tcp","127.0.0.1:8000")
    if err != nil {
        fmt.Println("net.Listen Err",err)
        return
    }
    defer listener.Close()
    fmt.Println("服务器正在等待客户端连接...")
    for {
        conn, err := listener.Accept()
        if err != nil {fmt.Println("listener.Accept Err",err)
            return
        }
        go HaldlerConn(conn)
    }

}

执行结果:

~/Downloads/learngo/src/(master ✘)✖✹✚✭ ᐅ ./tcp            
服务器正在等待客户端连接...
127.0.0.1:62699 客户端连接成功!!
127.0.0.1:62700 客户端连接成功!!
服务器获取客户端127.0.0.1:62700的数据wef
服务器获取客户端127.0.0.1:62699的数据erwe
服务器获取客户端127.0.0.1:62699的数据hello
服务器获取客户端127.0.0.1:62700的数据world

~  ᐅ nc 127.0.0.1 8000               
erwe
ERWE
hello
HELLO
ᐅ nc 127.0.0.1 8000               
wef
WEF
world
WORLD

并发服务器(客户端):

package main

import (
    "fmt"
    "net"
    "os"
)

func main()  {

    //指定服务器IP + port创建通信套接字
    conn, err := net.Dial("tcp","127.0.0.1:8000")
    if err != nil {
        fmt.Println("net.Dial Err",err)
        return
    }
    defer conn.Close()

    //指定键盘输入
    go func() {
        str := make([]byte, 4096)
        for {
            n, err := os.Stdin.Read(str)
            if err != nil {
                fmt.Println("os.Stdin.Read Err",err)
                continue
            }
            n, err = conn.Write(str[:n])
            if err != nil {
                fmt.Println("os.Stdin.Read Err",err)
                return
            }
        }
    }()
    //回显服务器回发来的大写数据
    buf := make([]byte, 4096)
    for {
        n, err := conn.Read(buf)
        if n == 0 {//服务器端回显数据为0
            fmt.Println("检查到服务器关闭,客户端退出")
            return
        }
        if err != nil {
            fmt.Println("conn.Read Err",err)
            return
        }
        fmt.Println("客户端读到服务器回发",string(buf[:n]))
    }
}

UDP
UDP通信:无连接的,不可靠的报文传输。

UDP服务端示例代码:

package main

import (
    "fmt"
    "net"
    "time"
)

func main (){
    //组织一个udp地址结构,指定服务器的ip+port
    srvAddr, err := net.ResolveUDPAddr("udp","127.0.0.1:8002")
    if err != nil {
        fmt.Println("net.ResolveUDPAddr Err",err)
        return
    }
    fmt.Println("udp服务器地址结构创建完成!")
    //创建一个用户通信的socket
    udpConn, err := net.ListenUDP("udp",srvAddr)
    if err != nil {
        fmt.Println("net.ListenUDP Err",err)
        return
    }
    defer udpConn.Close()
    fmt.Println("udp服务器socket创建完成!")
    //读取客户端发送的数据
    buf := make([]byte, 4096)
    //返回三个只,分别是读取的字节数,客户端的IP地址,err
    n, clientAddr, err := udpConn.ReadFromUDP(buf)
    if err != nil {
        fmt.Println("ReadFromUDP Err",err)
        return
    }
    //处理数据
    fmt.Printf("服务器读到%v的数据:%s\n",clientAddr,string(buf[:n]))
    //提取系统当前时间
    nowTime := time.Now().String()
    //写回数据给客户端
    _, err = udpConn.WriteToUDP([]byte(nowTime),clientAddr)
    if err != nil {
        fmt.Println("WriteToUDP Err",err)
        return
    }
}

UDP客户端示例代码:

package main

import (
    "fmt"
    "net"
)

func main(){
    //指定服务器IP + port创建通信套接字
    conn, err := net.Dial("udp","127.0.0.1:8002")
    if err != nil {
        fmt.Println("net.Dial err",err)
        return
    }
    defer conn.Close()
    //主动写数据给服务器
    _, err = conn.Write([]byte("Hello World"))
    if err != nil {
        fmt.Println("conn.Write err",err)
        return
    }
    //接收服务器回发数据
    buf := make([]byte,4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Println("conn.Write err",err)
        return
    }
    fmt.Println("服务器回发",string(buf[:n]))
}

总结UDP和TCP差异:


image.png

使用场景:
TCP:对数据传输安全性、稳定性要求较高的场合,网络文件传输、下载、上传。
UDP:对数据实时传输要求较高的场景,视频直播、在线电话会议、游戏。

文件传输-发送端(客户端)
1、提示用户使用命令行参数输入文件名,接收文件名filepath(含有访问路径)
2、使用os.stat()获取文件属性。得到文件名filename(取出访问路径)
3、主动发起连接服务器请求,结束时关闭连接
4、发送文件名到接收端conn.write()
5、读取接收端回发的确认数据conn.read()
6、判断是否为“ok”,如果是,封装函数sendFile发送文件内容,传参filepath和conn
7、只读方式open文件,结束时close文件
8、循环读取本地文件,读到EOF,读取完毕
9、将读取的内容原封不动的conn.write给接收端(服务端)
文件传输示例:
发送端

package main

import (
    "fmt"
    "io"
    "net"
    "os"
    )

func main () {
    //获取命令行参数
    list := os.Args
    //获取文件路径
    filePath := list[1]
    fileInfo, err := os.Stat(filePath)
    if err != nil {
        fmt.Println(" os.Stat err",err)
        return
    }
    //获取文件名
         fileName := fileInfo.Name()

    //建立连接
    conn, err := net.Dial("tcp","127.0.0.1:8003")
    if err != nil {
        fmt.Println("net.Dial err",err)
        return
    }
    defer conn.Close()
    //发送文件名给接收端
    _, err = conn.Write([]byte(fileName))
        if err != nil {
        fmt.Println("conn.Read err",err)
        return
    }
    //接收服务器回发消息
    buf := make([]byte,4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Println("conn.Write err",err)
        return
    }
    if "ok" == string(buf[:n]) {
        sendFile(conn,filePath)
    }
}

func sendFile (conn net.Conn, filePath string) {
    //打开文件
    file, err := os.Open(filePath)
    if err != nil {
        fmt.Println("os.Open err",err)
        return
    }
    defer file.Close()
    //从文件中读取数据,写给接收端.读多少,写多少。
    buf := make([]byte, 4096)
    for {
        n, err := file.Read(buf)
        if err != nil {
            if err == io.EOF {
                fmt.Println("文件发送完毕!")
                return
            }else {
                fmt.Println("file.Read err",err)
            }
            return
        }  
        //写到网络中
        _, err = conn.Write(buf[:n])
        if err != nil {
            fmt.Println("conn.Write err",err)
            return
        }
    }
}

文件传输-接收端
1、创建监听listener,程序结束时关闭。
2、阻塞等待客户端连接conn,程序结束时关闭conn
3、读取客户端发送文件名,保存filename
4、回发“ok”
5、封装函数recvFile接收客户端发送的文件内容,穿参数filename和conn
6、按照文件名create文件,结束时close
7、循环read发送端网络温年内容,当读到0时,说明文件读取完毕
8、将读取的文件内容原封不动的write到创建的文件中
接收端:

package main

import (
    "fmt"
    "net"
    "os"
)

func main()  {
    //创建监听socket
    listener, err := net.Listen("tcp","127.0.0.1:8003")
    if err != nil {
        fmt.Println("net.Dial err",err)
        return
    }
    defer listener.Close()
    //阻塞等待发送端建立连接
    conn, err := listener.Accept()
    if err != nil {
        fmt.Println("listener.Accept err",err)
        return
    }
    defer conn.Close()
    //获取文件名,保存
    buf := make([]byte, 4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Println("conn.Read err",err)
        return
    }
    filename := string(buf[:n])
    //回写ok给发送端
    _, err = conn.Write([]byte("ok"))
    if err != nil {
        fmt.Println("conn.Write err",err)
        return
    }
    //接收文件
    recvFile(conn,filename)
}

func recvFile(conn net.Conn,filename string)  {
        //创建文件
    file, err := os.Create(filename)
    if err != nil {
        fmt.Println("os.Create err",err)
        return
    }
    defer file.Close()
    //从网络中读数据,写入文件
    buf := make([]byte, 4096)
    for {
        n, err := conn.Read(buf)
        if n == 0 {
            fmt.Printf("文件%s接收完毕!",filename)
            return
        }
        if err != nil {
            fmt.Println("conn.Read err",err)
            return
        }
        //写入文件
        _,err = file.Write(buf[:n])
        if err != nil {
            fmt.Println("file.Write err",err)
            return
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读