Go的网络编程

1.网络编程

1.1 网络协议

从应用的角度出发,协议可理解为“规则”,是数据传输和数据的规则,是通信双方都要遵守的规则。

协议存在的意义是为了让 双方为了更好的沟通

在双方之间被遵守的协议称为原始协议
当此协议被更多的人采用后,并不断的增加、改进、维护、完善。最终形成一个稳定的、完整的文件传输协议,被广泛应用与各种文件传输过程中。该协议就成为了一个标准协议。 btw,最早的ftp协议就是由此衍生而来的。

1.2 分层模型

1.2.1 网络分层架构

网络模型采用分层的方式的是 为了减少协议设计的复杂性
每一层利用下一层提供的服务来为上一层提供服务,本层服务的实现细节对上层屏蔽。

网络分层结构.png

TCP/IP
链路层:主要用于处理mac地址
网络层:主要处理IP相关的信息
传输层:主要处理端口相关的信息
应用层:主要处理应用层协议

1.2.2 层与协议

网络的每一层,都定义了很多协议。这些协议发总称,叫做“TCP/IP协议”。TCP/IP协议是一个大家族,不仅仅只有TCP和IP协议,它还包括其他协议,如下图:

TCPIP协议.png

TCP/IP 协议族:

  • ARP: Address Resolution Protocol, 正向地址解析协议。通过已知的IP,寻找对应的主机MAC地址。

  • RARP:是反响的地址转换协议,通过MAC地址确定IP地址。

  • IP:Internet Protocol,是因特网互联协议

  • ICMP:Internet Control Message Protocol,是Internet控制报文协议,它是TCP/IP协议的一个子协议,用于在IP主机、路由器之间传递控制消息。
    eg: ping 127.0.0.1 就是用到了ICMP协议

  • IGMP:Internet Group Management Protocol,是Internet组管理协议,它是因特网协议家族中的一个组播协议,该协议运行在主机和组播路由器之间。

  • TCP:Transmission Control Protocol,传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。主要用于 文件传输

  • UDP:User Datagram Protocol,是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。主要用于 聊天

  • FTP:

  • Telnet

  • TFTP

  • NFS

1.2.3 每层的功能
每层协议的功能.png
  • 链路层

以太网规定,连入网络的所有设备,都必须具有“网卡”接口。

  • 数据包必须是从一块网卡,传送到另一块网卡的。通过网卡能够使不同的计算机之间连接,从而完成数据通信等功能。
  • 网卡的地址----MAC地址,就是数据包的物理发送地址和物理接受地址。
  • 网卡 就是有标识的 ==》》网卡地址 《《==》》 物理地址 MAC地址
  • 网络层
    网络层.png

    链路层主要处理 mac 地址,网络层主要处理 ip 地址。
    mac:物理地址 (理论上是唯一的) ip:逻辑地址 (网段地址,可以改变的)
  • 如果只有一层链路层的话,通信仅通过mac地址的话,最终会导致广播风暴。
  • 因此,会加上网络层,引入 ip 地址,将 mac 地址分为不同的组,每一组就是一个网段,也就是 ip 地址,避免了广播风暴问题。

*ARP:通过 ip 找 mac (ARP是在链路层)

  • 传输层

传输层:端口号,目的端口号
端口号, 是网络程序的一个编号
传输层是进程到进程的

  • 应用层
网络通信条件:

1)网卡,mac地址(不需要用户处理) ARP ===》 通过ip找mac
2)逻辑地址,ip地址(需要用户指定) ====》为了确定哪个电脑接受
3) 端口 ===》为了确定哪个程序接受
同一个系统,一个程序只能绑定一个端口。
不同系统,同意端口对应的程序可能不一样。

通信过程的组包和拆包
通信过程的组包和拆包.png

2.Socket编程

  • Socket 也是一种文件描述符。
  • Socket 起源于 Unix,而 Unix 基本哲学之一就是“一切皆文件”,都可以用“打开open-> 读写 write/read -> 关闭 close” 模式来操作,Socket 就是该模式的一个实现,网络的Socket数据传输是一种特殊的 I/O。
  • Socket 也只有一个类似于打开文件的函数调用:Socket( ), 该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过 Socket 实现的。

2.1 Socket类型

常见的 Socket 类型有两种:流式 Socket ( SOCK_STREAM )数据报式 Socket ( SOCK_DGRAM ) 。 流式是一种面向连接的 Socket, 针对于面向连接的 TCP 服务应用;数据报式 Socket 是一种无连接的 Socket, 对应于无连接的 UDP服务应用。

2.2 TCP的C/S架构

cs模型:客户端和服务器*

客户端 ===》 客户
1)主动请求服务

服务器 ===》 客服
1)被动提供服务

TCP的CS架构.png

bs模型:浏览器和服务器
browser/server

2.3 示例程序代码

TCP服务器示例代码:

package main
import (
            "fmt"
            "net"
)

func main( )  {
        //监听
        listen, err := net.Listen("tcp", "127.0.0.1 : 8000" )   // 如果是本地的ip地址 则可以不用写 直接省略掉 即 "  : 8000"
         if err != nil {
            fmt.Println("err = ", err)
            return 
          }

          defer listen.Close( )  //最后程序结束后才执行listen.Close( )进行关闭
        
        //阻塞等待用户链接
                      conn , err := listen.Accept( ) 
               if err != nil {
                  fmt.Println("err = ", err)
                  return
                 }

                 //接受用户的请求
                 buf := make( [ ]byte, 1024)  //创建切片缓冲区,其大小为1024
                 n, err1 := conn.Read(buf )
                 if err1 != nil {
                     fmt.Println("err = ", err)
                     return
                     }   
                        
                    fmt.Println("buf = " , string(buf[:n])) //指定打印的切片,即读了多少就打印多少
                    defer conn.Close( )  //关闭当前用户链接
}

TCP客户端的示例代码:

package main 
import (
            "fmt"
            "net"
)

func main( )  {
        //主动连接服务器
       conn, err :=  net.Dial ("tcp", "127.0.0.1 : 8000" ) //服务器的ip地址和端口
       if err != nil {
                fmt.Println ( " err = " , err)
                return 
        }
         
        defer conn.Close( )
 
         //发送数据
         conn.Write([ ]byte ("are you ok")
}

执行结果截图:


执行结果截图

简单版并发服务器
TCP服务器示例代码:

package main
import (
            "fmt"
            "net"
            "strings" //大写转小写函数用到的包
)

//处理用户请求
func HandleConn(conn net.Conn) { //conn 是 net.Conn 类型
      //函数调用完毕,自动关闭conn
      defer conn.Close( ) 

       //获取客户端的网络地址信息
       addr :=  conn.RemoteAddr( ).String( )  // .String( ) 转换成字符串
       fmt.Println(addr, " connect sucessful" )

       buf := make([ ]byte, 2048)

      for{
            //读取用户数据
             n, err := conn.Read(buf)
             if err != nil {
                     fmt.Println("err = ", err)
                      return 
                }
               fmt.Printf("read buf = %s\n", string(buf[:n])) //把缓冲区的内容转换成string类型后打印
               // fmt.Println("len = " ,  len(string(buf[:n])))
     
               if "exit" == string(buf[:n - 1] {  //如果输入 exit 则退出连接
                         fmt.Println(addr, "exit")                       
                         return 
                }

                 //把数据转换为大写,再给用户发送
                conn.Write([ ]byte( strings.ToUpper( string(buf[:n]) )  ))
          }
}


func main( )  {
        //监听
        listen, err := net.Listen("tcp", "127.0.0.1 : 8000" )   // 如果是本地的ip地址 则可以不用写 直接省略掉 即 "  : 8000"
         if err != nil {
            fmt.Println("err = ", err)
            return 
          }

          defer listen.Close( )  //最后程序结束后才执行listen.Close( )进行关闭
        
        //接受多个用户
        for {
              conn, err := listener.Accept( ) 
              if err != nil {
                              fmt.Println("err = " , err)
               return 
               }

            //处理用户请求。每来一个用户就新建一个 go 协程
             go HandleConn(conn)

执行结果截图:


简单版并发服务器的执行结果截图

客户端即可输入也可接受服务器回复的示例代码

package main
import (
         "fmt"
          "net"
          "os"
)

func main( ) {
        //主动连接服务器
        conn, err := net.Dial("tcp", "127.0.0.1:8000")
        if err != nil {
               fmt.Println("net.Dial err = ", err)
               return 
          }

          //main调用完毕,关闭连接
          defer conn.Close( )

           //接受服务器回复的数据,新任务
           go func( ) {
          //从键盘输入内容,给服务器发送内容
           str := make([ ] byte, 1024)
            for {         
                   n, err := os.Stdin.Read(str)   //从键盘上读取内容,并存在str中
                   if err != nil {
                               fmt.Println("conn.Read err = ", err)
                               return 
                           }
                        //把输入的内容给服务器发送
                      conn.Write(str[:n])
                       

                        }
                             } ( )

                 //切片缓冲
                  buf := make([ ]byte, 1024)
                  for {
                        n, err := conn.Read(buf)   //接受服务器的请求
                        if err != nil {
                               fmt.Println("conn.Read err = ", err)
                               return 
                           }
                          fmt.Println(string(buf[:n])) //打印接收到的内容,转换为字符串再打印
                        }


    

               }

TCP服务器示例代码见上

   //  if "exit" == string(buf[:n - 1] {  //如果输入 exit 则退出连接 !!!nc测试
     if "exit" == string(buf[:n - 2] {  //自己写的客户端测试。发送时,多了2个字符。"\r\n"
                         fmt.Println(addr, "exit")                       
                         return 
                }

执行结果截图:


执行结果截图

3.HTTP编程

3.1 概述

3.1.1 Web工作方式

平时浏览网页的时候,会打开浏览器。输入网址后按下回车键,然后就会显示出你想要浏览的内容。其背后的过程可以简单概述为:

对于普通的上网过程,系统其实是这样做的:浏览器本身就是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP,然后通过IP地址找到IP对应的服务器后,要求建立TCP连接,等浏览器发送完HTTP Request(请求)包后,服务器接受到请求包之后才开始处理请求包,服务器调用自身服务,返回HTTP Response(响应)包:客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP连接。

一个Web服务器也称为HTTP服务器,它通过HTTP协议与客户端通信。这个客户端通常指的是Web浏览器(其实手机端客户端内部也是浏览器实现的)。

3.1.2 HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol)
它详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。

HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。如下图所示:


3.1.3 地址(URL)
  • URL全程为Unique Resource Location,用来表示网络资源,可以理解为网络文件路径。
  • URL的长度有限制,不同的服务器的限制值不太相同,但是不能无限长。
    URL的格式如下:
https://www.baidu.com/
3.2 HTTP报文浅析


执行结果截图:


  • 请求报文格式说明
    HTTP请求报文由请求行、请求头部、空行、请求包体 4个部分组成,如下图所示。
    image.png

    (1)请求行

请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。
常用的HTTP协议的请求方法有GET、POST。
GET:

  • 当客户端从服务器中读取某个资源时,使用GET方法。GET方法要求服务器将URL定位的资源放在响应报文的数据部分,回送给客户端,即向服务器请求某个资源。
  • 使用GET方法时,请求参数和对应的值附加在URL后面,利用一个问号("?")代表URL的结尾与请求参数的开始,传递参数长度受限制,因此GET方法不适合用于上传数据。
  • 通过GET方法来获取网页时,参数会显示在浏览器地址栏上,因此保密性很差。
    POST:
  • 当客户端给服务端提供信息较多时可以用POST方法,POST方法向服务器提交数据,比如完成表单数据的提交,将数据提交给服务器处理。
  • GET一般用于获取/查询资源信息,POST会附带用户数据,一般用于更新资源信息。POST方法将请求参数封装在HTTP请求数据中,而且长度没有限制,因为POST携带的数据,在HTTP的请求正文中,以名称/值的形式出现,可以传输大量数据。

(2)请求头
请求头部为请求报文添加了一些附加信息,由”名/值“( 即 ”key/value" )对组成,每一行一对,名和值之间使用冒号分隔。

3.3 响应报文测试方法

响应报文格式分析

image.png

状态码

3.4 HTTP服务器编程

http服务器获取客户端的一些信息



©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 文章首发于个人blog欢迎指正补充,可联系lionsom_lin@qq.com原文地址:《网络是怎样连接的》阅读整...
    lionsom_lin阅读 14,124评论 6 31
  • 网络编程的概述 网络编程的实质就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。 一.OSI网络模型...
    思念挥霍阅读 373评论 0 0
  • 网络编程 一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运...
    go以恒阅读 1,999评论 0 6
  • 网络概念第一天 两台电脑怎么通过网络传输数据?怎样才能知道传输的是数据?谁摸过网线? 看电影,怎么看的?通过电流,...
    小吖朱阅读 1,547评论 0 1
  • 2017 .12.2 有一种等待叫遥遥无期,比起这样我更希望自己掌握主动权,而不是被动的接受无奈的一切。我不想听原...
    喵凤阅读 209评论 0 0