一,服务器程序
1.用VS2022创建一个新的winform项目作为服务器
2.将新项目的class Program中的代码修改
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//1,TcpListener对socket进行了一层封装,这个类里面自己会去创建socket对象
TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 7788);
//2,开始进行监听
listener.Start();
//3,等待客户端连接过来
TcpClient client = listener.AcceptTcpClient();
//4,取得客户端发送过来的数据
NetworkStream stream = client.GetStream();//得到了一个网络流 从这个网络流可以取得客户端发送过来的数据
byte[] data = new byte[1024];//创建一个数据的容器,用来承接数据,此处有大小限制
Thread t1 = new Thread(() => ReadMsg(stream, data)); //开启线程读取消息
t1.Start();
Thread t2 = new Thread(() => SendMsg(stream)); //开始线程发送消息
t2.Start();
//stream.Close();
//client.Close();
//listener.Stop();//停止监听
//Console.ReadKey();
}
private static void SendMsg(NetworkStream stream)
{
byte[] data = new byte[7] { 0xCB,0x05,0x01,0x00,0x19,0x00,0x20};
stream.Write(data, 0, data.Length);
//byte[] data = new byte[255];
//data[1] = 0xfd;
//byte[] temp = new byte[100];
//Array.Copy(data, 0, temp, 0,100 );
//stream.Write(temp, 0, temp.Length);
//Thread.Sleep(90);
//byte[] temp2 = new byte[100];
//Array.Copy(data, 100, temp2, 0, 100);
//stream.Write(temp2, 0, temp2.Length);
//Thread.Sleep(90);
//byte[] temp3 = new byte[55];
//Array.Copy(data, 200, temp3, 0, 55);
//stream.Write(temp3, 0, temp3.Length);
}
private static void ReadMsg(NetworkStream stream, byte[] data)
{
while (true)
{
//0 表示从数组的哪个索引开始存放数据
//1024表示最大读取的字节数
int length = stream.Read(data, 0, 1024);//读取数据
string result = BitConverter.ToString(data).Replace("-", " ");
Debug.WriteLine(result);
Thread.Sleep(1000);
}
}
}
二,客户端程序
1.用VS2022创建一个新的winform项目作为客户端
2.将它的class Program做如下修改
internal static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
TCPClient client = new TCPClient("127.0.0.1", 7788);
if (client.Connect())
for (int i = 0; i < 10; i++)
{
int[] ret=client.ReadyToStart();
foreach (int item in ret)
{
Console.WriteLine(item);
}
}
}
}
3.创建一个新的类,命名为TCPClient
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class TCPClient
{
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *变量表
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
private TcpClient _client;
private string _serverIpAddress;
private int _serverPort;
private NetworkStream _stream;
private Sender _sender;
private Receiver _receiver;
//发送锁
public static Boolean TCPSend = false;
public static Object TCPLockObj = new object();
//主线程锁
public static Boolean MainLock = false;
public static AutoResetEvent MainLockEvent = new AutoResetEvent(false);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *TCP连接并初始化
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
public TCPClient(string serverIpAddress, int serverPort)
{
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *客户端的初始化* * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
_serverIpAddress = serverIpAddress;
_serverPort = serverPort;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *服务器的初始化* * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
//TcpListener listener = new TcpListener(IPAddress.Parse(serverIpAddress), serverPort);//1,TcpListener对socket进行了一层封装,这个类里面自己会去创建socket对象
//listener.Start();//2,开始进行监听
//TcpClient client = listener.AcceptTcpClient();//3,等待客户端连接过来
//NetworkStream stream = client.GetStream();//4,取得客户端发送过来的数据,得到了一个网络流 从这个网络流可以取得客户端发送过来的数据
}
public Boolean Connect()
{
try
{
_client = new TcpClient();
_client.Connect(_serverIpAddress, _serverPort);
StartSender_StartReceiver();
Console.WriteLine("已连接到服务器");
return true;
}
catch (Exception)
{
return false;
}
}
public Boolean Disconnect()
{
try
{
StopSender_StopReceiver();
_client.Close();
Console.WriteLine("已断开与服务器的连接");
return true;
}
catch (Exception)
{
return false;
}
}
public void StartSender_StartReceiver()
{
_stream=_client.GetStream();
_sender=new Sender(_stream);
_sender.StartSender();
_receiver = new Receiver(_stream);
_receiver.StartReceiver();
}
public void StopSender_StopReceiver()
{
_sender.StopSender();
_receiver.StopReceiver();
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *客户端发送的指令
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
public int[] ReadyToStart()
{
_sender.ReadyToStart();
MainLock = MainLockEvent.WaitOne(2000);
int[] ret;
if (MainLock)
{
ret = new int[] {_receiver.X_DIST,_receiver.THETA };
return ret;
}
else
return ret=new int[] { -1,-1 };
}
}
4.再创建两个新的类Sender、Receiver
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
class Sender
{
private Thread SenderThread;
private CancellationTokenSource _ctsSenderThread;
private CancellationToken tokenSenderThread;
private byte[] QueueBytes;
private ConcurrentQueue<byte[]> CmdQueue = new ConcurrentQueue<byte[]>();
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *初始化
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
private NetworkStream stream;
public Sender(NetworkStream stream)
{
this.stream = stream;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * * 发送线程
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * */
public void StartSender()//启动发送者线程
{
_ctsSenderThread = new CancellationTokenSource(); // 创建新的 CancellationTokenSource
tokenSenderThread = _ctsSenderThread.Token;
SenderThread = new Thread(() => Send(tokenSenderThread)); // 创建新的线程
SenderThread.Start(); // 启动线程
}
public void StopSender()//停止发送者线程
{
object objDataSend = SenderThread;
if (objDataSend != null)//判断dataSend是否为空
{
_ctsSenderThread.Cancel();
}
}
private void Lock()
{
TCPClient.TCPSend= false;
// Monitor.Pulse机制,等待两秒钟
lock (TCPClient.TCPLockObj)
{
Monitor.Wait(TCPClient.TCPLockObj, 2000);
// 检查是否接收到返回结果
if (!TCPClient.TCPSend)
{
Console.WriteLine("服务器未返回");
}
}
}
void Send(CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
if (CmdQueue.TryDequeue(out QueueBytes))
{
stream.Write(QueueBytes, 0, QueueBytes.Length);
Lock();//发送锁,两秒内接收线程接到回复后解锁,若两秒后仍无回复则自动解锁
}
}
}
catch (ThreadInterruptedException)
{
// 处理线程被中断的情况
Console.WriteLine("发送者线程被中断");
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *指令表
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
byte HEAD,HEAD_ACTIVE=0xDA,HEAD_UNACTIVE = 0xDB;//报文头
byte LEN;//长度位
byte CMD;//命令位
byte CHECK;//校验位
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * * *发送函数
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * */
public void ReadyToStart()//开始指令
{
HEAD = HEAD_ACTIVE;
LEN = 0x01;
CMD = 0x02;
byte[] SendBytes = new byte[] { HEAD,LEN,CMD };
CmdQueue.Enqueue(SendBytes);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *校验
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class Receiver
{
private Thread ReceiverThread;
private CancellationTokenSource _ctsReceiverThread;
private CancellationToken tokenReceiverThread;
private int _delayTime=50;//监听到数据流有数据后,接收线程中断_delayTime(ms),以便数据流有足够的时间传输完整的报文到客户端,
//即监听到数据流有数据后等待_delayTime(ms)客户端再读取stream中的数据
private int _lengthOfLongestCMD=167;//最长的指令的长度
private int _redundancy=100;//冗余,加在_lengthOfLongestCMD后面,以便扩展缓存buffer的长度
private int _outOfLENRange=2;//LEN长度位记录CMD命令位+DATA数据位总长度,不包括HEAD报文头+LEN长度位这两个字节,故_outOfLENRange=2
private int _path=0;
private int _tempLength;
private byte[] _tempBuffer,buffer,_complete;
List<byte> _combinedList;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *初始化
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
private NetworkStream stream;
public Receiver(NetworkStream stream) { this.stream = stream; }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *TCP数据接收
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
public void StartReceiver()//启动接收者线程
{
_ctsReceiverThread = new CancellationTokenSource(); // 创建新的 CancellationTokenSource
tokenReceiverThread = _ctsReceiverThread.Token;
ReceiverThread = new Thread(() => TCP_DataReceived(tokenReceiverThread));
ReceiverThread.Start(); // 启动线程
}
public void StopReceiver()//停止接收者线程
{
object objDataReceive = ReceiverThread;
if (objDataReceive != null)//判断dataSend是否为空
{
_ctsReceiverThread.Cancel();
}
}
private void UnLock()//释放发送锁,使发送线程可以发送下一个指令
{
lock (TCPClient.TCPLockObj)
{
TCPClient.TCPSend = true;
Monitor.Pulse(TCPClient.TCPLockObj);
}
}
private void TCP_DataReceived(CancellationToken token)//数据接收
{
while (!token.IsCancellationRequested)
{
if (stream.DataAvailable)
{
Thread.Sleep(_delayTime);
try
{
buffer = new byte[_lengthOfLongestCMD+_redundancy];
int length = stream.Read(buffer, 0, buffer.Length);//读取数据
switch(_path)//处理报文接收不完整机制
{
case 0:
byte[] b = new byte[] { buffer[1] ,0x00,0x00,0x00};//小端表示
_tempLength = BitConverter.ToInt32(b, 0);//将字节数组转化为整数,其中b必须是四字节否则会报错
Console.WriteLine("长度位规定的长度为: " + _tempLength);
//判断是否接收到完整的报文
if (length-_outOfLENRange == _tempLength) //接收到的报文长度等于长度位所规定的长度
{
_complete=new byte[length];
Array.Copy(buffer, 0, _complete, 0, length);
string result = BitConverter.ToString(_complete).Replace("-", " ");
Console.WriteLine("一次成功:"+result);
UnLock();//释放发送锁,使发送线程可以发送下一个指令
DataAnalysis(_complete);//分析报文数据
}
else if (length - _outOfLENRange > _tempLength)//接收到的报文长度超过长度位所规定的长度
{
Console.WriteLine("0报文长度过长,无效");
UnLock();//释放发送锁,使发送线程可以发送下一个指令
}
else //接收到的报文长度少于长度位所规定的长度
{
_tempBuffer = new byte[length];
Array.Copy(buffer, 0, _tempBuffer, 0, length);
_combinedList = new List<byte>(_tempBuffer);
_path = 1;
}
break;
case 1:
_tempBuffer = new byte[length];
Array.Copy(buffer, 0, _tempBuffer, 0, length);
_combinedList.AddRange(_tempBuffer);
if (_combinedList.Count - _outOfLENRange == _tempLength)//接收到完整报文
{
_complete = _combinedList.ToArray();
string result = BitConverter.ToString(_complete).Replace("-", " ");
Console.WriteLine(result);
_path = 0;
UnLock();//释放发送锁,使发送线程可以发送下一个指令
DataAnalysis(_complete);//分析报文数据
}
else if(_combinedList.Count - _outOfLENRange > _tempLength)//接收到的报文长度超过长度位所规定的长度
{
Console.WriteLine("1报文长度过长,无效");
_path = 0;
UnLock();//释放发送锁,使发送线程可以发送下一个指令
}
break;
}
}
catch (IndexOutOfRangeException Ine)
{
Console.WriteLine("索引超出数组界限错误:" + Ine.Message);
// 处理索引超出数组界限的错误,例如回退到默认处理或给出提示信息等
}
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *数据解析
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
private void DataAnalysis(byte[] _analysis)
{
//分析所获取的报文数据
switch (_analysis[0])
{
case 0xCB:
switch (_analysis[2])
{
case 0x01:
x_dist = BitConverter.ToInt32(new byte[] { _analysis[4], _analysis[3], 0x00, 0x00 }, 0);
theta = BitConverter.ToInt32(new byte[] { _analysis[6], _analysis[5], 0x00, 0x00 }, 0);
TCPClient.MainLockEvent.Set();
break;
}
break;
case 0xCA:
break;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
* * * * * * * * * * ** * * * * * * * * * * * * * *解析所得数据
* * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * */
private int x_dist;
public int X_DIST
{
get { return x_dist; }
set { x_dist = value; }
}
private int theta;
public int THETA
{
get { return theta; }
set { theta = value; }
}
}