注:发文章我不像某些大咖只发大概思路,每一步我的操作我都会写下来,如果实在看不懂就去下载我的案例哈,本文章只研究下如何互发文字通讯,web端互发图片的等我研究出来再发哈
Unity做聊天室,网上大多数都是通过socket做的,我之前也尝试了一下不难,但是最近的项目都是发布成webgl,我发现一个问题,那就是web端不支持socket,通过查找资料,我找到另一个通讯方式——websocket,先普及型websocket。
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。
1.原理:
WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。
2.连接过程 —— 握手过程
- 1. 浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
- 2. TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)
- 3. 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
- 4. 当收到了连接成功的消息后,通过TCP通道进行传输通信。
3.WebSocket与HTTP的关系
相同点
- 1. 都是一样基于TCP的,都是可靠性传输协议。
- 2. 都是应用层协议。
不同点
- 1. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
- 2. WebSocket是需要握手进行建立连接的。
联系
WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。
4.WebSocket与Socket的关系
Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。
WebSocket则是一个典型的应用层协议。
区别
Socket是传输控制层协议,WebSocket是应用层协议。
5.在Unity中,WebSocket比Socket更适用各个平台的开发
以下介绍下Unity发布Web,通讯功能如何做,以聊天室为案例
1.导入BestHttp插件
这个插件包含所有即使通讯方式的简单使用,下载地址为BestHttp通讯插件-C#文档类资源-CSDN下载
2.布置UI
这边我只做了两个简易界面,一个是登录界面,一个是聊天窗口界面,功能如图所示
?
?3.构建服务端
使用VS新建项目C#控制台
?
通过包管理器,引用websocket和litjson相关组件
?
?
?
除了控制台自带的类Program,还需新建类MessageData,ClientController,各代码如下:
using SuperSocket.SocketBase;
using SuperWebSocket;
using System;
using System.Collections.Generic;
class Program
{
public static List<ClientController> clientControllerList = new List<ClientController>();
static void Main(string[] args)
{
// Console.WriteLine("Press any key to start the WebSocketServer!");
//Console.ReadKey();
// Console.WriteLine();
var appServer = new WebSocketServer();
//Setup the appServer
if (!appServer.Setup(2012)) //Setup with listening port
{
Console.WriteLine("Failed to setup!");
Console.ReadKey();
return;
}
appServer.NewSessionConnected += delegate (WebSocketSession session)
{
// Console.WriteLine("新玩家加入: " + session.SessionID);
// session.Send("新玩家加入: " + session.SessionID);
};
appServer.SessionClosed += delegate (WebSocketSession session, CloseReason value)
{
for (int i = 0; i < clientControllerList.Count; i++)
{
if (session.SessionID == clientControllerList[i].session.SessionID)
{
ClientController client = clientControllerList[i];
client .SendMessageDataToAllClientWithOutSelf(client.nickName + "离开聊天室");
Console.WriteLine(client.nickName+ "离开聊天室");
clientControllerList.Remove(clientControllerList[i]);
return ;
}
}
};
appServer.NewMessageReceived += delegate (WebSocketSession session, string value)
{
MessageData data = LitJson.JsonMapper.ToObject<MessageData>(value);
ClientController clientController =GetClient(session);
string msg = "";
switch (data.msgType)
{
case MessageType.Login://如果是登陆消息
clientController.nickName = data.nickname;
// session.Send("玩家加入: " + data.name);
clientControllerList.Add(clientController);
msg = clientController.nickName + "加入聊天室";
Console.WriteLine(clientController.nickName + "加入聊天室");
break;
case MessageType.Chat://如果是聊天消息
Console.WriteLine(clientController.nickName + "说: " + data.msg);
data.nickname = clientController.nickName;
msg = LitJson.JsonMapper.ToJson(data);
break;
case MessageType.LogOut://客户端请求退出
Console.WriteLine(clientController.nickName + "离开聊天室");
clientControllerList.Remove(clientController);
msg = clientController.nickName + "离开聊天室";
break;
}
clientController.SendMessageDataToAllClientWithOutSelf(msg);
};
Console.WriteLine();
//Try to start the appServer
if (!appServer.Start())
{
Console.WriteLine("Failed to start!");
Console.ReadKey();
return;
}
Console.WriteLine("The server started successfully, press key 'q' to stop it!");
while (Console.ReadKey().KeyChar != 'q')
{
Console.WriteLine();
continue;
}
//Stop the appServer
appServer.Stop();
Console.WriteLine();
Console.WriteLine("The server was stopped!");
Console.ReadKey();
}
public static ClientController GetClient(WebSocketSession session)
{
for (int i = 0; i < clientControllerList.Count; i++)
{
if (session.SessionID == clientControllerList[i].session.SessionID)
{
return clientControllerList[i];
}
}
return new ClientController(session);
}
}
using SuperWebSocket;
using System;
class ClientController
{
/// <summary>
/// 用户链接的通道
/// </summary>
public WebSocketSession session;
/// <summary>
/// 昵称
/// </summary>
public string nickName;
//接收的线程
public ClientController(WebSocketSession socket)
{
session= socket;
}
/// <summary>
/// 广播消息,排除掉自己
/// </summary>
/// <param name="data"></param>
public void SendMessageDataToAllClientWithOutSelf(string data)
{
//5 i=3
for (int i = 0; i < Program.clientControllerList.Count; i++)
{
if (Program.clientControllerList[i] != this)
{
Program.clientControllerList[i].SendToClient(data);
}
}
}
void SendToClient(string data)
{
session.Send(data);
}
}
/// <summary>
/// 简单的协议类型
/// </summary>
public enum MessageType
{
Chat = 0,//聊天
Login = 1,//登陆
LogOut = 2,//登出
}
/// <summary>
/// 消息体
/// </summary>
public class MessageData
{
/// <summary>
/// 消息类型
/// </summary>
public MessageType msgType;
/// <summary>
/// 消息内容
/// </summary>
public string msg;
public string nickname;
}
此时点击运行,即可运行服务端
?
?4.客户端代码
新建类WebSocketTest,MessageData,ChatItem,代码如下:
using LitJson;
/// <summary>
/// 简单的协议类型
/// </summary>
public enum MessageType
{
Chat = 0,//聊天
Login = 1,//登陆
LogOut = 2,//登出
}
/// <summary>
/// 消息体
/// </summary>
public class MessageData
{
/// <summary>
/// 消息类型
/// </summary>
public MessageType msgType;
/// <summary>
/// 消息内容
/// </summary>
public string msg;
public string nickname;
}
using System;
using UnityEngine;
using BestHTTP;
using BestHTTP.WebSocket;
using UnityEngine.UI;
using BestHTTP.Examples.Helpers;
namespace BestHTTP.Examples.Websockets
{
public class WebSocketTest : BestHTTP.Examples.Helpers.SampleBase
{
// [SerializeField]
// [Tooltip("The WebSocket address to connect")]
private string address = "ws://192.168.2.50:2012";
[SerializeField]
private InputField name_input;
[SerializeField]
private InputField content_input;
[SerializeField]
private ScrollRect _scrollRect;
[SerializeField]
private RectTransform _contentRoot;
[SerializeField]
private TextListItem _listItemPrefab;
[SerializeField]
private int _maxListItemEntries = 100;
#pragma warning restore
/// <summary>
/// Saved WebSocket instance
/// </summary>
WebSocket.WebSocket webSocket;
public GameObject loginGo;
public GameObject chatGo;
protected override void Start()
{
base.Start();
}
public void LoginIn()
{
if (name_input.text == "" || name_input.text == null)
return;
// Create the WebSocket instance
this.webSocket = new WebSocket.WebSocket(new Uri(address));
#if !UNITY_WEBGL
this.webSocket.StartPingThread = true;
#if !BESTHTTP_DISABLE_PROXY
if (HTTPManager.Proxy != null)
this.webSocket.InternalRequest.Proxy = new HTTPProxy(HTTPManager.Proxy.Address, HTTPManager.Proxy.Credentials, false);
#endif
#endif
// Subscribe to the WS events
this.webSocket.OnOpen += OnOpen;
this.webSocket.OnMessage += OnMessageReceived;
this.webSocket.OnClosed += OnClosed;
this.webSocket.OnError += OnError;
// Start connecting to the server
this.webSocket.Open();
// AddText("Connecting...");
}
public void Chat()
{
if (content_input.text == "" || content_input.text == null)
return;
MessageData data = new MessageData();
data.msgType = MessageType.Chat;
data.msg = content_input.text;
string json = JsonUtility.ToJson(data);
webSocket.Send(json);
ChatItem chatItem = (ChatItem)AddText(string.Format(" <color=yellow>我:</color>\n{0}",data.msg));
chatItem.AddLeftPadding(20);
chatItem.SetTextAlignment(TextAnchor.MiddleRight);
}
public void LoginOut()
{
MessageData data = new MessageData();
data.msgType = MessageType.LogOut;
data.msg = "退出";
string json = JsonUtility.ToJson(data);
webSocket.Send(json);
webSocket.Close(1000, "Bye!");
webSocket.OnOpen -= OnOpen;
webSocket.OnMessage -= OnMessageReceived;
webSocket.OnClosed -= OnClosed;
webSocket = null;
AddText("Closing!");
loginGo.SetActive(true);
chatGo.SetActive(false);
}
#region WebSocket Event Handlers
/// <summary>
/// Called when the web socket is open, and we are ready to send and receive data
/// </summary>
void OnOpen(WebSocket.WebSocket ws)
{
AddText("WebSocket Open!");
MessageData data = new MessageData();
data.msgType = MessageType.Login;
data.nickname = name_input.text;
string json = JsonUtility.ToJson(data);
webSocket.Send(json);
loginGo.SetActive(false);
chatGo.SetActive(true);
}
/// <summary>
/// Called when we received a text message from the server
/// </summary>
void OnMessageReceived(WebSocket.WebSocket ws, string message)
{
try
{
MessageData data = JsonUtility.FromJson<MessageData>(message);
AddText(string.Format(" <color=yellow>{0}:</color>\n{1}", data.nickname,data.msg))
.AddLeftPadding(20);
}
catch (System.Exception ex)
{
AddText(string.Format(" <color=yellow>{0}</color>", message))
.AddLeftPadding(20);
}
}
/// <summary>
/// Called when the web socket closed
/// </summary>
void OnClosed(WebSocket.WebSocket ws, UInt16 code, string message)
{
AddText(string.Format("WebSocket closed! Code: {0} Message: {1}", code, message));
webSocket = null;
}
/// <summary>
/// Called when an error occured on client side
/// </summary>
void OnError(WebSocket.WebSocket ws, string error)
{
AddText(string.Format("An error occured: <color=red>{0}</color>", error));
webSocket = null;
}
#endregion
private TextListItem AddText(string text)
{
return GUIHelper.AddText(this._listItemPrefab, this._contentRoot, text, this._maxListItemEntries, this._scrollRect);
}
void OnDestroy()
{
if (this.webSocket != null)
{
this.webSocket.Close();
this.webSocket = null;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace BestHTTP.Examples.Helpers
{
public class ChatItem : TextListItem
{
public void SetTextAlignment(TextAnchor textAnchor){
_text.alignment=textAnchor;
}
}
}
5.UI绑定功能
先复制一个“TextListItem”预制件,插件BestHttp里面有,然后去除它本身的脚本,挂上ChatItem脚本,成为一个新的预制件,供后面使用。
挂载脚本并赋值
?将登陆按钮绑定登陆方法,发送按钮绑定发送方法
?
?最后我们复制两个聊天系统,运行查看结果
?
?打包web同样适用
demo:unitywebsocket聊天室案例-Unity3D文档类资源-CSDN下载
?
|