项目场景:
一个飞屏项目,两台主机使用tcp协议通信。控制端为服务器,发送控制信号,发送图片;大屏端为客户端,接收控制信号展示内容,接收图片并展示。
问题描述
- 只有先打开服务器再打开客户端,才能正确连接通信。
- 图片传输过去是乱码,不能正确显示。
原因分析:
- tcp脚本通信连接相关逻辑是单次连接,没有考虑周全。
- 图片数据很长,客户端接收数据不全。
解决方案:
- 客户端脚本《TcpClient》添加连接失败后等待10秒后重试。
- 服务器发送图片时先发送图片数据长度再发送图片数据,客户端根据图片数据长度接收完整数据,再根据头字节判定是否是图片。
代码示例1:
private async void StartClient()
{
if (!isRunning)
{
Debug.Log("客户端已停止运行,不再尝试连接");
return;
}
Debug.Log("启动客户端" + ConfigMgr.Instance.baseConfig.IP地址);
//服务器地址
IPAddress ip = IPAddress.Parse(ConfigMgr.Instance.baseConfig.IP地址);
IPEndPoint ipEnd = new IPEndPoint(ip, ConfigMgr.Instance.baseConfig.监听端口);
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
bool connected = await ConnectAsync(clientSocket, ipEnd);
if (connected)
{
clientThread = new Thread(ClientThread);
clientThread.Start();
}
else
{
Debug.LogError("连接失败,10秒后重试");
await Task.Delay(10000); // 等待10秒后重试
StartClient();
}
}
private Task<bool> ConnectAsync(Socket socket, IPEndPoint endPoint)
{
return Task.Run(() =>
{
try
{
socket.Connect(endPoint);
return true;
}
catch (Exception ex)
{
Debug.LogError("连接失败: " + ex.Message);
return false;
}
});
}
代码示例2:
《TcpClient.cs》
//接收数据并校验
private byte[] ReceiveDataWithLength(Socket client)
{
// 接收数据长度
byte[] lengthBytes = new byte[sizeof(int)];
int receivedLength = 0;
while (receivedLength < lengthBytes.Length)
{
int bytesReceived = client.Receive(lengthBytes, receivedLength, lengthBytes.Length - receivedLength, SocketFlags.None);
if (bytesReceived == 0)
{
throw new SocketException(); // 连接可能已关闭
}
receivedLength += bytesReceived;
}
int dataLength = BitConverter.ToInt32(lengthBytes, 0);
// 检查数据长度是否合理
if (dataLength < 0 || dataLength > 1024 * 1024 * 10) // 假设最大数据长度为10MB
{
throw new OverflowException("接收到的数据长度不合理");
}
// 接收数据
byte[] data = new byte[dataLength];
receivedLength = 0;
while (receivedLength < dataLength)
{
int bytesReceived = client.Receive(data, receivedLength, dataLength - receivedLength, SocketFlags.None);
if (bytesReceived == 0)
{
throw new SocketException(); // 连接可能已关闭
}
receivedLength += bytesReceived;
}
Debug.Log("接收数据: " + dataLength + "," + data);
return data;
}
if (fileHead == "255216" || fileHead == "13780")
{
//Debug.Log("是图片");
return true;
}
else
{
//Debug.Log("不是图片");
return false;
}
《TcpServer.cs》
// 发送RawImage图片(需要在“纹理导入设置”中使此纹理可读)
public void SendTexture(RawImage rawImage)
{
try
{
// 获取RawImage的Texture2D
Texture2D tex = rawImage.texture as Texture2D;
if (tex == null)
{
Debug.LogError("RawImage的texture不是Texture2D类型");
return;
}
// 获取Texture2D的bytes数据
//byte[] textureBytes = tex.EncodeToPNG();
// 创建一个新的Texture2D来存储未压缩的纹理数据
Texture2D uncompressedTex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
uncompressedTex.SetPixels(tex.GetPixels());
uncompressedTex.Apply();
// 获取未压缩Texture2D的bytes数据
byte[] textureBytes = uncompressedTex.EncodeToJPG();
// 将图片数据发送给客户端
SendDataWithLength(client, textureBytes);
Debug.Log("图片数据长度: " + textureBytes.Length);
}
catch (Exception ex)
{
Debug.LogError("发送图片时出错: " + ex.Message + "\n" + ex.StackTrace);
}
}
private void SendDataWithLength(Socket client, byte[] data)
{
try
{
// 发送数据长度
byte[] lengthBytes = BitConverter.GetBytes(data.Length);
client.Send(lengthBytes);
// 发送数据
int bytesSent = client.Send(data);
if (bytesSent == data.Length)
{
Debug.Log("服务器发送消息成功");
}
else
{
Debug.LogError("服务器发送消息失败");
}
}
catch (Exception ex)
{
Debug.LogError("发送数据时出错: " + ex.Message + "\n" + ex.StackTrace);
}
}