前言
- 通过几个图可以很好的总结编程逻辑
https://www.cnblogs.com/dream844/archive/2012/06/12/2546083.html - 这个博客总结的很好,但是有些内容我不是很理解,以后遇到再去研究好了
- https://www.cnblogs.com/jikexianfeng/p/6413262.html 介绍什么是DMA
- 异步是目的,为了避免某个耗时的操作阻塞当前主程序的运行。
- 通过创建一个子线程去执行耗时操作,在操作完之后通知主程序
- DMA功能可以不依赖CPU来进行读取到内存中,因此在读取完成后利用回调通知程序即可。这种方式没有创建线程
TCPSocket通信
- Server端
- 创建终端(指定ip和端口号)
- 创建服务端socket 指定协议TCP, 数据为流方式,用IPV4,
- socket绑定终端
- 开始监听(此时会发生阻塞,如果没有监听到,就不会向下运行)
- 接受到一个连接的请求时,会返回创建一个clientsock连接来处理与该客户端的通信,然后继续监听
- 服务端代码
注意事项
- 如果客户端断开连接,服务端会持续接收空字符串,因此在接收之前需要做判断
- 还需要处理可能出现的异常,然后关闭连接,释放资源
- 合理关闭连接的方式——先shutdown,再close 因为直接close会导致资源被直接释放,应该先不再接收,和不再发送,然后关闭
class Program
{
static List<Client> clientList = new List<Client>();
static void Main(string[] args)
{
//创建服务器端的socket
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定端口
server.Bind(new IPEndPoint(IPAddress.Parse("192.168.0.106"), 8080));
//开始监听
server.Listen(100);
Console.WriteLine("server is running");
while (true)
{
Socket clientSockrt = server.Accept();
Console.WriteLine("a client is connected");
Client client = new Client(clientSockrt); //把与每个客户端通信的逻辑(封装到client类里面进行处理)
clientList.Add(client);
}
}
//广播消息
public static void SendAllClient(byte[] data, int length)
{
var notConnectedList = new List<Client>();
for (int i = 0; i < clientList.Count; i++)
{
if (clientList[i].Connected != false)
{
clientList[i].SendMessageToClient(data, length);
}
else
{
notConnectedList.Add(clientList[i]);
}
}
foreach (var i in notConnectedList)
{
clientList.Remove(i);
}
}
}
- 处理与客户端通信的类即客服,
/// <summary>
///创建与客户端进行通信的类
/// </summary>
class Client
{
private Socket clientSocket;
private Thread t;
private byte[] data = new byte[1024];
private int length;
public Client(Socket s)
{
clientSocket = s;
//启动一个线程来处理客户端的数据接收
t = new Thread(ReceiveMessage);
t.Start();
}
//接收消息
private void ReceiveMessage()
{
//一直接收客户端的数据
while (true)
{
//在接受数据之前判断一下socket连接是否断开
//判断是否响应 可以从客户端读取到东西
//返回为true, 则是断开连接的意思
if (clientSocket.Poll(10, SelectMode.SelectRead))
{
clientSocket.Close();
break; //跳出循环,终止线程的执行
}
length = clientSocket.Receive(data);
string message = Encoding.UTF8.GetString(data, 0, length);
Console.WriteLine("从客户端发来一条消息:"+message);
Program.SendAllClient(data, length);
}
}
//向客户端发送数据的方法
public void SendMessageToClient(byte[] data, int length)
{
clientSocket.Send(data, length, SocketFlags.None);
}
public bool Connected
{
get { return clientSocket.Connected; }
}
}
-
Client端
- 创建客户端socket
- 创建服务端终端的实例
- 使用该socket向指定终端发起连接
- 通过socket.send() 方法发送数据
- 通过socket.receive()方法来接收数据
-
这是一个网络聊天室的客户端
-
由于unity不允许在单独一个线程里面操纵UI元素,所以需要额外创建一个变量message
public class ChatControl : MonoBehaviour {
public Text content;
public Text panelText;
private Socket client;
private byte[] buffer = new byte[1024];
private string message;
// Use this for initialization
void Start () {
content.text = "lallallalal";
CreateConnection();
}
// Update is called once per frame
void Update () {
if (message!=null&&message!="")
{
ShowMessageInPanel(message);
message = "";
}
}
//创建连接与服务器
private void CreateConnection()
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建与服务端连接
client.Connect(new IPEndPoint(IPAddress.Parse("192.168.0.106"), 8080));
Thread t = new Thread(GetMessageFromServer);
t.Start();
}
//发送消息给服务端
private void SendMessageToServer(string message)
{
Debug.Log("message:" + message);
byte[] data = Encoding.UTF8.GetBytes(message); //得到数据字节码
client.Send(data);
}
//从服务器得到数据
private void GetMessageFromServer()
{
//创建缓冲数组
while (true)
{
if (client.Connected == false)
{
break;
}
int n = client.Receive(buffer);
string getMes = Encoding.UTF8.GetString(buffer, 0, n);
message = getMes;//unity 并不允许在单独的线程里面操纵一个组件
}
}
//当按钮被点击发送的时候
public void OnSendSomething()
{
//获取content的内容
string sm = content.text;
//进行数据校验
string valid = CheckMessageValid(sm);
if (valid != "")
{
SendMessageToServer(valid);
content.text = "";
}
}
//检测信息的有效性
private string CheckMessageValid(string message)
{
string valid = message.TrimEnd();
return valid;
}
//将数据显示在面板UI上
private void ShowMessageInPanel(string message)
{
panelText.text = panelText.text + "\n" + message;
}
private void OnDestroy()
{
//正确关闭socket连接的方法
client.Shutdown(SocketShutdown.Both);
client.Close();
}
}
UDPSocket通信
- Server端
- 创建终端(指定ip和端口号)
- 创建服务端socket 指定协议UDP, 数据为数字报方式,用IPV4,
- socket绑定终端
- 直接开始接受数据
- Client端
- 创建客户端socket
- 创建服务端终端的实例
- 直接向指定终端发送数据
- 注意到UDP是没有连接的