网络编程基本介绍

Golang 的主要设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分。

网络编程有两种

  1. TCP socket 编程,是网络编程的主流。之所以叫 Tcp socket 编程,是因为底层是基于 TCP/IP 协议的. 比如: QQ 聊天
  2. B/S 结构的 http 编程,我们使用浏览器去访问服务器时,使用的就是 http 协议,而 http 底层依旧是用 tcp socket 实现的。 比如: 京东商城

    协议(tcp/ip)

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

    OSI 与 Tcp/ip 参考模型 (推荐 tcp/ip 协议 3 卷)

    TCP编程 - 图1
    TCP编程 - 图2

    ip 地址

    概述:每个 internet 上的主机和路由器都有一个 ip 地址,它包括网络号和主机号,ip 地址有 ipv4(32位)或者 ipv6(128 位). 可以通过ipconfig 来查看
    image.png

    端口(port)-介绍

    我们这里所指的端口不是指物理意义上的端口,而是特指 TCP/IP 协议中的端口,是逻辑意义上的端口.
    如果把 IP 地址比作一间房子,端口就是出入这间房子的门。真正的房子只有几个门,但是一个 IP 地址的端口 可以有 65536(即:256×256)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从 0 到 65535(256×256-1)
    TCP编程 - 图4

    端口(port)-分类

  • 0 号是保留端口.
  • 1-1024 是固定端口(程序员不要使用) 又叫有名端口,即被某些程序固定使用,一般程序员不使用.
    • 22: SSH 远程登录协议
    • 23: telnet 使用
    • 21: ftp 使用
    • 25: smtp 服务使用
    • 80: iis 使用
    • 7: echo 服务
  • 1025-65535 是动态端口这些端口,程序员可以使用.

    端口(port)-使用注意

  1. 在计算机(尤其是做服务器)要尽可能的少开端口
  2. 一个端口只能被一个程序监听
  3. 如果使用 netstat –an 可以查看本机有哪些端口在监听
  4. 可以使用 netstat –anb来查看监听端口的 pid,再结合任务管理器关闭不安全的端口

    tcp socket 编程的快速入门

    服务端的处理流程

  5. 监听端口 8888

  6. 接收客户端的 tcp 链接,建立客户端和服务器端的链接.
  7. 创建 goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)

    客户端的处理流程

  8. 建立与服务端的链接

  9. 发送请求数据[终端],接收服务器端返回的结果数据
  10. 关闭链接

    简单的程序示意图

    TCP编程 - 图5

    代码的实现

    服务器端

    编写一个服务器端程序,在 8888 端口监听,可以和多个客户端创建链接链接成功后,客户端可以发送数据,服务器端接受数据,并显示在终端上. 先使用 telnet 来测试,然后编写客户端程序来测试

  1. package main
  2. import (
  3. "fmt"
  4. "net" //做网络socket开发时,net包含有我们需要所有的方法和函数
  5. _"io"
  6. )
  7. func process(conn net.Conn) {
  8. //这里我们循环的接收客户端发送的数据
  9. defer conn.Close() //关闭conn
  10. for {
  11. //创建一个新的切片
  12. buf := make([]byte, 1024)
  13. //conn.Read(buf)
  14. //1. 等待客户端通过conn发送信息
  15. //2. 如果客户端没有wrtie[发送],那么协程就阻塞在这里
  16. //fmt.Printf("服务器在等待客户端%s 发送信息\n", conn.RemoteAddr().String())
  17. n , err := conn.Read(buf) //从conn读取
  18. if err != nil {
  19. fmt.Printf("客户端退出 err=%v", err)
  20. return //!!!
  21. }
  22. //3. 显示客户端发送的内容到服务器的终端
  23. fmt.Print(string(buf[:n]))
  24. }
  25. }
  26. func main() {
  27. fmt.Println("服务器开始监听....")
  28. //net.Listen("tcp", "0.0.0.0:8888")
  29. //1. tcp 表示使用网络协议是tcp
  30. //2. 0.0.0.0:8888 表示在本地监听 8888端口
  31. listen, err := net.Listen("tcp", "0.0.0.0:8888")
  32. if err != nil {
  33. fmt.Println("listen err=", err)
  34. return
  35. }
  36. defer listen.Close() //延时关闭listen
  37. //循环等待客户端来链接我
  38. for {
  39. //等待客户端链接
  40. fmt.Println("等待客户端来链接....")
  41. conn, err := listen.Accept()
  42. if err != nil {
  43. fmt.Println("Accept() err=", err)
  44. } else {
  45. fmt.Printf("Accept() suc con=%v 客户端ip=%v\n", conn, conn.RemoteAddr().String())
  46. }
  47. //这里准备其一个协程,为客户端服务
  48. go process(conn)
  49. }
  50. //fmt.Printf("listen suc=%v\n", listen)
  51. }

image.png

客户端

编写一个客户端端程序,能链接到 服务器端的 8888 端口 客户端可以发送单行数据,然后就退出 能通过终端输入数据(输入一行发送一行), 并发送给服务器端 在终端输入 exit,表示退出程序.

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "bufio"
  6. "os"
  7. "strings"
  8. )
  9. func main() {
  10. conn, err := net.Dial("tcp", "127.0.0.1:8888")
  11. if err != nil {
  12. fmt.Println("client dial err=", err)
  13. return
  14. }
  15. //功能一:客户端可以发送单行数据,然后就退出
  16. reader := bufio.NewReader(os.Stdin) //os.Stdin 代表标准输入[终端]
  17. for {
  18. //从终端读取一行用户输入,并准备发送给服务器
  19. line, err := reader.ReadString('\n')
  20. if err != nil {
  21. fmt.Println("readString err=", err)
  22. }
  23. //如果用户输入的是 exit就退出
  24. line = strings.Trim(line, " \r\n")
  25. if line == "exit" {
  26. fmt.Println("客户端退出..")
  27. break
  28. }
  29. //再将line 发送给 服务器
  30. _, err = conn.Write([]byte(line + "\n"))
  31. if err != nil {
  32. fmt.Println("conn.Write err=", err)
  33. }
  34. }
  35. }