基于Socket实现客户端和服务端的Tcp通信(C#)
0.前言
使用C#和Unity实现复刻Liar’s bar中的功能
软件开发大作业
本系列文章用于记录与分享开发过程中使用到的知识点,以及常见错误
本文主要描述有关网络编程的内容
目录
- 0.前言
- 1.使用Socket搭建Server
- 1.1Server端的Socket连接
- 1.2 Server端接收Client的信息
- 1.3 Server端接收信息后,处理信息
- 1.4 Server端给Client端发送信息
- 1.5 小结
- 2.Client端 搭建Socket连接
- 2.0 前言
- 2.1 连接服务器
- 2.2 Unity中怎么转换Json
- 3.远程联机
- 3.1SakuraFrp
- 3.2 Client端
1.使用Socket搭建Server
1.1Server端的Socket连接
public static void waitClient{
//ipv4地址,此处使用localhost
string host = "127.0.0.1";
//端口号,需要与其他端口占用不同,例如3306,8080等
//client端与此处端口相同即可
string port = "6657";
//创建ip
IPEndPoint pos = new IPEndPoint(IPAddress.Parse(host), port);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(pos);
//最多接收3个人的连接,参数可以改变
listener.Listen(3);
Socket sclient;
String name;
byte[] buffer = new byte[1024];
Player p;
int len;
//监听客户端的连接
while (true)
{
//接收到客户端连接 保存到sclient对象中
sclient = listener.Accept();
//并给这个对象单独开一个监听线程,在文后会再详细说明
Thread listen = new Thread(new ParameterizedThreadStart(listenerClient));
listen.Start(sclient);
}
}
static void Main(String[] args){
//为等待客户端连接创建一个线程
Thread wait = new Thread(new ThreadStart(waitClient));
wait.Start();
}
1.2 Server端接收Client的信息
static void listenerClient(Socket s)
{
byte[] buffer = new byte[1024];
while (true)
{
Message msg;
try
{
int len = s.Receive(buffer);
string str = Encoding.UTF8.GetString(buffer, 0, len);
//根据&进行分割
//由于客户端和服务端发送信息有延迟,
//传输时在每个信息后面加&可以分清楚信息的范围
foreach (string s in str.Split('&'))
{
if (s.Length > 0)
{
//反序列成msg对象 发送的是json格式的数据
//这里用的是NewtonSoft.Json可以在NuGet引入依赖
msg = JsonConvert.DeserializeObject<Message>(s);
//将msg加入到queue中
//上锁 使线程同步
//这里的toDoListLock的声明为
//private static readonly object toDoListLock = new object();
lock (toDoListLock)
{
//这里的toDoList的声明为:
//private static Queue<Message> toDoList = new Queue<Message>();
toDoList.Enqueue(msg);
}
}
}
}
catch (SocketException)
{//常见try-catch,不做过多赘述
break;
}
}
}
//常见Msg类
class Message
{
public String type;
public String info;
//构造器
public Message(string type, string info)
{
this.type = type;
this.info = info;
}
}
1.3 Server端接收信息后,处理信息
//也需要给本类启动一个线程,
//Thread toDo = new Thread(new ThreadStart(toDoListClient));
//toDo.start
static void toDoListClientOperation()
{
while (true)
{//循环监听,如果此时队列中无信息就进入下次循环
if (toDoList.Count == 0)
{
continue;
}
//得到msg对象
Message msg = toDoList.Dequeue();
switch(msg.type){
//根据类型进行对应操作即可 此处省略
}
}
}
1.4 Server端给Client端发送信息
static void sendMsg(string type, string info,Socket socket)
{
Message msg = new Message(type, info);
string str = JsonConvert.SerializeObject(msg);
//将json字符串转成字符数组并最后加上一个&
//添加&的原因已在1.2中说明,此处省略
byte[] bytes = Encoding.UTF8.GetBytes(str + '&');
//发送信息
socket.Send(bytes);
}
1.5 小结
大体思路就是一个线程用于接收Client的连接,一个线程用于接收Client的信息,一个线程专门用于处理信息
面对Server端要被多个Client端连接时,可以创建数组保存这些Socket
2.Client端 搭建Socket连接
2.0 前言
由于Client端与Server端搭建类似,此处只写如何连接,后面的收发信息则不过多赘述
2.1 连接服务器
//与Server端保持一致
string host = "127.0.0.1";
string port = "6657";
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(host, port);
2.2 Unity中怎么转换Json
在Unity项目中引入NewtonSoft.Json.dll后发现经常会导致程序崩溃,
Unity中自带一个处理JSON的类:JsonUtility 类
常用API:
JsonUtility.ToJson(param)
JsonUtility.FromJson<>(param)
3.远程联机
这里简单提供一种思路:内网穿透
在代码实现方面与上面相同,只不过host和port要改变
3.1SakuraFrp
这里用SakuraFrp实现内网穿透
搭建时本机IP去看当前联网的属性中IPV4的地址
(注意:此时开启服务器的电脑本地IP不是127.0.0.1,要去Wlan那里看具体的ip是什么)
搭建选择TCP
附SakuraFrp的官方地址:https://www.natfrp.com/
Server端的ip填写ipv4的地址,端口就写搭建时开放的端口
(注:Server端电脑的端口应该设置为开放)
笔者曾使用该软件进行游戏联机,所以想到这里能用(doge)
3.2 Client端
启动SakuraFrp该隧道后,在日志信息中会给出映射的ip和端口
冒号前面是host,后面是port。
则Client端的host=“211.149.167.185”,port=28821