GO WEB 学习笔记(五)WEB服务

Socket编程

什么是Socket

  1. Socket起源于Unix,所以Socket的模式跟UNIX的模式类似,Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符,Socket也具有类似于打开文件的函数调用: Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现。
  2. Socket 有 两种类型:
    • 一种是流失Socket:面向连接的Socket,针对于面向连接的TCP服务应用
    • 一种是数据报式Socket:无连接的Socket,对应于无连接的UDP服务应用
  3. Socket通信:
    • 通信的话首先要想到一个问题,就是我们每一次通信相当于一个进程,但我们如何标记一个进程呢,怎么保证进程的唯一性呢?
    • TCP/IP协议族已经帮我们解决了这个问题,网络层的“IP地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(IP地址,协议,端口)就可以标识网络的进程了,网络中需要互相通信的进程,就可以利用这个标志在他们之间进行交互。
  4. Socket基础知识:
    • IPV4:目前全球Internet所采用的协议族是TCP/IP。IP是TCP/IP中网络层的协议,是TCP/IP族的核心协议。目前主要采用的IP版本号是4(简称为IPv4),Pv4的地址位数为32位,也就是最多有2的32次方的网络设备可以联到Internet上。地址格式类似为:127.0.0.1 172.122.121.111。近十年来由于互联网的蓬勃发展,IP位址的需求量愈来愈大,使得IP位址的发放愈趋紧张,前一段时间,据报道IPv4的地址已经发放完毕,目前很多服务器的IP都是一个宝贵的资源。
    • IPV6:IPv6是下一版本的互联网协议,也可以说是下一代互联网的协议,它是为了解决IPv4在实施过程中遇到的各种问题而被提出的,IPv6采用128位地址长度,几乎可以不受限制地提供地址。地址格式类似为:2002:c0e8:82e7:0:0:0:c0e8:82e7。按保守方法估算IPv6实际可分配的地址,可支持整个地球的每平方米面积上各分配1000多个地址。IPv6的设计过程除了一劳永逸地解决了地址短缺问题以外,还考虑了在IPv4中解决不好的其他问题,主要有端到端IP连接、服务质量(QoS)、安全性、多播、移动性、即插即用等。

GO的Socket

  1. go语言支持的IP类型:type IP []byte
  • net包中有很多函数来操作IP,但是比较有用的较少,其中ParseIP(s string) IP函数会把一个IPv4或者IPv6的地址转化成IP类型,执行之后,只需要输入一个IP地址,就会给出相应的IP格式,下面演示案例:
package main

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

func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "Usage: %s ip=addr\n", os.Args[0])
		os.Exit(1)
	}
	name := os.Args[1]
	addr := net.ParseIP(name)
	if addr == nil {
		fmt.Println("Invalid address")
	} else {
		fmt.Println("The address is ", addr.String())
	}
	os.Exit(0)
}
  1. TCP Socket:
  • 首先我们需要想到一个问题,当我们知道如何通过网络端口访问一个服务时,我们能够做什么呢?
  • 作为客户端来说,我们可以通过向远端某台机器的某个网络端口发送一个请求,然后得到机器在此端口上监听的服务反馈的信息。作为服务端,我们需要把服务绑定到某个指定端口,并且在此端口上监听,当有客户端来访问时能够读取信息并且写入反馈信息。
  • 在go语言的net包中有一个类型TCPConn,这个类型可以用来作为客户端和服务端交互的通道,有两个主要的函数
    • func (c *TCPConn) Write(b []byte) (n int,err os.Error)
    • func (c *TCPConn) Read(b []byte) (n int,err os.Error)
  • 有了这两个函数就可以在客户端和服务器来读写数据,但我们还需要一个函数来知道TCP地址信息,并且还需要一个实体类来存放,这些net包都有
    • 获取一个TCPAddr :func ResolveTCPAddr(network, address string) (*TCPAddr, error)
      • net参数是"tcp4"、“tcp6”、"tcp"中的任意一个,分别表示TCP(IPv4-only),TCP(IPv6-only)或者TCP(IPv4,IPv6的任意一个)
      • addr表示域名或者IP地址,例如"www.google.com:80"或者"127.0.0.1:22"
        在这里插入图片描述
  1. TCP client:Go语言通过net包中的DialTCP函数来建立一个TCP连接,并返回一个TCPConn类型的对象,当连接建立时服务器端也创建一个同类型的对象,此时客户端和服务器端通过各自拥有的TCPConn对象来进行数据交换。一般而言,客户端通过TCPConn对象将请求信息发送到服务器端,读取服务器端响应的信息。服务器端读取并解析来自客户端的请求,并返回应答信息,这个连接只有当任一端关闭了连接之后才失效,不然这连接可以一直使用。
  • 函数定义:func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)
    • net参数是 “tcp4”、“tcp6”、"tcp"中的任意一个,分别表示TCP(IPv4-only)、TCP(IPv6-only)或者TCP(IPv4,IPv6的任意一个)
    • laddr表示本机地址,一般设置为nil
    • raddr表示远程的服务地址
  1. TCP server :可以通过net包来创建一个服务器端程序,在服务器端我们需要绑定服务到指定的非激活端口,并监听此端口,当有客户端请求到达时,可以接收到来自客户端连接的请求。
  • 监听函数:func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error)
  • 连接函数:func (l *TCPListener) Accept() (Conn, error)
  • 下面就是测试样例:
import (
	"fmt"
	"net"
	"time"
)

func main() {
	service := "127.0.0.1:9091"
	tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
	fmt.Println(fmt.Sprintf("IP : %v , port : %v", tcpAddr.IP, tcpAddr.Port))
	if err != nil {
		fmt.Println(err)
	}
	listenAddr, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		fmt.Println(err)
	}
	for {
		conn, err := listenAddr.Accept()

		if err != nil {
			continue
		}
		start := time.Now().String()
		fmt.Println(start)
		conn.Write([]byte(fmt.Sprintf("%v 连接成功!", start)))
		conn.Close()
	}
}
  • 但是上面的代码有个问题,执行的时候是单任务,不能同时接收多个请求,所以我们需要改造一下,加入并发
package main

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

func main() {
	service := "127.0.0.1:9091"
	tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
	fmt.Println(fmt.Sprintf("IP : %v , port : %v", tcpAddr.IP, tcpAddr.Port))
	if err != nil {
		fmt.Println(err)
	}
	listenAddr, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		fmt.Println(err)
	}
	for {
		conn, err := listenAddr.Accept()

		if err != nil {
			continue
		}
		go handlerClient(conn)
	}
}

func handlerClient(conn net.Conn) {
	defer conn.Close()

	start := time.Now().Format("2016-01-02 10:11:12")
	for i := 0; i < 1000; i++ {
		fmt.Println(fmt.Sprintf("%v : %v", start, i))
	}

}
  1. UDP Socket:Go语言包中处理UDP Socket和TCP Socket不同之处在于服务器端处理多个客户端请求数据包的方式不同,UDP缺少了对客户端连接请求的Accept函数。其他几乎一模一样,只有TCP换成了UDP而已。

WebSocket

  1. WebSocket是HTML5的重要特性,它实现了基于浏览器的远程Socket,使浏览器和服务器可以进行全双工通信,许多浏览器(Firefox、Google Chrome和Safari)都已对此做了支持。在WebSocket出现之前,为了实现即时通信,采用的技术都是“轮询”,即在特定的时间间隔内,由浏览器对服务器发出HTTP Request,服务器在收到请求后,返回最新的数据给浏览器刷新,“轮询”使得浏览器需要对服务器不断发出请求,这样会占用大量带宽。WebSocket采用了一些特殊的报头,使得浏览器和服务器只需要做一个握手的动作,就可以在浏览器和服务器之间建立一条连接通道。且此连接会保持在活动状态,你可以使用JavaScript来向连接写入或从中接收数据,就像在使用一个常规的TCP Socket一样。它解决了Web实时化的问题,相比传统HTTP有如下好处:
    • 一个Web客户端只建立一个TCP连接
    • Websocket服务端可以推送数据到Web客户端
    • 有更加轻量级的头,减少数据传送量
  2. WebSocket URL的起始输入是ws://或是wss://,一个带有特定报头的HTTP握手被发送到了服务器端,接着在服务器端或是客户端就可以通过JavaScript来使用某种套接口(Socket),这一套接口可被用来通过事件句柄异步地接收数据。
  3. WebSocket原理:WebSocket的发起流程是这样的
    • 发出WebSocket连接请求
    • 服务器回应
    • 客户端和服务器连接成功,连接成功之后,两者之间就建立连接,然后双方通信就通过特定形式进行传输
      • 通信数据都是以“\x00”开头,以“\xFF”结尾
      • 客户端是可以直接看到的
      • 服务器会自动将“\x00”和“\xFF”去掉

GO 语言实现 WebSocket

  1. Go 语言标准包里面没有提供对 WebSocket 的支持,我们需要通过命令获取这个包go get github.com/gorilla/websocket
  2. 下面通过一个简单的例子来实现:

REST

  1. RESTful,是目前最为流行的一种互联网软件架构。因为它结构清晰、符合标准、易于理解、扩展方便,所以得到越来越多网站的采用。
  2. 什么是REST?
    • 我觉得太过专业的语言不太好描述,我个人的感觉就是一个框架规范,就比如之前 查询数据的接口路由是这么写的 /getxxxxx,添加路由是这么写的 addxxxx 但是这样不太好看 ,现在只需要 【method】 /xxxx 在url前面添加状态就可以,状态有四种 GET来获取资源 , POST 来新建资源, PUT 来更新资源, DETELE来删除资源,这样就可以告诉服务端需要进行什么操作
  3. Web应用要满足REST最重要的原则是:客户端和服务器之间的交互在请求之间是无状态的,即从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,该请求可以由任何可用服务器回答,这十分适合云计算之类的环境。因为是无状态的,所以客户端可以缓存数据以改进性能。
  4. 简单来讲一下RestFul的优缺点:
    • 使用URL描述资源
    • 使用HTTP方法描述行为。使用HTTP状态码来表示不同的结果
    • 使用json交互数据,传统模式使用的是键值对形式
    • RESTful只是一种风格,并不是强制的标准。
  5. 注意事项:
    • HTML标准只能通过链接和表单支持GET和POST。在没有Ajax支持的网页浏览器中不能发出PUT或DELETE命令
    • 有些防火墙会挡住HTTP PUT和DELETE请求要绕过这个限制,客户端需要把实际的PUT和DELETE请求通过POST请求穿透过来。RESTful服务则要负责在收到的POST请求中找到原始的HTTP方法并还原。

GO 实现 RESTful

  • GO 语言没有对REST提供直接的支持,因为RESTful是基于HTTP协议实现的,我们需要自己实现

RPC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值