如何在Go语言中使用Websockets:最佳工具与行动指南

如今,在不刷新页面的情况下发送消息并获得即时响应在我们看来是理所当然的事情 。但是曾几何时,启用实时功能对开发人员来说是一个真正的挑战 。开发社区在HTTP长轮询(http long polling)和AJAX上走了很长一段路,但终于还是找到了一种构建真正的实时应用程序的解决方案 。
该解决方案以WebSockets的形式出现,这使得在用户浏览器和服务器之间开启一个交互式会话成为可能 。WebSocket支持浏览器将消息发送到服务器并接收事件驱动的响应,而不必使用长轮询服务器的方式去获取响应 。
就目前而言,WebSockets是构建实时应用程序的首选解决方案,包括在线游戏,即时通讯程序,跟踪应用程序等均在使用这一方案 。本文将说明WebSockets的操作方式,并说明我们如何使用
Go语言构建WebSocket应用程序 。我们还将比较最受欢迎的WebSocket库,以便您可以根据选择出最适合您的那个 。
网络套接字(network socket)与WebSocket
在Go中使用WebSockets之前,让我们在网络套接字和WebSockets之间划清一条界限 。
网络套接字网络套接字(或简称为套接字)充当内部端点,用于在同一计算机或同一网络上的不同计算机上运行的应用程序之间交换数据 。
套接字是Unix和windows操作系统的关键部分,它们使开发人员更容易创建支持网络的软件 。应用程序开发人员不可以直接在程序中包含套接字,而不是从头开始构建网络连接 。由于网络套接字可用于许多不同的网络协议(如HTTP,FTP等),因此可以同时使用多个套接字 。
套接字是通过一组函数调用创建和使用的,这些函数调用有时称为套接字的应用程序编程接口(API) 。正是由于这些函数调用,套接字可以像常规文件一样被打开 。
网络套接字有如下几种类型:

  • 数据报套接字(SOCK_DGRAM),也称为无连接套接字,使用用户数据报协议(UDP) 。数据报套接字支持双向消息流并保留记录边界 。
  • 流套接字(SOCK_STREAM),也称为面向连接的套接字,使用传输控制协议(TCP),流控制传输协议(SCTP)或数据报拥塞控制协议(DCCP) 。这些套接字提供了没有记录边界的双向,可靠,有序且无重复的数据流 。
  • 原始套接字(或原始IP套接字)通常在路由器和其他网络设备中可用 。这些套接字通常是面向数据报的,尽管它们的确切特性取决于协议提供的接口 。大多数应用程序不使用原始套接字 。提供它们是为了支持新的通信协议的开发,并提供对现有协议更深层设施的访问 。
套接字通信首先,让我们弄清楚如何确保每个套接字都是唯一的 。否则,您将无法建立可靠的沟通通道(channel) 。
为每个进程(process)提供唯一的PID有助于解决本地问题 。但是,这种方法不适用于网络 。要创建唯一的套接字,我们建议使用TCP / IP协议 。使用TCP / IP,网络层的IP地址在给定网络内是唯一的,并且协议和端口在主机应用程序之间是唯一的 。
TCP和UDP是用于主机之间通信的两个主要协议 。让我们看看您的应用程序如何连接到TCP和UDP套接字 。
  • 连接到TCP套接字
为了建立TCP连接,Go客户端使用net程序包中的DialTCP函数 。DialTCP返回一个TCPConn对象 。建立连接后,客户端和服务器开始交换数据:客户端通过TCPConn向服务器发送请求,服务器解析请求并发送响应,TCPConn从服务器接收响应 。
如何在Go语言中使用Websockets:最佳工具与行动指南

文章插图
图:TCP Socket
该连接将持续保持有效,直到客户端或服务器将其关闭 。创建连接的函数如下:
客户端:
// init tcpAddr, err := net.ResolveTCPAddr(resolver, serverAddr) if err != nil { // handle error } conn, err := net.DialTCP(network, nil, tcpAddr) if err != nil { // handle error } // send message _, err = conn.Write({message}) if err != nil { // handle error } // receive message var buf [{buffSize}]byte _, err := conn.Read(buf[0:]) if err != nil { // handle error }服务端:
// init tcpAddr, err := net.ResolveTCPAddr(resolver, serverAddr) if err != nil { // handle error } listener, err := net.ListenTCP("tcp", tcpAddr) if err != nil { // handle error } // listen for an incoming connection conn, err := listener.Accept() if err != nil { // handle error } // send message if _, err := conn.Write({message}); err != nil { // handle error }// receive message buf := make([]byte, 512) n, err := conn.Read(buf[0:]) if err != nil { // handle error }
  • 连接到UDP套接字
与TCP套接字相反,使用UDP套接字,客户端只是向服务器发送数据报 。没有Accept函数,因为服务器不需要接受连接,而只是等待数据报到达 。


推荐阅读