先看看效果图
以上分别是两个客户端相互通讯的情况的。
源码:https://github.com/zymix/Unity_ChatSystem
C#本身对Socket拥有着高度的封装,所以搭建这样的一个多人聊天系统是非常容易的事情。这里先提醒几点:
1.关于Unity内部编码问题,Unity在debug阶段时其编码模式是跟操作系统一致的,但是当其发布以后,由于Unity的跨平台性使其编码改变成UTF8的形式,所以无论哪个阶段,都应该注意客户端与服务器段的编码模式一致。
2.关于Scroll View的问题,想滚动条动态的改变滚动长度需要,动态修改Scroll Rect组件的Content对象的RectTransform的deltaSize
剩下的,看注释,算是比较清晰了
客户端代码
using UnityEngine;
using System.Net.Sockets;
using System;
using System.Collections.Generic;
using UnityEngine.UI;
using System.Collections;
public class ChatClient : MonoBehaviour
{
public GameObject content;
public InputField input; //用于发送输入框的信息
public InputField usernameInput;//客户端用户名
public GameObject messageItem;//预制件的宽高
public Button connectButton; //连接按钮
TcpClient client;//客户端
NetworkStream nstream;//网络数据流
string ip = "192.168.10.5";//客户端IP
int port = 10086;//客户端端口
byte[] data;//客户端收发数据
bool isConnect = false;
List<GameObject> messageList;//存储接受的消息列表
public int messagesMaxSize = 20;//存储接受的消息列表最大长度
bool isNewMessage = false;//消息更新的标志位
string newMessage;//接受到的最新信息
//预制件的宽高
float height = 0;
float width = 0;
void Start()
{
messageList = new List<GameObject>();
messageList.Capacity = messagesMaxSize;
//获取预设件的宽高
width = messageItem.GetComponent<RectTransform>().rect.width;
height = messageItem.GetComponent<RectTransform>().rect.height;
//初始化组件
if (content == null)
content = transform.FindChild("Chat View/Viewport/Content").gameObject;
if (input == null)
input = transform.FindChild("InputField").GetComponent<InputField>();
if (usernameInput == null)
usernameInput = transform.FindChild("UsernameInput").GetComponent<InputField>();
if (connectButton == null)
connectButton = transform.FindChild("ConnectButton").GetComponent<Button>();
//给连接按钮添加OnConnect事件,控制链接开启和关闭
connectButton.onClick.AddListener(delegate { OnConnect(); });
}
void Update()
{
if (isNewMessage)
{//若存在新的消息,则添加消息更新界面
AddMessage(newMessage);
isNewMessage = false;
}
}
public void OnDestroy()
{
Disconnect();
}
//连接按钮的点击事件调用Onconnect连接函数
public void OnConnect()
{
isConnect = !isConnect;
if (isConnect)
{
connectButton.transform.FindChild("Text").GetComponent<Text>().text = "断开连接";
Connect();
}
else
{
connectButton.transform.FindChild("Text").GetComponent<Text>().text = "连接服务器";
Disconnect();
}
}
//中断网络连接
void Disconnect()
{
//关闭流和客户端
if(nstream !=null&&(nstream.CanRead|| nstream.CanWrite))
nstream.Close();
if (nstream != null && client.Connected)
client.Close();
}
//网络连接
void Connect()
{
Info.debugStr = "";
if ("" == usernameInput.text)
{
Info.debugStr = DateTime.Now + "->用户名不能为空";
return;
}
try
{
//创建TCP连接
client = new TcpClient(ip, port);
nstream = client.GetStream();
//先发送用户名
SendMessage(usernameInput.text);
//初始化收发数据
data = new byte[client.ReceiveBufferSize];
//开启异步从服务器获取消息,获取到的数据流存入data,回调ReceiveMessage方法
nstream.BeginRead(data, 0, data.Length, AsyncReceive, null);
}
catch (SocketException socketEx)
{//输出错误码
Info.debugStr = DateTime.Now + "->" + socketEx.ErrorCode + ": " + socketEx.Message;
//关闭流和客户端
client.Close();
}
catch (Exception e)
{ //显示错误信息
Info.debugStr = DateTime.Now + "->" + e.Message;
//断开连接
Disconnect();
}
}
//发送消息到服务器
public void Send()
{
Info.debugStr = "";
if ("" == input.text)
{
Info.debugStr = DateTime.Now + "->消息不能为空";
return;
}
//发送消息到服务器
SendMessage(input.text);
//清空输入框
input.text = "";
}
new public void SendMessage(string message)
{
try
{ //把输入框的消息写入数据流,发送服务器
//NetworkStream stream = client.GetStream();
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(message);
nstream.Write(bytes, 0, bytes.Length);
}
catch (Exception e)
{
//显示错误信息
Info.debugStr = DateTime.Now + "->" + e.Message;
//断开连接
Disconnect();
}
}
//异步处理从服务器获得的消息
void AsyncReceive(IAsyncResult ar)
{
Info.debugStr = "";
try
{
int bytesRead = client.GetStream().EndRead(ar);
if (bytesRead < 1)
{ //接收不到任何消息
return;
}
else
{
//把接收到的消息编码为UTF8,跟Unity发布后编码方式一致
newMessage = System.Text.Encoding.UTF8.GetString(data, 0, bytesRead);
//设置标志位,在UI上添加一条消息
isNewMessage = true;
}
//再次开启异步从服务器获取消息,形成循环等待服务器消息
nstream.BeginRead(data, 0, client.ReceiveBufferSize, AsyncReceive, null);
}
catch (Exception e)
{
Info.debugStr = DateTime.Now + "->" + e.Message;
//断开连接
Disconnect();
}
}
//在UI上添加一条消息
private void AddMessage(string uiMessage)
{
//把接收到的消息存入消息列表中
GameObject item = Instantiate<GameObject>(messageItem);
item.GetComponent<Text>().text = uiMessage;
//超出列表上限时,再删除第一条
if (messageList.Count > messageList.Capacity)
messageList.RemoveAt(0);
messageList.Add(item);
//刷新界面
ShowMessages();
}
private void ShowMessages()
{
int i = 0;
foreach (GameObject m in messageList)
{
//设置为显示内容面板的子对象,便于位置的设置位置
m.transform.SetParent(content.transform, false);
//按序设置位置
RectTransform rf = m.GetComponent<RectTransform>();
rf.sizeDelta = new Vector2(width, height);
rf.localPosition = new Vector2(0, -i * height);
++i;
}
content.GetComponent<RectTransform>().sizeDelta = new Vector2(width, i * height);
}
}
简单的负责debug的提示信息类
using UnityEngine;
using System.Collections;
public class Info : MonoBehaviour {
public static string debugStr = "";
void OnGUI() {
GUILayout.Label(debugStr);
}
}
服务器端的代码
主程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ChatSystem
{
class Program
{
static void Main(string[] args)
{
//设置监听的端口号
Console.WriteLine("请输入服务器需要监听的端口: ");
string input = Console.ReadLine();
int port = int.Parse(input);
//调用方法启动服务器
Server(port);
}
static void Server(int port)
{
//初始化服务器ip
//IPAddress localAddress = IPAddress.Parse("127.0.0.1");
//设置监听
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
//提示信息
Console.WriteLine("{0:HH:mm:ss}->监听端口{1}....", DateTime.Now, port);
//循环等待客户端的连接请求
while(true)
{
ChatServerHandle usr = new ChatServerHandle(listener.AcceptTcpClient());
Console.WriteLine(usr.ip + "加入聊天室");
}
}
}
}
服务器辅助类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
namespace ChatSystem
{
class ChatServerHandle
{
public static Hashtable Clients = new Hashtable();//客户端连接记录表
public TcpClient client;//客户端
public string username;//客户端用户名
public string ip;//客户端IP
public int port;//客户端端口
public byte[] data;//客户端收发数据
bool firstConnet = true;
public ChatServerHandle(TcpClient client)
{
this.client = client;
//保存客户端IP和端口
IPEndPoint ipEndPoint = client.Client.RemoteEndPoint as IPEndPoint;
this.ip = ipEndPoint.Address.ToString();
this.port = ipEndPoint.Port;
//初始化收发数据
this.data = new byte[client.ReceiveBufferSize];
//开启异步从客户端获取消息,获取到的数据流存入data,回调ReceiveMessage方法
client.GetStream().BeginRead(data, 0, data.Length, ReceiveMessage, null);
}
//向客户端发送消息
public void SendMessage(string message)
{
try
{
NetworkStream stream;
lock(client.GetStream())
{
stream = client.GetStream();
}
byte[] bytes = Encoding.UTF8.GetBytes(message); //注意Unity发布后程序已UTF8编码
stream.Write(bytes, 0, bytes.Length);
//stream.Flush();
}
catch (Exception e)
{
Console.WriteLine("{0:HH:mm:ss}->[SendMessage]Error: {1}", DateTime.Now, e.Message);
}
}
//从客户端接受到的数据,再广播给所有客户端
public void ReceiveMessage(IAsyncResult ar)
{
try
{
int bytesRead;
lock(client.GetStream())
{
bytesRead = client.GetStream().EndRead(ar);
}
if (bytesRead < 1)
{//接收不到任何数据,则删除在客户端连接记录表
Clients.Remove(this.ip);
//向所有客户端广播该用户的下线信息
Broadcast("<color=#00ffffff>"+this.username + "</color> 于" + DateTime.Now + " 已下线....");
return;
}
else
{
string recMessage = Encoding.UTF8.GetString(data, 0, bytesRead); //注意Unity发布后程序已UTF8编码
if (firstConnet)
{ //检查是不是同一台电脑重复连接
if(Clients.ContainsKey(this.ip))
return;
//第一次连接,将客户端信息记录入客户端连接记录表中
Clients.Add(this.ip,this);
//获取发送的用户名信息
this.username = recMessage;
//向所有客户端广播该用户的上线信息
Broadcast("<color=#00ffffff>" + this.username + "</color> 于" + DateTime.Now+ " 已上线....");
//不在是第一次连接
firstConnet = false;
}
else
{
//向所有客户端广播该用户发送的
Broadcast(DateTime.Now + " ->【"+ this.username+"】" + recMessage);
}
//Console.WriteLine(recMessage);
}
lock(client.GetStream())
{
//再次开启异步从服务器获取消息,形成循环
client.GetStream().BeginRead(data, 0, client.ReceiveBufferSize, ReceiveMessage, null);
}
}
catch(Exception e)
{
//删除连接记录
Clients.Remove(this.ip);
//向所有客户端广播该用户的下线信息
Broadcast("<color=#00ffffff>" + this.username + "</color> 于" + DateTime.Now + " 已下线....");
Console.WriteLine("{0:HH:mm:ss}->[ReceiveMessage]Error: {1}", DateTime.Now, e.Message);
}
}
//向所有连接到的客户端广播
public void Broadcast(string message)
{
Console.WriteLine(message);
foreach (DictionaryEntry item in Clients)
{
ChatServerHandle c = item.Value as ChatServerHandle;
c.SendMessage(message);
}
}
}
}
版权声明:本文为u012325397原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。