之前做的画面传输功能,点击一次就重新加载一次,对于几十kb的图像加载起来很快,感觉不到什么延迟。那时候没有考虑到图像占用大的问题,经过测试后发现问题还是蛮大的,每次点击后都要重新去加载一次图像,即使图像已经循环了一次。当需要加载的图像较大时,点击后就需要等图像加载完,然后才能更新UI,这时候如果点击了两次,就会导致跳过了上一张图像。
想了一下,发现要解决这个问题也挺简单的,只要通过程序开始运行的时候将所有的图像加载出来,存储在数组里,客户端传输当前显示的图片索引给服务端,服务端在通过索引加载数组里对应的图像即可,这样不就避免了每次都要加载的问题。另外加上了一个文本,用于设置该程序最多可以加载几张图片,就不再程序里写死了。
但我估摸,如果在程序里放很多图片的话,运行的那一刻会卡很久。这时应该考虑先加载一部分图像,然后在开一个子线程去预加载出剩余的图像到数组内。
附上代码
服务端
public class SocketServer : MonoBehaviour
{
[SerializeField] private Image image;
// 控制图片加载
private bool isSuccessGetStr;
// 客户端发送的索引
private int getClientSendMes;
// ip文本的路径
private string ipTxtPath = Application.streamingAssetsPath + "/IPAddress.txt";
// 需要加载的精灵
private Sprite[] showSprites;
// 读取文本中图片数量
private int spriteCountTxt;
// 图片数量路径
private string countTxtPath = Application.streamingAssetsPath + "/ImageCount.txt";
void Awake()
{
// 获取所需加载的图片数量
spriteCountTxt = int.Parse(File.ReadAllText(countTxtPath));
// 根据需要实例数组
showSprites = new Sprite[spriteCountTxt];
}
void Start()
{
LoadImage();
StartServer();
}
void Update()
{
// 在主线程进行UI更新
if (isSuccessGetStr)
{
isSuccessGetStr = !isSuccessGetStr;
image.sprite = showSprites[getClientSendMes];
}
}
/// <summary>
/// 监听客户端发起连接
/// </summary>
private void StartServer()
{
try
{
int _port = 8888;
// string _ip = "127.0.0.1";
string _ip = File.ReadAllText(ipTxtPath);
//点击开始监听时 在服务端创建一个负责监听IP和端口号的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(_ip);
//创建对象端口
IPEndPoint point = new IPEndPoint(ip, _port);
socketWatch.Bind(point);//绑定端口号
Debug.Log("监听成功!");
socketWatch.Listen(10);//设置监听,最大同时连接10台
//创建监听线程
Thread thread = new Thread(Listen);
thread.IsBackground = true;
thread.Start(socketWatch);
}
catch { }
}
/// <summary>
/// 等待客户端的连接 并且创建与之通信的Socket
/// </summary>
Socket socketSend;
void Listen(object o)
{
try
{
Socket socketWatch = o as Socket;
while (true)
{
socketSend = socketWatch.Accept();//等待接收客户端连接
Debug.Log(socketSend.RemoteEndPoint.ToString() + ":" + "连接成功!");
//开启一个新线程,执行接收消息方法
Thread r_thread = new Thread(Received);
r_thread.IsBackground = true;
r_thread.Start(socketSend);
}
}
catch { }
}
/// <summary>
/// 服务器端不停的接收客户端发来的消息
/// </summary>
/// <param name="o"></param>
void Received(object o)
{
try
{
Socket socketSend = o as Socket;
while (true)
{
//客户端连接服务器成功后,服务器接收客户端发送的消息
byte[] buffer = new byte[1024 * 1024 * 3];
//实际接收到的有效字节数
int len = socketSend.Receive(buffer);
if (len == 0)
{
break;
}
string str = Encoding.UTF8.GetString(buffer, 0, len);
getClientSendMes = int.Parse(str);
// image.sprite = showSprites[getClientSendMes]; // 同样对UI操作写在网络线程时,发生阻塞
isSuccessGetStr = true;
// SendMesToClient("我收到了" + str);
}
}
catch { }
}
/// <summary>
/// 服务器向客户端发送消息
/// </summary>
/// <param name="str"></param>
void SendMesToClient(string str)
{
byte[] buffer = Encoding.UTF8.GetBytes(str);
socketSend.Send(buffer);
}
/// <summary>
/// 加载精灵
/// </summary>
/// <param name="path"></param>
private void LoadImage()
{
for (int i = 0; i < spriteCountTxt; i++)
{
string path = Application.streamingAssetsPath + "/" + i.ToString() + ".jpg";
byte[] fileData = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(fileData);
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
// image.sprite = sprite;
showSprites[i] = sprite;
}
}
}
客户端
public class SocketClient : MonoBehaviour
{
[SerializeField] private Button sendMesBtn;
private string ipTxt_Path = Application.streamingAssetsPath + "/IPAddress.txt";
private string stringIP;
void Awake()
{
// 协程同步进行,导致主线程要使用Ip的时候,协程还没拿到Ip数据
StartCoroutine(Get_IPTxTFileBytes(ipTxt_Path));
// SendConnect();
}
void Start()
{
// 放到后一帧调用
SendConnect();
SendMesToServer();
sendMesBtn.onClick.AddListener(() =>
{
SendMsg();
});
}
public void SendMsg()
{
SendMesToServer();
}
Socket socketSend;
/// <summary>
/// 向服务端发起连接
/// </summary>
private void SendConnect()
{
try
{
int _port = 8888;
//创建客户端Socket,获得远程ip和端口号
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(stringIP);
IPEndPoint point = new IPEndPoint(ip, _port);
socketSend.Connect(point);
Debug.Log("连接成功!");
//开启新的线程,不停的接收服务器发来的消息
Thread c_thread = new Thread(Received);
c_thread.IsBackground = true;
c_thread.Start();
}
catch (Exception)
{
Debug.Log("IP或者端口号错误...");
}
}
/// <summary>
/// 接收服务端返回的消息
/// </summary>
void Received()
{
while (true)
{
try
{
byte[] buffer = new byte[1024 * 1024 * 3];
//实际接收到的有效字节数
int len = socketSend.Receive(buffer);
if (len == 0)
{
break;
}
string str = Encoding.UTF8.GetString(buffer, 0, len);
Debug.Log("客户端打印:" + socketSend.RemoteEndPoint + ":" + str);
}
catch { }
}
}
/// <summary>
/// 向服务器发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SendMesToServer()
{
try
{
// 发送当前的索引,服务端根据索引加载精灵
string msg = ShowImage.instance.Getindex.ToString();
byte[] buffer = new byte[1024 * 1024 * 3];
buffer = Encoding.UTF8.GetBytes(msg);
socketSend.Send(buffer);
}
catch { }
}
/// <summary>
/// 请求文本IP
/// </summary>
/// <param name="URL"></param>
/// <returns></returns>
/// 安卓端不能进行IO读写,只能通过请求的方式读写
private IEnumerator Get_IPTxTFileBytes(string URL)
{
using (UnityWebRequest requestGet = UnityWebRequest.Get(URL))
{
yield return requestGet.SendWebRequest();
if (requestGet.isHttpError || requestGet.isNetworkError)
{
Debug.Log(requestGet.error);
}
else
{
stringIP = requestGet.downloadHandler.text;
Debug.Log("请求完成,获取IP数据 :" + stringIP);
}
}
}
}
客户端更新UI
Touch 和请求的方法是由于之前考虑安卓端的使用,PC端的话使用请求的方式与IO操作都可以。
public class ShowImage : MonoBehaviour
{
public static ShowImage instance { get; private set; }
[SerializeField] private Button clickBtn;
[SerializeField] private Image showImage;
private int index = 0;
/// <summary>
/// 当前图片索引
/// </summary>
public int Getindex { get { return index; } }
// 存储加载的精灵
private Sprite[] showSprites;
// 读取文本中图片数量
private int spriteCountTxt;
// 图片数量文本路径
private string countTxtPath = Application.streamingAssetsPath + "/ImageCount.txt";
void Awake()
{
instance = this;
// 获取所需加载的图片数量
spriteCountTxt = int.Parse(File.ReadAllText(countTxtPath));
showSprites = new Sprite[spriteCountTxt];
// StartCoroutine(GetFileBytes());
LoadImage();
showImage.sprite = showSprites[index];
}
void Start()
{
clickBtn.onClick.AddListener(() =>
{
index++;
// 根据本地配置的图片数量进行循环
if (index > spriteCountTxt - 1)
{
index -= spriteCountTxt;
}
showImage.sprite = showSprites[index];
});
}
void Update()
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
OnScreenTouched();
}
}
private void OnScreenTouched()
{
index++;
if (index > spriteCountTxt - 1)
{
index -= spriteCountTxt;
}
showImage.sprite = showSprites[index];
}
/// <summary>
/// 运行时先加载所有精灵
/// </summary>
/// <param name="path"></param>
private void LoadImage()
{
for (int i = 0; i < spriteCountTxt; i++)
{
string path = Application.streamingAssetsPath + "/" + i.ToString() + ".jpg";
byte[] fileData = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(fileData);
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
showSprites[i] = sprite;
}
}
/// <summary>
/// 请求文件数据流
/// </summary>
/// <param name="URL"></param>
/// <returns></returns>
private IEnumerator GetFileBytes()
{
for (int i = 0; i < spriteCountTxt; i++)
{
String path = Application.streamingAssetsPath + "/" + i.ToString() + ".jpg";
using (UnityWebRequest requestGet = UnityWebRequest.Get(path))
{
yield return requestGet.SendWebRequest();
if (requestGet.isHttpError || requestGet.isNetworkError)
{
Debug.Log(requestGet.error);
}
else
{
byte[] data = requestGet.downloadHandler.data;
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(data);
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
showSprites[i] = sprite;
}
}
}
}
}
写得不好的地方还请大家指正,感谢。