https://blog.csdn.net/MikuLingSSS/article/details/82317768
https://blog.csdn.net/MikuLingSSS/article/details/82357104
里面会用到一些东西,请先读完这两篇博客 使用版本VS-2017(请看完在说我为什么没有提及Unity版本)
================================================================================================
首先,我们需要了解一些东西,以下,我会尽可能的用一些简单的语句来描述我的一些理解
假设,我们住在一个小区,现在我们想要找到一个人,但是我们没有他的地址,只知道他的姓名,这个时候我们应该怎么办?答案肯定不会是一家家的问,找管理员肯定是最简单快捷的方式,毕竟管理者有着小区所有住户的信息......那么管理者就是一个服务器,他会存储目前的所有已经住在小区中人员的信息(偷渡不管),当需要告诉所有住户(比如世界语音,坐标,状态等)的时候,或是小区发生什么事情(重启服务器)的时候,肯定要经过他,也是通过他来通知我们这些住户。
现在,你也想住在这个小区,那么,我们第一时间肯定是找个房子,这个时候你去找管理者,他会告诉你现在小区还有没有多余的房子,如果没有多余的房子,那么只能与各位住户协商,看看谁有没有搬出去的心(注:服务器的最大连接数目,是否进入等待队列,),而如果有多余的房子,那么我们就可以通过管理员来进行登记姓名,门牌号之类的(注:IP和端口)个人信息。
在想一下,一个小区辣么大,一个管理员肯定是不够的吧,我们一部分管理者,每个管理者负责一片区域(不同场景中的数据处理),但是一片区域每天发生的事情也不少啊,那么我们在招一些,负责管理区域中的某一部分(地图分割),这样我们的逻辑就比较完善了。
那么,我们可以得知了
管理部门=>总的管理者,下发任务的总服务器,区域管理者=>管理地图相关数据传输的逻辑划分,楼层管理者=>管理区域中相应范围内的数据,用户=>连接到我这个服务器的相关连接
经过上面的说明,我们大概对网络大致了解了一些,其实归根结底就是一个中转+处理(服务器),和发送数据接受数据(客户端)。
================================================================================================
打开VS,新建一个Win32控制台应用,加入相关命名空间
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Text;
新建一个类,里面声明一些初始变量
namespace SimpleSocket
{
class Server
{
static void Main(string[] args)
{
Console.ReadLine();
}
}
public class ServerSide
{
private Dictionary<int, Client> SaveClientConn;//用来保存Client
private int clientID; // 用来给客户端加上标识
private Thread listenClientConn; //监听连接进来客户端的线程
private Socket server; // 这个就是我们的主要服务器
private IPAddress ipAddress; // Ip
private static ServerSide Instance;
}
}
现在,我们在创建一个委托,广播信息的时候会用到
class DeleEvent
{
public delegate void dele(string info);
public static dele sendMessage;
public static void AddEvent(dele e)
{
sendMessage += e;
}
}
这段代码比较简单,如果有不明白的请看最上面的两篇博客;
现在,开始写一些初始化代码
public static void Initial(string ip, int port)
{
Instance = new ServerSide(ip, port); // 单例的初始化
}
private ServerSide(string ip, int port)
{
DeleEvent.sendMessage += Write; // 把广播加入到委托中
SaveClientConn = new Dictionary<int, Client>(); //
clientID = 0; // 最开始的clientID
ipAddress = IPAddress.Parse(ip); // 把字符转化为IP
server = new Socket( // 初始化Socket
AddressFamily.InterNetwork, // 使用Ipv4
SocketType.Stream, // 流式网络
ProtocolType.Tcp); // TCP
/* UDP
server = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
*/
server.Bind(new IPEndPoint(ipAddress, port)); // 绑定IP和端口
server.Listen(10); // 最大连接数目
listenClientConn = new Thread(ListenConn); // 创建一个监听链接的线程
listenClientConn.IsBackground = true; // 设为后台线程
listenClientConn.Start(); // 开启线程
}
private void ListenConn()
{
while (true)
{
Socket client = server.Accept(); // 如果有相应的Client连接,则创建一个Socket
Client c = new Client(client, clientID); // 这个类在后面
SaveClientConn.Add(clientID, c); // 把这个连接保存起来
clientID++; // 客户端标识加一
}
}
private void Write(string clientMes)
{
Console.WriteLine(clientMes);
}
我们现在已经绑定了服务器的IP和端口,并开启了监听,接下来,进行处理连接进来的客户端
class Client
{
private Socket client; // 客户端的Socket连接
private int clientID; // 字典的ID 也是这个客户端的ID
private Thread receive; // 接受客户端传输过来数据的线程
private byte[] result = new byte[1024]; // 需要接受数据的缓冲区
public Client(Socket client, int clientID)
{
DeleEvent.AddEvent(SendMessage); // 把发送方法保存如Delegate中
this.client = client; // 下面方法和服务器类似 不在解释
this.clientID = clientID;
receive = new Thread(ReceiveMessage);
receive.IsBackground = true;
receive.Start();
}
private void ReceiveMessage()
{
while (true)
{
try
{
int length = client.Receive(result); // 接受数据的长度,如果没有数据传输进来下面不会执行
Console.WriteLine("Client-{0}:{1}", clientID, Encoding.UTF8.GetString(result, 0, length)); // 输出客户端传送过来的消息和客户端ID
// 这个地方之所以要先减去,试音为我们上面已经在客户端已经说过了一句话,不应该再把我们说的话传给我们,而应该是只传给服务器和其他的客户端
DeleEvent.sendMessage -= SendMessage;
// 广播,发送给其他客户端
DeleEvent.sendMessage(client + ":" + Encoding.UTF8.GetString(result, 0, length));
DeleEvent.sendMessage += SendMessage;
// 这是服务器给他回传的话语 QAQ
SendMessage("Server:我知道了,你可以闭嘴了");
}
catch (Exception ex)
{
// 如果我们的客户端关闭连接 这个方法会报错 所以用Try
Console.WriteLine("Client-{0}关闭连接:Info-{1}", clientID, ex.Message);
// 销毁相应对象
client.Shutdown(SocketShutdown.Both);
DeleEvent.sendMessage -= SendMessage;
client.Close();
receive.Abort();
break;
}
}
}
private void SendMessage(string info)
{
client.Send(Encoding.UTF8.GetBytes(info));
}
}
以上,服务器就搭建完成了,运行的时候只需要
class Server
{
static void Main(string[] args)
{
ServerSide.Initial("127.0.0.1", 9002);
Console.ReadLine();
}
}
这就可以了,不过服务器没办法主动发送信息给客户端(友情提示:委托)
================================================================================================ 客户端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
namespace Client
{
class Program
{
static void Main(string[] args)
{
ReceiveEvent.Instance.AddEvent(cw);
ConnServer.Initial("127.0.0.1", 9002);
ConnServer.Instance.Conn();
while (true)
{
string str = Console.ReadLine();
ConnServer.Instance.SendMessage(str); // 发送消息给服务器
}
}
static void cw(string n)
{
Console.WriteLine(n);
}
}
/// <summary>
/// 连接服务器 由于代码和服务器类似 这里不做注解
/// </summary>
public class ConnServer
{
public static ConnServer Instance;
private Socket client;
private string ip;
private int port;
private IPAddress ipaddress;
private byte[] result = new byte[1024];
/// <summary>
/// 初始化参数
/// </summary>
private ConnServer(string ip, int port)
{
this.ip = ip;
this.port = port;
ipaddress = IPAddress.Parse(ip);
client = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
}
/// <summary>
/// 连接服务器
/// </summary>
public void Conn()
{
if (!client.Connected)
{
client.Connect(new IPEndPoint(ipaddress, port));
Thread startReceive = new Thread(ReceiveMessage);
startReceive.IsBackground = true;
startReceive.Start();
}
}
/// <summary>
/// 接收来自服务器的信息
/// </summary>
public void ReceiveMessage()
{
while (true)
{
int mesLength = client.Receive(result);
// 如果我这个客户端接受到了消息 则利用委托执行某个方法
// 我这里的方法比较简单 但是你可以进行一些处理
// 比如根据传入的数据 解析之后进行某些操作之类的
ReceiveEvent.Instance.play(Encoding.UTF8.GetString(result, 0, mesLength));
}
}
/// <summary>
/// 向服务器发送信息
/// </summary>
public void SendMessage(string info)
{
client.Send(Encoding.UTF8.GetBytes(info));
}
/// <summary>
/// 初始化参数
/// </summary>
public static void Initial(string ip, int port)
{
Instance = new ConnServer(ip, port);
}
}
/// <summary>
/// 添加事件,有信息传入可直接调取方法
/// </summary>
public class ReceiveEvent
{
private static ReceiveEvent instance;
public static ReceiveEvent Instance
{
get
{
if (instance == null)
instance = new ReceiveEvent();
return instance;
}
}
public delegate void Del(string info);
private Del Receive;
/// <summary>
/// 添加一个方法到委托中
/// </summary>
public void AddEvent(Del e)
{
Receive += e;
}
/// <summary>
/// 执行委托中的事件
/// </summary>
public void play(string info)
{
Receive(info);
}
}
}
如果看了前两篇博客还是不太明白这个和Unity有什么关系的话.......
=> https://blog.csdn.net/MikuLingSSS/article/details/82459555