golang實現tcp客戶端服務端程式

K3v發表於2020-12-27

服務端程式碼:

package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"strconv"
)



// 指定一個命令列引數的名字
var port int
func Init() {
	flag.IntVar(&port, "port", 8081, "set your server port")
}

func main() {
	// 初始化命令列變數
	Init()
	flag.Parse()

	// 監聽埠
	portStr := strconv.Itoa(port)
	if portStr == "" {
		fmt.Println("埠不正確,請確認")
		return
	}

	listen, err := net.Listen("tcp", ":" + portStr)
	if err != nil {
		fmt.Println("監聽埠" + portStr + "失敗", err)
	}
	fmt.Println("監聽" + portStr + "成功!")
	for {
		// 等待連線
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("建立連線失敗了")
		}
		fmt.Println("建立連線成功了")

		// STEP - 1 處理髮送訊息
		go func(conn net.Conn) {
			handleSend(conn)
		}(conn)

		// STEP - 2 處理接收訊息
		// 必須要這個for 保持程式一直處於喚醒狀態,一直讀取conn裡面的流
        var receiveError error
		for {
			//go func(conn net.Conn) {
				receiveError= handleReceive(conn)
				if receiveError !=nil {
					break
				}
			//}(conn)
		}
		if receiveError != nil {
			break
		}
	}

}


func handleReceive(conn net.Conn)  error{
	//fmt.Println("handleReceive Method")
	buff := make([]byte, 4096)
	cnt, err := conn.Read(buff)
	if cnt == 0 || err != nil {
		fmt.Println("[WARN]-----斷開連線------")
		fmt.Println("[WARN]------END--------")
		return errors.New("連線錯誤")
	}
	receiveData := string(buff)
	fmt.Println("你收到了訊息:", receiveData)
	return nil
}

func handleSend(conn net.Conn) {
	// 讀取命令列裡面的字元
	//fmt.Println("handleSend Method")
	r := bufio.NewReader(os.Stdin)
	var line string
	for {
		fmt.Println("Send Message->")
		rawLineBytes, _, _ := r.ReadLine()
		line = string(rawLineBytes)
		if len(line) < 1 {
			continue
		}
		// 傳送
		fmt.Println("你傳送了資料: [YOU]:" + line)
		//conn.Write([]byte("[SERVER]:  " + line))
		_, err := fmt.Fprintf(conn, "[SERVER]:  " + line)
		if err != nil {
			fmt.Println("傳送資料失敗")
		}
	}
}


啟動程式碼:

```go
go run main.go -port=8081 

客戶端程式碼:

package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"time"
)

var serverPort int
var serverIP string
func Init() {
	flag.IntVar(&serverPort, "serverPort", 8081, "set the server PORT you connect with")
	flag.StringVar(&serverIP, "serverIP", "127.0.0.1", "set the server IP you will connect with")
}

func main() {
	// 初始化命令列引數
	Init()
	flag.Parse()

	// 連線server端
	serverAddress := fmt.Sprintf("%s:%d", serverIP, serverPort)
	conn, err := net.DialTimeout("tcp", serverAddress, time.Second * 3)

	// 連線失敗
	if err != nil {
		fmt.Println(fmt.Sprintf("連線[%s]失敗了", serverAddress))
		return
	}
	// 連線成功
	fmt.Println(fmt.Sprintf("建立連線成功了[%s] !", serverAddress))

	// 往conn這個寫一個訊息
	fmt.Fprintf(conn, "HEAD / HTTP/1.0\r\n\r\n")


	// STEP - 1 處理髮送訊息
     go func(conn net.Conn) {
		 handleSend(conn)
	 }(conn)

	// STEP - 2 處理接收訊息
	// 必須要這個for 保持程式一直處於喚醒狀態,一直讀取conn裡面的流
	for {
		err := handleReceive(conn)
		if err != nil {
			break
		}
	}
}


func handleReceive(conn net.Conn) error {
	//fmt.Println("handleReceive Method")
	buff := make([]byte, 4096)
	cnt, err := conn.Read(buff)
	// 收到的資料長度為0
	//if cnt == 0 || err != nil {
	if  err != nil {
		fmt.Println("斷開連線")
		fmt.Println("----------END-----------")
		fmt.Println(err, cnt, err == io.EOF)
		return errors.New("斷開連線了")
	}
	fmt.Println("你收到了訊息:", string(buff))
	return nil
}

func handleSend(conn net.Conn)  {
	//fmt.Println("handleSend Method")
	r := bufio.NewReader(os.Stdin)
	var line string
	for {
		fmt.Println("Send Message->")
		rawLineBytes, _, _ := r.ReadLine()
		line = string(rawLineBytes)
		if len(line) < 1 {
			continue
		}
		fmt.Println("你傳送了資料: [YOU]:" + line)
		_, err := fmt.Fprintf(conn, "[CLIENT]:  " + line)
		if cnt == 0||err != nil {
			fmt.Println("傳送資料失敗")
		}
	}
}

啟動程式碼:

	go run main.go -serverPort=8081 -serverIp=127.0.0.1

tips:

1, 這個簡單的網路應用有幾個關鍵點
     A : for 迴圈 等待連線/ 等待使用者從命令列輸入
     B :go routine 協程處理髮送或者接受訊息,因為這兩個動作,都是需要程式block住,所以必須 要用go routine

2,注意啟動的時候埠占用情況,也許8081被佔用

3,go build 的時候,windows/mac/linux平臺有不同的命令。(自行搜尋下

相關文章