Socket(套接字)网络编程流程简介
TCP 协议:
使用TCP协议通讯需要具备以下几个条件:
1.建立一个套接字(Socket)
2.绑定服务器端的IP地址以及端口号
3.利用Listen()方法开启监听
4.利用Accept()方法尝试与客户端建立一个连接
5.利用Connect()方法与服务器建立连接---客户端
6.利用Send()方法互相发送消息
7.利用Recive()方法互相接收消息
UDP协议
UDP协议是无连接模式通用,占用资源少,响应速度快,延时低,但是无法保证数据完整性,属于不可靠连接,但是其可靠性可以通过应用层的控制、对数据进行判断等方式来满足。
1.建立一个套接字(socket)
2.绑定服务器的端口号和IP
3.通过SendTo()方法向主机发送消息,需要提供主机的端口号和IP
4.通过ReciveFrom()方法接收指定主机发送的消息,需要提供主机IP和端口号
TCP协议与UDP协议的区别:
1.TCP协议需要建立连接; UDP连接无需建立连接,UDP结构比较简单。
2.TCP协议数据类型是以字节流的形式传输:SocketType.Stream;
UDP协议数据类型以数据报的形式传输:SocketType.Dgram。
3.TCP保证数据的正确性、数据顺序等,具有可靠性,但是耗费资源大
UDP可能会丢包、不保证数据顺序、不具有可靠性,耗费资源小、效率高
TCP服务端简单代码:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
namespace Server
{
public class Program
{
private static Func write;
public static Socket SocketServer { get; set; }
public static List clients { get; set; }
public static List notConnectedList { get; set; }
static void Main(string[] args)
{
write = WriteMessage;
write.BeginInvoke("写入", WriteEnd, write);
clients = new List();
notConnectedList = new List();
SocketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketServer.Bind(new IPEndPoint(IPAddress.Parse("192.168.199.128"), 8888));
SocketServer.Listen(100);
Console.WriteLine("服务器启动成功。");
while (true)
{
Socket clientSocket = SocketServer.Accept();
Client client = new Client(clientSocket); //把与每个客户端进行通信的逻辑放在Client类中进行处理
clients.Add(client);
Console.WriteLine("客户端连接成功,客户端数量:" + clients.Count);
}
}
public static void BroadMassage(Client self, string mesage)
{
foreach (var client in clients)
{
if (client != self)
if (client.IsConnected)
{
client.SendMessage(mesage);
}
else
{
notConnectedList.Add(client);
}
}
ClineClient();
}
public static void BroadMassage(string mesage)
{
foreach (var client in clients)
{
if (client.IsConnected)
{
client.SendMessage(mesage);
}
else
{
notConnectedList.Add(client);
}
}
ClineClient();
}
public static void ClineClient()
{
foreach (var item in notConnectedList)
{
if (clients.Contains(item))
clients.Remove(item);
Console.WriteLine("客户端断开连接,客户端数量:" + clients.Count);
}
notConnectedList.Clear();
}
///
/// 服务器写入
///
///
///
public static string WriteMessage(string temp)
{
string message = Console.ReadLine();
write.BeginInvoke("写入", WriteEnd, write);
return message;
}
public static void WriteEnd(System.IAsyncResult result)
{
Func func = (System.Func)result.AsyncState;
string message = func.EndInvoke(result);
Console.WriteLine("写好的消息:" + message);
Program.BroadMassage(message);
}
}
}
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Server
{
///
/// 用来和服务端做通信
///
public class Client
{
public bool IsConnected { get { return clientSocket.Connected; } }
private Socket clientSocket;
private Thread thread;
private byte[] data = new byte[1024];
public Client(Socket client)
{
clientSocket = client;
thread = new Thread(MyResaveMessage);
thread.IsBackground = true;
thread.Start();
}
///
/// 发送消息
///
///
public void SendMessage(string message)
{
try
{
byte[] tempData = new byte[1024];
tempData = Encoding.UTF8.GetBytes(message);
clientSocket.Send(tempData);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
///
/// 接收消息
///
public void MyResaveMessage()
{
while (true)
{
//在接受数据之前,判断连接是否断开
if (clientSocket.Poll(100, SelectMode.SelectRead))
{
clientSocket.Close();
break;
}
int lenth = clientSocket.Receive(data);
string massage = Encoding.UTF8.GetString(data, 0, lenth);
//接收到数据的时候,将数据发送到所有客户端
Program.BroadMassage(this, massage);
}
}
}
}
TCP协议客户端的简单代码
其中UIControl是用来写发送消息逻辑的,可以删除写自己的逻辑
using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
///
/// socket客户端
///
public class SocaketClientTCP : MonoBehaviour
{
public static Action resiveMessage;
public string IP = "192.168.199.128";
public short port = 8888;
Thread task1;
private Socket socketClient;
public void StartConnectingToTheServer()
{
//通过UI控制类委托来监听是否需要发送消息,可以写入自己的发送逻辑
UIControl.scendMessage += MySendMessage;
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//以下代码可以进行简化
IPAddress iPAddress = IPAddress.Parse(IP);
EndPoint endPoint = new IPEndPoint(iPAddress, port);
socketClient.Connect(endPoint);
//传递一个需要线程去执行的方法,设置后台线程,在主线程关闭后,后台线程自动关闭
task1 = new Thread(MyResaveMessage) {IsBackground=true};
task1.Start();
}
///
/// 发送消息
///
public void MySendMessage(string message)
{
byte[] data = Encoding.UTF8.GetBytes(message);
try { socketClient.Send(data); }
catch (Exception e) { Debug.Log("消息发送失败" + e); }
}
///
/// 接收消息
///
public void MyResaveMessage()
{
byte[] data = new byte[1024];
while (true)
{
// 通过这种方式判断是否断开连接比较可靠: socketClient.Poll(100, SelectMode.SelectRead);
//判断当前连接是否断开
if (!socketClient.Connected)
break;
int lenth = socketClient.Receive(data);
string massage = Encoding.UTF8.GetString(data, 0, lenth);
//将接收到的信息通过委托发送出去
resiveMessage?.Invoke(massage);
}
}
private void Start()
{
StartConnectingToTheServer();
}
private void OnDestroy()
{
task1.Abort();
socketClient.Shutdown(SocketShutdown.Both);
socketClient.Close();
}
}
UDP协议接收消息的简单代码:由于UDP没有服务端和客户端的具体区分,所以写成两个部分来表示数据的发送和接收
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace ServerUDP
{
class Program
{
public static string IP = "192.168.199.128";
public static int Port = 8888;
private static Socket socketServerUDP;
static void Main(string[] args)
{
//1.创建Socket
socketServerUDP = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//2.绑定IP和端口号
socketServerUDP.Bind(new IPEndPoint(IPAddress.Parse(IP), Port));
//3.通过线程接收数据接收数据
new Thread(ReceiveMessage) { IsBackground = true }.Start();
//4.发送数据需要直到目标位置的IP和端口号
//与客户端发送消息一样,绑定目标位置的端口号和IP即可发送
}
///
/// 接收数据
///
static void ReceiveMessage()
{
while (true)
{
byte[] data = new byte[1024];
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
int length = socketServerUDP.ReceiveFrom(data, ref remoteEndPoint);
string message = Encoding.UTF8.GetString(data, 0, length);
Console.WriteLine(message + "收到来自: \nIP:"
+ (remoteEndPoint as IPEndPoint).Address.ToString()
+ "\nPort:" + (remoteEndPoint as IPEndPoint).Port.ToString()
+ "的消息:" + message);
}
}
}
}
UDP协议消息发送简单代码
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
public class SocketClientUDP : MonoBehaviour
{
public string IP = "192.168.199.128";
public int Port = 8888;
private Socket socketClientUDP;
private EndPoint endPoint;
// Start is called before the first frame update
void Start()
{
//创建Socket
socketClientUDP = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//创建一个服务器的目标位置方便之后使用
endPoint = new IPEndPoint(IPAddress.Parse(IP), Port);
//启动线程接收数据
new Thread(ReciveMessage) { IsBackground = true }.Start();
UIControl.scendMessage += SendMessageToServer;
}
///
/// 发送数据
///
public void SendMessageToServer(string message)
{
byte[] data = Encoding.UTF8.GetBytes(message);
socketClientUDP.SendTo(data, endPoint);
}
///
/// 接收数据
///
public void ReciveMessage()
{
while (true)
{
byte[] data = new byte[1024];
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
//程序运行到ReceiveFrom后会进行等待,直到有消息发送过来才会执行下边的代码
int length = socketClientUDP.ReceiveFrom(data, ref remoteEndPoint);
string message = Encoding.UTF8.GetString(data, 0, length);
Debug.Log("接收到数据:" + message);
}
}
}