利用golang通道优化TCP Socket服务器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yjp19871013/article/details/82711237

前面的几篇文章分别介绍了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消耗。

猜你喜欢

转载自blog.csdn.net/yjp19871013/article/details/82711237