今天跟大家继续学习下socket,由于最近有个招标参数需要给之前的一款产品做教师端以及后台数据库部分,忙了将近两个礼拜,今天刚发布了,就继续我们的socket通信部分。
之前服务端一直在VS中去做,考虑到后面客户端与服务器一体化,就把服务端也在unity中实现了,需要注意的一点是,由于服务端中包含阻塞方法,而unity所有脚本都只在仅存的一个主线程中运行,所以如果直接在start中调用启动服务器的方法,unity会直接卡死的,所以我们就自己起一个线程去搞服务端部分,unity中起线程后要清楚两点:
1、monoBehavior类中的方法尽量别用了,会出现不可预知的bug。
2、自己起的线程,系统不会掺和,自己找个合适的地方再把他kill掉。
下面的代码是在程序结束时杀掉线程,想起以前老谢给我讲的,起线程就像拉屎,完事一定要记得自己擦,系统不会给你擦,经测试不擦第二次启动unity会无响应。
这一次代码里简单实现了从客户端发送一个字符串的包到服务端,服务端接收后并打印出来,代码全部贴出来:
1、服务端部分:
-
using UnityEngine;
-
using System.Collections;
-
using System.Net;
-
using System.Net.Sockets;
-
using System.Text;
-
using System.Threading;
-
using System;
-
/// <summary>
-
/// scoket服务器监听端口脚本
-
/// </summary>
-
public class SocketServer : MonoBehaviour
-
{
-
private Thread thStartServer;//定义启动socket的线程
-
void Start()
-
{
-
thStartServer = new Thread(StartServer);
-
thStartServer.Start();//启动该线程
-
}
-
void Update()
-
{
-
}
-
private void StartServer()
-
{
-
const int bufferSize = 8792;//缓存大小,8192字节
-
IPAddress ip = IPAddress.Parse("192.168.0.13");
-
TcpListener tlistener = new TcpListener(ip, 10001);
-
tlistener.Start();
-
Debug.Log("Socket服务器监听启动......");
-
TcpClient remoteClient = tlistener.AcceptTcpClient();//接收已连接的客户端,阻塞方法
-
Debug.Log("客户端已连接!local:" + remoteClient.Client.LocalEndPoint + "<---Client:" + remoteClient.Client.RemoteEndPoint);
-
NetworkStream streamToClient = remoteClient.GetStream();//获得来自客户端的流
-
do
-
{
-
try //直接关掉客户端,服务器端会抛出异常
-
{
-
//接收客户端发送的数据部分
-
byte[] buffer = new byte[bufferSize];//定义一个缓存buffer数组
-
int byteRead = streamToClient.Read(buffer, 0, bufferSize);//将数据搞入缓存中(有朋友说read()是阻塞方法,测试中未发现程序阻塞)
-
if (byteRead == 0)//连接断开,或者在TCPClient上调用了Close()方法,或者在流上调用了Dispose()方法。
-
{
-
Debug.Log("客户端连接断开......");
-
break;
-
}
-
string msg = Encoding.Unicode.GetString(buffer, 0, byteRead);//从二进制转换为字符串对应的客户端会有从字符串转换为二进制的方法
-
Debug.Log("接收数据:" + msg + ".数据长度:[" + byteRead + "byte]");
-
}
-
catch(Exception ex)
-
{
-
Debug.Log("客户端异常:"+ex.Message);
-
break;
-
}
-
}
-
while (true);
-
}
-
void OnApplicationQuit()
-
{
-
thStartServer.Abort();//在程序结束时杀掉线程,想起以前老谢给我讲的,起线程就像拉屎,完事一定要记得自己擦,系统不会给你擦,经测试不擦第二次启动unity会无响应
-
}
-
}
2、客户端部分:
-
using UnityEngine;
-
using System.Collections;
-
using System.Net;
-
using System.Net.Sockets;
-
using System;
-
using System.Text;
-
public class SocketClient : MonoBehaviour
-
{
-
void Start()
-
{
-
}
-
void Update()
-
{
-
if (Input.GetKeyDown(KeyCode.Alpha1))
-
{
-
Client();
-
}
-
if (Input.GetKeyDown(KeyCode.Alpha2))
-
{
-
SimulateMultiUserClient();
-
}
-
}
-
TcpClient client;
-
private void Client()
-
{
-
client = new TcpClient();
-
try
-
{
-
client.Connect(IPAddress.Parse("192.168.0.13"), 10001);//同步方法,连接成功、抛出异常、服务器不存在等之前程序会被阻塞
-
}
-
catch (Exception ex)
-
{
-
Debug.Log("客户端连接异常:" + ex.Message);
-
}
-
Debug.Log("LocalEndPoint = " + client.Client.LocalEndPoint + ". RemoteEndPoint = " + client.Client.RemoteEndPoint);
-
//客户端发送数据部分
-
for (int i = 0; i < 2; i++)
-
{
-
try
-
{
-
string msg = "hello server i am No " + i;
-
NetworkStream streamToServer = client.GetStream();//获得客户端的流
-
byte[] buffer = Encoding.Unicode.GetBytes(msg);//将字符串转化为二进制
-
streamToServer.Write(buffer, 0, buffer.Length);//将转换好的二进制数据写入流中并发送
-
Debug.Log("发出消息:" + msg);
-
}
-
catch(Exception ex)
-
{
-
Debug.Log("服务端产生异常:"+ex.Message);
-
}
-
}
-
}
-
private void SimulateMultiUserClient()
-
{
-
for (int i = 0; i < 2; i++)
-
{
-
client = new TcpClient();
-
try
-
{
-
client.Connect(IPAddress.Parse("192.168.0.13"), 10001);//连接到服务器端
-
}
-
catch (Exception ex)//抛出连接错误的异常
-
{
-
Debug.Log("客户端连接异常:" + ex.Message);
-
}
-
Debug.Log("LocalEndPoint = " + client.Client.LocalEndPoint + ". RemoteEndPoint = " + client.Client.RemoteEndPoint);
-
//客户端发送数据部分
-
string msg = "hello server i am No " + i;
-
NetworkStream streamToServer = client.GetStream();//获得客户端的流
-
byte[] buffer = Encoding.Unicode.GetBytes(msg);//将字符串转化为二进制
-
streamToServer.Write(buffer, 0, buffer.Length);//将转换好的二进制数据写入流中并发送
-
Debug.Log("发出消息:" + msg);
-
}
-
}
-
private void SendMsgToServer(string content)
-
{
-
}
-
}