1. 程式人生 > >利用golang通道優化TCP Socket伺服器

利用golang通道優化TCP Socket伺服器

前面的幾篇文章分別介紹了UDP和TCP進行Socket程式設計的方法,在TCP的文章中,我們除了傳統的阻塞型伺服器,還給出了多執行緒伺服器的實現方式。今天我們利用golang的通道,給出一種更加高效的伺服器設計。

package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"

	"go-study/socket/config"
)

func main() {
	address := config.SERVER_IP + ":" + strconv.Itoa(config.SERVER_PORT)
	listener, err := net.Listen("tcp", address)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	defer listener.Close()

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}

		go handleConn(conn)
	}
}

main函式的部分和之前的TCP伺服器基本相同,目前還看不出什麼差別,主要是執行Listen和Accept,當有客戶端連線時,將連線交給handleConn協程處理。大家或許猜到了,差別就在handleConn上面。

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

	readChan := make(chan string)
	writeChan := make(chan string)
	stopChan := make(chan bool)

	go readConn(conn, readChan, stopChan)
	go writeConn(conn, writeChan, stopChan)

	for {
		select {
		case readStr := <-readChan:
			upper := strings.ToUpper(readStr)
			writeChan <- upper
		case stop := <-stopChan:
			if stop {
				break
			}
		}
	}
}

handleConn函式中建立了三個通道,分別為讀,寫以及停止通道,之後啟動了兩個協程,分別進行讀操作和寫操作,兩個操作之間的聯絡我們就是利用上面的三個通道構造的。下面在for中我們使用select語句,select語句可以根據通道的狀態選擇性地執行不同的語句,上面的程式碼中,當readChan可讀時,會將字串轉為大寫,之後傳送給writeChan,當接收到stopChan的停止指令時,會跳出select,從而handleConn協程就會結束。

func readConn(conn net.Conn, readChan chan<- string, stopChan chan<- bool) {
	for {
		data := make([]byte, config.SERVER_RECV_LEN)
		_, err := conn.Read(data)
		if err != nil {
			fmt.Println(err)
			break
		}

		strData := string(data)
		fmt.Println("Received:", strData)

		readChan <- strData
	}

	stopChan <- true
}

func writeConn(conn net.Conn, writeChan <-chan string, stopChan chan<- bool) {
	for {
		strData := <-writeChan
		_, err := conn.Write([]byte(strData))
		if err != nil {
			fmt.Println(err)
			break
		}

		fmt.Println("Send:", strData)
	}

	stopChan <- true
}

readConn和writeConn就是把之前的讀寫操作分為了兩個協程,readConn協程會阻塞在Read函式,直到網路上有資料可讀,通過readChan傳送讀到的字串,writeConn協程會阻塞在讀取writeChan,直到在handleConn中向writeChan寫入資料。

通過使用通道和select,golang構造出了很好的非同步io機制,我們不需要等待任何的讀寫操作,只是在需要執行的時候,程式才會執行。避免了不必要的CPU消耗。