1.1.1. 一、协议之间的关系。

1、socket 封装了 TCP/IP协议。

2、HTTP 协议(HyperText Transfer Protocol,超文本传输协议) 封装了 socket。

3、如果我们要自定义一个协议,是和HTTP是一层的。相当于把HTTP替换为自己的协议。比如:使用protobuf定义了自己的协议。

1.1.2. 二、TCP/IP 协议。

1、TCP/IP 协议。

TCP/IP(Transmission Control Protocol/Internet Protocol 的简写),中文为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是 Internet 最基本的协议、Internet 国际互联网络的基础,简单地说,就是由网络层的 IP 协议和传输层的 TCP 协议组成的。

2、IP地址。

每个 internet 上的主机和路由器都有一个 ip 地址,它包括网络号和主机号,ip 地址有 ipv4(4字节,32 位)或者 ipv6(16字节,128 位),可以通过 ipconfig 来查看。

3、端口。

电脑的端口有2字节,16位。范围是从 0 到 65535(2的16次方-1)。

可以使用 netstat –an 可以查看本机有哪些端口在监听。使用 netstat –anb 来查看监听端口的 pid。

1.1.3. 三、tcp服务器、客户端通信的实现。

go语言网络编程是在net包下面。

1、服务器 server.go 代码:

实现效果:服务器监听8888端口,等待客户端连接。客户端连接之后,开启一个协程处理客户端连接。这时候服务器和客户端实际上是一个tcp长连接。客户端和服务器一直可以互相发信息通信。它们是双工的。

package main

import (
    "fmt"
    "net"
)

// 处理请求
func processTest(conn net.Conn)  {
    //关闭连接
    defer conn.Close()

    //循环接收客户端发送的数据
    for {
        buf := make([]byte,1024) //创建切片接收数据

        //fmt.Printf("等待客户端 %s 发送数据 \n",conn.RemoteAddr())
        n, err := conn.Read(buf) //阻塞等待接收客户端信息。
        if err != nil {
            fmt.Println("服务端Read 错误 err=",err)
            return
        }

        //显示服务端发送的内容,n表示实际读到的字节数
        fmt.Print("客户端的数据=",string(buf[:n]))
    }

}

func main() {
    fmt.Println("服务器开始监听。。。")
    //本地监听 8888 端口
    listener, err := net.Listen("tcp", "0.0.0.0:8888")

    if err != nil {
        fmt.Println("监听失败。。。",err)
        return
    }
    defer listener.Close() // 延时关闭

    for {
        fmt.Println("等待客户端连接。。。")
        conn, err := listener.Accept() //等待连接。
        if err != nil {
            fmt.Println("Accept() error=",err)
        }else {
            fmt.Printf("连接成功 conn=%v ,客户端的ip=%v \n",conn,conn.RemoteAddr())
        }

        //开一个协程,处理一个请求。
        go processTest(conn)

    }

}

关键代码说明:

1、监听tcp端口:listener, err := net.Listen("tcp", "0.0.0.0:8888")

2、关闭监听:defer listener.Close()

3、阻塞等待连接:conn, err := listener.Accept()

4、开一个协程,处理一个连接:go processTest(conn)

5、关闭连接:defer conn.Close()

6、阻塞等待接收客户端信息:n, err := conn.Read(buf)

2、客户端 client.go 代码。

实现效果:客户端循环读取控制台输入,向服务器发送读取的内容。如果控制台输入 exit,客户端就退出。

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strings"
)

func main() {
    //连接127.0.0.1:8888,返回连接Conn
    conn, err := net.Dial("tcp", "127.0.0.1:8888")
    if err != nil {
        fmt.Printf("连接错误 err=%v", err)
        return
    }

    //os.Stdin 是标准输入,就是终端控制台。从控制台读取数据。
    reader := bufio.NewReader(os.Stdin)

    // 循环读取控制台输入,向服务器发送
    for {

        str, err := reader.ReadString('\n') //阻塞等待输入
        if err != nil {
            fmt.Printf("从控制台读取数据错误 err=%v", err)
            return
        }
        //输入exit退出。
        if strings.Trim(str,"\r\n") == "exit" {
            fmt.Printf("客户端退出")
            return
        }

        // 把数据发送给服务器
        n, err := conn.Write([]byte(str))
        if err != nil {
            fmt.Printf("发送数据错误 err=%v", err)
            return
        }
        fmt.Printf("发送了 %d 字节数据。\n", n)
    }

}

关键代码说明:

1、连接127.0.0.1:8888,返回连接Conn。conn, err := net.Dial("tcp", "127.0.0.1:8888")

2、建立控制台读取数据的Reader:reader := bufio.NewReader(os.Stdin)

3、阻塞等待控制台输入:str, err := reader.ReadString('\n')

4、把数据发送给服务器:n, err := conn.Write([]byte(str))

3、运行效果。

  • 服务器控制台:
服务器开始监听。。。
等待客户端连接。。。
连接成功 conn=&{ {0xc00008c2c0} } ,客户端的ip=127.0.0.1:59028 
等待客户端连接。。。
客户端的数据=你好
客户端的数据=abc
服务端Read 错误 err= read tcp 127.0.0.1:8888->127.0.0.1:59028: wsarecv: An existing connection was forcibly closed by the remote host.
  • 客户端控制台:
你好
发送了 7 字节数据。
abc
发送了 4 字节数据。
exit
客户端退出

results matching ""

    No results matching ""