文章目錄
- 一、前言
- 二、Go開發環境搭建(Windows系統)
- 1、安裝Go命令列工具
- 2、創建GoWorkspace目錄
- 3、配置GOPATH環境變數
- 4、配置GOPROXY代理
- 5、安裝VSCode
- 6、VSCode安裝Go插件
- 7、安裝Go開發工具鏈
- 三、HelloGo 工程
- 1、創建go腳本: main.go
- 2、main.go代碼
- 3、生成go.mod檔案
- 4、編譯生成可執行程式: go build命令
- 5、測驗運行
- 四、用Go做個訊息廣播的服務端
- 1、思維導圖
- 2、腳本說明
- 五、開始寫服務端Go代碼
- 1、創建專案檔案夾和腳本
- 2、server.go腳本
- 2.1、成員變數宣告
- 2.2、全域方法,NewServer
- 2.3、Socket監聽連接,Listen和Accept
- 2.4、啟動協程處理用戶訊息,Handler
- 2.5、訊息廣播,通過管道同步
- 3、user.go腳本
- 3.1、成員變數宣告
- 3.2、全域方法,NewUser
- 3.3、用戶上線,Online
- 3.4、用戶下線,Offline
- 3.5、訊息處理,DoMessage
- 4、main.go腳本
- 5、編譯運行
- 六、Unity客戶端
- 1、創建工程,UnitySocketClient
- 2、UGUI制作界面
- 3、C#腳本
- 3.1、ClientNet.cs腳本
- 3.2、Main.cs腳本
- 4、打包客戶端
- 七、運行測驗
- 1、啟動Go服務端
- 2、啟動Unity客戶端
- 八、工程原始碼
- 九、完畢
一、前言
嗨,大家好,我是新發,
有老同事問我會不會Go語言,人生苦短,Let's Go,我做了一個Go語言基礎的思維導圖,福利給大家~

嘛,今天就做一個Go語言服務端與Unity通信的小案例吧,效果如下,

工程原始碼見文章末尾~
二、Go開發環境搭建(Windows系統)
Go語言是一門編譯型語言,代碼檔案以.go為后綴,我們寫的.go代碼最終要編譯為可執行檔案(在Windows平臺下就是.exe檔案),編譯需要用到go build命令,go build命令哪來的呢,這就需要我們在系統中安裝GO命令列工具,
另外,寫.go代碼需要一個IDE,推薦使用VSCode,需要而外安裝Go插件和工具鏈,
畫個圖,方便大家理解~

看起來好像有點小麻煩,不要怕,幾分鐘搞定,下面我就來教大家~
1、安裝Go命令列工具
進入Go官網:https://golang.google.cn/,點擊Download Go,

然后根據你的作業系統選擇對應的檔案,它支持Windows、macOS、Linux三個平臺,我以Windows為例,點擊第一個,如下,

下載完畢后直接雙擊執行安裝即可,

安裝完畢后,打開cmd命令列(步驟: 按win + r鍵,輸入cmd按回車),然后執行go version,如果能正常輸出版本號,則說明安裝成功了,如下,

2、創建GoWorkspace目錄
在任意磁盤中創建一個檔案夾作為工程 作業空間,建議命名為GoWorkSpace,然后再分別創建bin、pkg、src三個檔案夾,

三個檔案夾的用途如下:
| 檔案夾 | 用途 |
|---|---|
| bin | 用來存放編譯后的可執行檔案 |
| pkg | 用于存放編譯后的包檔案(一些第三方包檔案) |
| src | 是用來存放.go原始碼檔案(就是自己寫的.go代碼) |
3、配置GOPATH環境變數
GOPATH是一個環境變數,用來表明你寫的go專案的存放路徑,現在我們來設定一下GOPATH環境變數,
在我的電腦上滑鼠右鍵,點擊屬性,然后點擊高級系統設定,

再點擊環境變數,

在系統變數下點擊新建按鈕,

變數名為GOPATH,變數值為剛剛創建的GoWorkSpace路徑,然后點擊確定,

這樣,我們的GOPATH環境變數就配置完成了~
4、配置GOPROXY代理
我們在執行go編譯時,會自動去下載依賴包,GOPROXY默認配置是:GOPROXY=https://proxy.golang.org,direct,由于國內訪問不到,編譯時會報錯超時,我們需要改成國內的源,打開命令列,執行下面的命令:
go env -w GOPROXY=https://goproxy.cn,direct
如下,

5、安裝VSCode
接下來是IDE的安裝,建議用VSCode,安裝程序很簡單,這里不贅述~
VSCode官網:https://code.visualstudio.com/

6、VSCode安裝Go插件
VSCode安裝完畢后,點擊插件安裝按鈕,搜索go,選擇Go插件,點擊install按鈕,如下,

注:這個
Go插件提供了go代碼的智能感知、提示、語法高亮、語法檢測等功能,
7、安裝Go開發工具鏈
進行go開發還需要下載配套的開發工具鏈(比如除錯器、代碼風格格式化等),
我們打開VSCode,按Ctrl + Shift + P,輸入go:install,選擇Go: Install/Update Tools,然后全選,最后點擊OK按鈕,如下,
注:如果你沒有
Go: Install/Update Tools這個選項,請檢查第6步的Go插件是否已正常安裝,

耐心等待(大約1分鐘左右),下載完畢后可以在VSCode的日志輸出中看到All tools successfully installed. You are ready to Go. :),如下,

三、HelloGo 工程
以上,我們的Go開發環境就搭建好了,現在我們來寫一個HelloWorld,不,是HelloGo測驗一下吧~
1、創建go腳本: main.go
在GoWorkSpace/src目錄中新建一個HelloGo檔案夾,如下,

回到VScode,創建一個main.go腳本,
注:檔案名不叫
main也可以,不過一般作為程式入口腳本,建議叫main

2、main.go代碼
好了,現在我們開始寫代碼,功能就是列印一句日志:Hello Golang,代碼如下:
// 包名,main包為入口包,main包中必須含有一個main方法
package main
import "fmt"
// 程式入口方法,必須叫main
func main() {
// 輸出日志
fmt.Println("Hello Golang")
}
3、生成go.mod檔案
go mod全稱go modules,在Golang 1.11版本之前,go代碼的包依賴沒有版本控制的概念,比如你依賴了一個protobuf庫,你在go腳本中通過import引入包,如下
import "github.com/micro/protobuf/proto"
它只會從github中下載最新版本的protobuf,可想而知,這對于團隊協作是很不友好的,不同人電腦上不同時期引入的第三方包坑內版本存在差異,可能導致程式無法正常作業,
于是呢,在Golang 1.11版本開始,就引入了go mod,由一個go.mod檔案來記錄依賴包的版本資訊,
現在,我們就來生成這個go.mod檔案,在VSCode終端中,cd進入HelloGo目錄,然后執行命令
go mod init HelloGo
注: 上面的命令的
HelloGo是模塊名
它會生成一個go.mod檔案,如下,

4、編譯生成可執行程式: go build命令
go代碼最終要生成成可執行程式才能運行,現在我們在HelloGo目錄下,執行go build命令,最終它生成了一個HelloGo.exe,如下,

注: 如果要指定生成的
exe名字,則可以加上-o引數,例:go build -o MyTest.exe,它就會生成一個MyTest.exe啦~
5、測驗運行
現在我們去執行這個HelloGo.exe,可以看到,成功輸出了Hello Golang,如下,

如果我們想跳過go build命令,直接測驗go腳本,可以使用go run命令,例:
go run main.go
如下,

四、用Go做個訊息廣播的服務端
接下來,我們用Go來開發一個Socket通信的服務端,實作訊息廣播的功能吧~
1、思維導圖
在開始寫代碼之前,我們先設計一下服務端的模塊,畫個圖,如下,

2、腳本說明
main.go為程式入口腳本;
server.go負責socket監聽和管理;
user.go是用戶類腳本,當server.go的socket監聽到有客戶端連接時,構造一個User物件,后續的socket通信交由user.go腳本代理,
模塊很簡單,相信大家很容易看懂,
五、開始寫服務端Go代碼
1、創建專案檔案夾和腳本
我們在src目錄中創建一個GoSocketServer檔案夾,作為專案檔案夾,

接著我們在GoSocketServer檔案夾中創建main.go、server.go和user.go三個腳本,

2、server.go腳本
我們先封裝一下Server類,注意在Go語言中,定義類用的是struct關鍵字,成員變數名如果是首字母大寫,則表示是public的,如果是小寫,則表示是private的,
2.1、成員變數宣告
// Server.go 腳本
package main
// import ...
type Server struct {
Ip string
Port int
// 在線用戶容器
OnlineMap map[string]*User
// 用戶列容器鎖,對容器進行操作時進行加鎖
mapLock sync.RWMutex
// 訊息廣播的管道
Message chan string
}
講解:
Message成員是一個chan型別,即管道型別,用于goroutine之間的訊息同步,當客戶端連接服務端時,服務端開啟一個goroutine來處理后續的用戶訊息,訊息需要廣播給所有在線的客戶端,所以這里我們通過Message管道來做一層訊息傳遞,
OnlineMap是在線用戶容器(注意User類在user.go腳本中定義,下文會講),OnlineMap存盤當前連接到服務端的用戶,OnlineMap的操作存在多執行緒并行處理的情況,所以我們需要使用一個sync.RWMutex讀寫鎖對它進行加鎖處理,宣告一個mapLock成員,
2.2、全域方法,NewServer
我們定義一個NewServer全域方法,構造Server物件,提供給外部呼叫,
func NewServer(ip string, port int) *Server {
server := &Server{
Ip: ip,
Port: port,
OnlineMap: make(map[string]*User),
Message: make(chan string),
}
return server
}
2.3、Socket監聽連接,Listen和Accept
監聽Socket,我們可以使用net模塊的Listen方法,函式原型如下,
// net 模塊
func Listen(network, address string) (Listener, error)
例:
// import "net"
listener, err := net.Listen("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Println("net.Listen err:", err)
return
}
要監聽客戶端連接,則用到的是Listener的Accept介面,該方法會阻塞,當接收到socket連接時才會繼續往下執行,介面原型如下,
// Listener介面
Accept() (Conn, error)
例:
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err:", err)
}
我們把Socket監聽連接的邏輯封裝到Server的Start方法中,如下,
// Server.go 腳本
// 啟動服務器的介面
func (this *Server) Start() {
// socket監聽
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
if err != nil {
fmt.Println("net.Listen err:", err)
return
}
// 程式退出時,關閉監聽,注意defer關鍵字的用途
defer listener.Close()
// 注意for回圈不加條件,相當于while回圈
for {
// Accept,此處會阻塞,當有客戶端連接時才會往后執行
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err:", err)
continue
}
// TODO 啟動一個協程去處理
}
}
2.4、啟動協程處理用戶訊息,Handler
上面Start函式中,當Listener接收到連接后,為了不阻塞for回圈,我們啟動協程去處理用戶行為,封裝一個Handler方法,
// server.go 腳本
func (this *Server) Handler(conn net.Conn) {
// ...
}
在上面的Start方法中添加Handler呼叫,
// server.go 腳本
func (this *Server) Start() {
// ...
for {
// Accept,此處會阻塞,當有客戶端連接時才會往后執行
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err:", err)
continue
}
// 啟動一個協程去處理
go this.Handler(conn)
}
}
Handle方法里面主要做三件事情:
1、構建User物件;
2、啟動一個新的協程從Conn中讀取訊息;
3、通過User物件執行訊息處理,
// server.go 腳本
func (this *Server) Handler(conn net.Conn) {
// 構造User物件,NewUser全域方法在user.go腳本中
user := NewUser(conn, this)
// 用戶上線
user.Online()
// 啟動一個協程
go func() {
buf := make([]byte, 4096)
for {
// 從Conn中讀取訊息
len, err := conn.Read(buf)
if 0 == len {
// 用戶下線
user.Offline()
return
}
if err != nil && err != io.EOF {
fmt.Println("Conn Read err:", err)
return
}
// 用戶針對msg進行訊息處理
user.DoMessage(buf, len)
}
}()
}
2.5、訊息廣播,通過管道同步
收到用戶訊息時,我們要廣播給所有在線的用戶,首先是把要廣播的訊息寫到Message管道中,如下,
// server.go 腳本
func (this *Server) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
this.Message <- sendMsg
}
接著我們定義一個ListenMessager方法,去監聽Message管道,當Message管道中有訊息時,把訊息寫到用戶管道中,
// server.go 腳本
func (this *Server) ListenMessager() {
for {
// 從Message管道中讀取訊息
msg := <-this.Message
// 加鎖
this.mapLock.Lock()
// 遍歷在線用戶,把廣播訊息同步給在線用戶
for _, user := range this.OnlineMap {
// 把要廣播的訊息寫到用戶管道中
user.Channel <- msg
}
// 解鎖
this.mapLock.Unlock()
}
}
我們在Start方法中去啟動一個協程來執行ListenMessager,
// server.go 腳本
func (this *Server) Start() {
// ...
// 啟動一個協程來執行ListenMessager
go this.ListenMessager()
for {
// Accept,此處會阻塞,當有客戶端連接時才會往后執行
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err:", err)
continue
}
// 啟動一個協程去處理
go this.Handler(conn)
}
}
3、user.go腳本
User類,主要做的就是訊息處理,即用戶行為的代理,如果是在skynet中,就是一個用戶agent服務,
注:關于
skynet,我之前寫過幾篇文章,感興趣的同學也可以看看,
【游戲開發實戰】手把手教你從零跑一個Skynet,詳細教程,含案例講解(服務端 | Skynet | Ubuntu)
【游戲開發實戰】手把手教你在Windows上通過WSL運行Skynet,不用安裝虛擬機,方便快捷(WSL | Linux | Ubuntu | Skynet | VSCode)
【游戲開發實戰】教你Unity通過sproto協議與Skynet框架的服務端通信,附工程原始碼(Unity | Sproto | 協議 | Skynet)
3.1、成員變數宣告
我們先定義一些基礎的成員變數,
// user.go 腳本
type User struct {
Name string // 昵稱,默認與Addr相同
Addr string // 地址
Channel chan string // 訊息管道
conn net.Conn // 連接
server *Server // 快取Server的參考
}
3.2、全域方法,NewUser
我們定義一個NewUser全域方法,構造User物件,提供給外部呼叫,
// user.go 腳本
func NewUser(conn net.Conn, server *Server) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
Channel: make(chan string),
conn: conn,
server: server,
}
return user
}
3.3、用戶上線,Online
封裝一個Online方法,用戶上線時,廣播一個上線訊息,
// user.go 腳本
func (this *User) Online() {
// 用戶上線,將用戶加入到OnlineMap中,注意加鎖操作
this.server.mapLock.Lock()
this.server.OnlineMap[this.Name] = this
this.server.mapLock.Unlock()
// 廣播當前用戶上線訊息
this.server.BroadCast(this, "上線啦O(∩_∩)O")
}
3.4、用戶下線,Offline
封裝一個Offline方法,用戶下線時,廣播一個下線訊息,
// user.go 腳本
func (this *User) Offline() {
// 用戶下線,將用戶從OnlineMap中洗掉,注意加鎖
this.server.mapLock.Lock()
delete(this.server.OnlineMap, this.Name)
this.server.mapLock.Unlock()
// 廣播當前用戶下線訊息
this.server.BroadCast(this, "下線了o(╥﹏╥)o")
}
3.5、訊息處理,DoMessage
訊息的傳輸,實際專案中會使用到一些通信協議對訊息進行加密和壓縮,比如protobuf、sproto等,這里我就簡單處理,直接以字串的二進制流傳輸,做一個簡單的訊息廣播,
// user.go 腳本
func (this *User) DoMessage(buf []byte, len int) {
//提取用戶的訊息(去除'\n')
msg := string(buf[:len-1])
// 呼叫Server的BroadCast方法
this.server.BroadCast(this, msg)
}
上面Server類中的BroadCast方法,會把訊息同步回每個User物件的Channel管道,所以我們需要在User中去監聽Channel管道訊息,封裝個ListenMessage方法,我們先構造一個bytebuf,在頭部兩個位元組寫入訊息長度,然后再寫入訊息內容,如下,
func (this *User) ListenMessage() {
for {
msg := <-this.Channel
fmt.Println("Send msg to client: ", msg, ", len: ", int16(len(msg)))
bytebuf := bytes.NewBuffer([]byte{})
// 前兩個位元組寫入訊息長度
binary.Write(bytebuf, binary.BigEndian, int16(len(msg)))
// 寫入訊息資料
binary.Write(bytebuf, binary.BigEndian, []byte(msg))
// 發送訊息給客戶端
this.conn.Write(bytebuf.Bytes())
}
}
然后在NewUser方法中添加一個協程呼叫,如下
func NewUser(conn net.Conn, server *Server) *User {
// ...
// 啟動協程,監聽Channel管道訊息
go user.ListenMessage()
return user
}
4、main.go腳本
main.go腳本是程式入口腳本,我們要定義一個main方法作為入口函式,
我們封裝一個StartServer方法,通過NewServer全域方法構造一個Server物件,然后執行Start成員方法,如下,
// main.go 腳本
func StartServer() {
server := NewServer("127.0.0.1", 8888)
server.Start()
}
然后在main方法中啟動一個協程去執行StartServer,如下,
// main.go 腳本
func main() {
// 啟動Server
go StartServer()
// TODO 你可以寫其他邏輯
fmt.Println("這是一個Go服務端,實作了Socket訊息廣播功能")
// 防止主執行緒退出
for {
time.Sleep(1 * time.Second)
}
}
5、編譯運行
在VSCode的終端中,進入GoSocketServer目錄,然后執行go mod init GoSocketServer,生成go.mod檔案,如下,

執行go build命令,將go腳本編譯為.exe可執行程式(Windows平臺),如下,

運行GoSocketServer.exe,如下,可以看到,服務端啟動起來了,

下面,我們用Unity實作客戶端部分的功能吧~
六、Unity客戶端
1、創建工程,UnitySocketClient
創建Unity工程,專案名稱叫UnitySocketClient吧,如下,

2、UGUI制作界面
使用UGUI制作一個界面,如下,

節點層級結構如下,

3、C#腳本
C#腳本只有兩個,一個ClientNet.cs,一個Main.cs,

3.1、ClientNet.cs腳本
ClientNet.cs腳本封裝三個介面出來供外部呼叫,如下,

代碼如下,代碼比較簡單,我寫了注釋,相信大家能看懂,
using System;
using UnityEngine;
using System.Net.Sockets;
public class ClientNet : MonoBehaviour
{
private void Awake()
{
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_readOffset = 0;
m_recvOffset = 0;
// 16KB
m_recvBuf = new byte[0x4000];
}
private void Update()
{
if (null == m_socket) return;
if (m_connectState == ConnectState.Ing && m_connectAsync.IsCompleted)
{
// 連接服務器失敗
if (!m_socket.Connected)
{
m_connectState = ConnectState.None;
if (null != m_connectCb)
m_connectCb(false);
}
}
if (m_connectState == ConnectState.Ok)
{
TryRecvMsg();
}
}
private void TryRecvMsg()
{
// 開始接收訊息
m_socket.BeginReceive(m_recvBuf, m_recvOffset, m_recvBuf.Length - m_recvOffset, SocketFlags.None, (result) =>
{
// 如果有訊息,會進入這個回呼
// 這個len是讀取到的長度,它不一定是一個完整的訊息的長度,我們下面需要決議頭部兩個位元組作為真實的訊息長度
var len = m_socket.EndReceive(result);
if (len > 0)
{
m_recvOffset += len;
m_readOffset = 0;
if (m_recvOffset - m_readOffset >= 2)
{
// 頭兩個位元組是真實訊息長度,注意位元組順序是大端
int msgLen = m_recvBuf[m_readOffset + 1] | (m_recvBuf[m_readOffset] << 8);
if (m_recvOffset >= (m_readOffset + 2 + msgLen))
{
// 決議訊息
string msg = System.Text.Encoding.UTF8.GetString(m_recvBuf, m_readOffset + 2, msgLen);
Debug.Log("Recv msgLen: " + msgLen + ", msg: " + msg);
if (null != m_recvMsgCb)
m_recvMsgCb(msg);
m_readOffset += 2 + msgLen;
}
}
// buf移位
if (m_readOffset > 0)
{
for (int i = m_readOffset; i < m_recvOffset; ++i)
{
m_recvBuf[i - m_readOffset] = m_recvBuf[i];
}
m_recvOffset -= m_readOffset;
}
}
}, this);
}
/// <summary>
/// 連接服務端
/// </summary>
/// <param name="host">IP地址</param>
/// <param name="port">埠</param>
/// <param name="cb">回呼</param>
public void Connect(string host, int port, Action<bool> cb)
{
m_connectCb = cb;
m_connectState = ConnectState.Ing;
m_socket.SendTimeout = 100;
m_connectAsync = m_socket.BeginConnect(host, port, (IAsyncResult result) =>
{
// 連接成功會進入這里,連接失敗不會進入這里
var socket = result.AsyncState as Socket;
socket.EndConnect(result);
m_connectState = ConnectState.Ok;
m_networkStream = new NetworkStream(m_socket);
Debug.Log("Connect Ok");
if (null != m_connectCb) m_connectCb(true);
}, m_socket);
Debug.Log("BeginConnect, Host: " + host + ", Port: " + port);
}
/// <summary>
/// 注冊訊息接識訓呼函式
/// </summary>
/// <param name="cb">回呼函式</param>
public void RegistRecvMsgCb(Action<string> cb)
{
m_recvMsgCb = cb;
}
/// <summary>
/// 發送訊息
/// </summary>
/// <param name="bytes">訊息的位元組流</param>
public void SendData(byte[] bytes)
{
m_networkStream.Write(bytes, 0, bytes.Length);
}
/// <summary>
/// 關閉Sockete
/// </summary>
public void CloseSocket()
{
m_socket.Shutdown(SocketShutdown.Both);
m_socket.Close();
}
/// <summary>
/// 判斷Socket是否連接狀態
/// </summary>
/// <returns></returns>
public bool IsConnected()
{
return m_socket.Connected;
}
private enum ConnectState
{
None,
Ing,
Ok,
}
private Action<bool> m_connectCb;
private Action<string> m_recvMsgCb;
private ConnectState m_connectState = ConnectState.None;
private IAsyncResult m_connectAsync;
private byte[] m_recvBuf;
private int m_readOffset;
private int m_recvOffset;
private Socket m_socket;
private NetworkStream m_networkStream;
private static ClientNet s_instance;
public static ClientNet instance
{
get
{
if (null == s_instance)
{
var go = new GameObject("ClientNet");
s_instance = go.AddComponent<ClientNet>();
}
return s_instance;
}
}
}
3.2、Main.cs腳本
Main.cs腳本作為入口腳本,同時作為UI互動的腳本,
代碼如下,
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Main : MonoBehaviour
{
public Button sendBtn;
public InputField inputField;
public Text chatText;
private Queue<string> m_msgQueue;
private void Awake()
{
m_msgQueue = new Queue<string>();
}
void Start()
{
// 注冊訊息回呼
ClientNet.instance.RegistRecvMsgCb((msg) =>
{
// 把訊息快取到佇列中,注意不要在這里直接操作UI物件
m_msgQueue.Enqueue(msg);
});
// 連接服務端
ClientNet.instance.Connect("127.0.0.1", 8888, (ok) =>
{
Debug.Log("連接服務器, ok: " + ok);
});
sendBtn.onClick.AddListener(SendMsg);
}
/// <summary>
/// 發送訊息
/// </summary>
private void SendMsg()
{
if (ClientNet.instance.IsConnected())
{
// 把字串轉成位元組流
byte[] data = System.Text.Encoding.UTF8.GetBytes(inputField.text + "\n");
// 發送給服務端
ClientNet.instance.SendData(data);
// 清空輸入框文本
inputField.text = "";
}
else
{
Debug.LogError("你還沒連接服務器");
}
}
private void Update()
{
if (m_msgQueue.Count > 0)
{
// 從訊息佇列中取訊息,并更新到聊天文本中
chatText.text += m_msgQueue.Dequeue() + "\n";
}
// 按回車鍵,發送訊息
if(Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
{
SendMsg();
}
}
private void OnDestroy() {
ClientNet.instance.CloseSocket();
}
}
把Main.cs腳本掛到Canvas節點上,并賦值腳本的UI成員物件,如下,

4、打包客戶端
點擊選單File / Build Settings...,然后添加場景到Scenes in Build串列中,選擇平臺為PC平臺,如下,

接著點擊Player Settings,設定Fullscreen Mode為Windowed模式,即視窗模式,設定一下視窗大小,如下,

最終,點擊Build,執行打包,

打包完畢,生成了exe檔案,如下,

七、運行測驗
1、啟動Go服務端
回到我們的Go服務端工程,在終端執行我們上面生成的GoSocketServer.exe,如下,

2、啟動Unity客戶端
啟動多個客戶端,測驗聊天,如下,

功能正常,VeryGood,收拾吃飯去~
八、工程原始碼
本文服務端+客戶端工程原始碼已上傳到CODE CHINA,感興趣的同學可自行下載學習,
地址:https://codechina.csdn.net/linxinfa/golangserverandunityclientdemo
注:我使用的go版本是1.17.2,使用的Unity版本是2021.1.9f1c1

九、完畢
好啦,就先到這里吧~
我是林新發:https://blog.csdn.net/linxinfa
原創不易,若轉載請注明出處,感謝大家~
喜歡我的可以點贊、關注、收藏,如果有什么技術上的疑問,歡迎留言或私信~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/345862.html
標籤:其他
