Go语言构建Socket应用


本文主要介绍如何用Go语言来构建Socket应用。

Go语言实现Socket其实很容易,net包中对网络协议的封装已经非常完善,直接利用即可。当我们谈到Socket的时候,一般场景指的是服务端和客户端的”双向通信”,而通信就需要建立连接通道。Go语言中这个“通道”可以用net包中的类型Conn来表示,而TCPConnUDPConnConn对应于TCP和UDP协议的实现。本文仅以TCP为例来介绍Go语言的Socket实现(UDP实现差异不大)。

Simple Server

我们使用 TCPConn 在客户端和服务器端来读写数据以及管理连接,主要用到ReadWriteClose这三个方法:

func (c *TCPConn) Read(b []byte) (int, error)
func (c *TCPConn) Write(b []byte) (int, error)
func (c *TCPConn) Close() error
###

下面我们先来创建一个服务端,使用net包中的ListenTCP方法来建立一个TCP网络监听:

func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error)

此方法接收两个参数,network为TCP网络名字符串,TCPAddr为TCP的地址信息,返回一个TCPListener即TCP网络监听。TCPAddr可以通过ResolveTCPAddr方法来构造:

func ResolveTCPAddr(network, address string) (*TCPAddr, error)

服务端代码server/main.go如下:

package main

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

func main() {
	tcpAddr, _ := net.ResolveTCPAddr("tcp4", ":9000")
	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)
	for {
		conn, err := listener.Accept() // new client connected
		if err != nil {
			log.Println(err)
			continue // keep on listening
		}
		go handler(conn) // goroutine handle the connection
	}
}

func handler(conn net.Conn) {
	defer conn.Close()
	conn.Write([]byte("Success!"))
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
		os.Exit(1)
	}
}

这里我们建立了一个位于本地9000端口的TCP网络监听,持续接收新客户端连接,并在客户端连接成功之后返回成功信息并断开本次连接。每当有新的客户端接入的时候,TCPListener的Accept(或者AcceptTCP)方法会返回当前连接:

func (l *TCPListener) Accept() (Conn, error)
func (l *TCPListener) AcceptTCP() (*TCPConn, error)

Simple Client

方便测试我们的服务,下面来创建一个客户端程序,Go语言中通过net包中的DialTCP函数来建立一个TCP连接,并返回一个TCPConn类型的对象:

func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)

客户端代码client/main.go如下:

package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
)

func main() {
	tcpAddr, _ := net.ResolveTCPAddr("tcp4", ":9000")
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	checkError(err)
	_, err = conn.Write([]byte("Hi there!"))
	checkError(err)
	result, err := ioutil.ReadAll(conn)
	checkError(err)
	fmt.Println(string(result))
	os.Exit(0)
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
		os.Exit(1)
	}
}

以上代码创建了一个TCP连接conn,通过conn来发送请求信息,最后通过ioutil.ReadAll从conn中读取全部的文本,也就是服务端响应反馈的信息。 (未完占坑)

参考文章