通信(socket)的技术
大家好,我是你们的好朋友程序员:铭文
先简单的说下:好久没写通信了最近几年一直在写一些业务的接口程序。处理一些数据业务,但是根据工作需要:我现
在要写一个和下位机做这个时时通信的这样一个程序,目的主要是简历这个连接进行和下位机的交互,数据的传递:
Socket基础:
其实我的理解就是:Socket就是一个最最基础的通信而已。它是通过服务的端口来进行连接。连接互联网的机器都有自己的ip,也就是我们自己的服务器ip但是我们大家都连接到 Internet网 以后,我们就是可以通过这个socket 来进行通信。我们程序猿也都知道:网络通信的工作的7层吧:
1. 最基础的物理层:有 线路,光纤网线这些东西。
2. 数据链路层: 这就是我们内网的以太网。就是一个代表。
3. 网络传输层:这一层比较复杂:数据链路层的数据在这一层被转换为数据包,数据包这些就是分段组合。然后通过网络设备传送到另外一台网络设备的:TCP/IP,和地址解析等等感兴趣的可以自己去搜索下,
4. 运输层: 这就就包含我们这次要实现的 :TCP和UDP
5. 会话层: 这一层非常简单:用我们前后端分离的开发模式说就是:你前端拿了后端的api接口 return的数据。我来进行数据的渲染哈哈;还有 有2个页面之间的session会话对象,
6 表示层:
,比如我们经常使用的QQ就是socket 通信的一种:
打开QQ的设置就可以看到,这里我们可以看到服务器类型这块分为2个:UDP,TCP
7 应用层: 这个就代表的是应用的本身了。比如qq。通过应用程序的通讯服务的就是我们最容易理解的应用层。
UDP的介绍与应用:
我这边简单的说下:
udp就是用户数据报告交互协议,记住的关键单就是:不是面向连接的。不可靠的数据传输,udp的可靠性比较差。它可以把应用程序传给服务层(ip)的数据发送出去,但是不能保证到达。就是我们经常会出现的。丢数据,丢包,数据不稳定,udp是会经常出现的、
举一个简单的事例:
我们经常使用qq电话视频。这个时候如果信号差的时候经常会出现卡顿。但是连接还是有的,但是视频信号用的就是udp,我做的就是可以保证,视频信号高效率的发出,至于你接收端的接收等等不在我考虑的范围内。就是我们“ping” 命令来测试两台主机之间 tcp/ip 通信,其实“ping”命令的原理就是向服务主机发送UDP数据包,然后服务主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。就是这么简单,
TCP的介绍与应用 TCP/IP(Transmission Control Protocol/Internet Protocol 传输控制协议/网间协议) :
这个需要着重说下,因为目前我们使用的也是这个:
传输控制协议 tcp:可以2个主机之间交互反馈。也就是我们协议之间理解的一种“心跳”互动,交互的概念。实现的比如,我们做的交互,一段时间没有回复,定义的规则下我可以再次的发送消息。等另外还可以做到负责恢复,这个我没用过,我也是看书上写的。
网间协议 IP:可以进行传输的数据包的分割组装。我们项目中就出现了一个这个的问题:我们的环境是写了一个python 来读取别人系统的数据,然后把数据通过咱们现在说的这个tcp传输到下位机上面。下位机然后再把数据传输到。winfrom上面就是我们上位机的一个程序上面,我主要负责的就是下位机数据的处理,但是这个时候,下位机给我提交的数据就出现了一个问题,经常丢数据,丢包,丢前面或者丢后面,后来证实了。他的那个下位机的单片机传输的数据只能包含2k之内,不然经常丢数据,这个时候就要通过网间协议来,把数据进行分割。重新储存了。
tcp的特点和定义
tcp和udp正好相反:是面向连接、可靠的字节流服务,
不废话了上源码:(这是一个winfrom的程序)
客户端代码:
public partial class frmMainSender : Form
{
private Socket _socketSend = null;
private Thread _watchThread = null;
private Thread _heartBeatThread = null;
private tm.Timer _timer = null;
public frmMainSender()
{
InitializeComponent();
this.Load += FrmMainSender_Load;
}
private void FrmMainSender_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
private void btnStartServer_Click(object sender, EventArgs e)
{
try
{
if (string.IsNullOrEmpty(txtIP.Text) || string.IsNullOrEmpty(txtPort.Text))
{
MessageBox.Show("IP及端口不可空!");
return;
}
//IPAddress 类包含计算机在网络上的 IP 地址,它主要提供网际协议地址(IP)
IPAddress ipAdd = IPAddress.Parse(txtIP.Text);
_socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//ReceiveTimeout 属性确定Read方法在能够接受数据之前保持阻塞状态的时间量
_socketSend.ReceiveTimeout = 10000;
//超时时间量
_socketSend.SendTimeout = 10000;
_socketSend.Connect(new IPEndPoint(ipAdd, Int32.Parse(txtPort.Text)));
WriteLog("连接服务器成功!");
btnStartServer.Enabled = false;
_watchThread = new Thread(Received);
_watchThread.IsBackground = true;
_watchThread.Start();
_heartBeatThread = new Thread(HeartBeat);
_heartBeatThread.IsBackground = true;
_heartBeatThread.Start();
}
catch (Exception)
{
}
}
private void Received()
{
while (true)
{
try
{
byte[] buffer = new byte[1024 * 1024 * 3];
int length = _socketSend.Receive(buffer);
if (length == 0)
{
break;
}
string str = Encoding.UTF8.GetString(buffer, 0, length);
if (str.ToLower() == "heartbeat")
{
WriteLog("客户端---接收--检测心跳包!");
continue;
}
else if (str.ToLower() == "1")
{
WriteLog("接收端接收成功!");
continue;
}
txtReceiveData.Text = str;
}
catch (Exception)
{
}
}
}
private void HeartBeat()
{
_timer = new tm.Timer(3000);
_timer.Elapsed += _timer_Elapsed;
_timer.Start();
}
private void _timer_Elapsed(object sender, tm.ElapsedEventArgs e)
{
try
{
WriteLog("客户端---发送---检测心跳包!");
byte[] buffer = Encoding.UTF8.GetBytes("HeartBeat");
_socketSend.Send(buffer);
}
catch (Exception)
{
}
}
private void btnStopServer_Click(object sender, EventArgs e)
{
if (_socketSend == null) return;
if (!_socketSend.Connected) return;
try
{
_heartBeatThread.Abort();
_timer.Stop();
_timer = null;
_socketSend.Shutdown(SocketShutdown.Both);
}
catch (Exception)
{
}
try
{
_socketSend.Close();
}
catch (Exception)
{
}
btnStartServer.Enabled = true;
WriteLog("服务已关闭!");
}
private void btnSend_Click(object sender, EventArgs e)
{
try
{
byte[] buffer = Encoding.UTF8.GetBytes(txtBase64Data.Text);
_socketSend.Send(buffer);
WriteLog("已发送数据!");
}
catch (Exception)
{
}
}
private void btnBase64_Click(object sender, EventArgs e)
{
byte[] bytes = Encoding.Default.GetBytes(txtReceiveData.Text);
txtBase64Data.Text = Convert.ToBase64String(bytes);
}
private void WriteLog(string message)
{
txtLogs.AppendText(DateTime.Now.ToString("HH:mm:ss.fff") + "----" + message + "\r\n");
}
private void btnClear_Click(object sender, EventArgs e)
{
txtReceiveData.Text = "";
txtBase64Data.Text = "";
}
private void btnClearLog_Click(object sender, EventArgs e)
{
txtLogs.Clear();
}
}
服务器端代码:
public partial class frmMainReceiver : Form
{
private Socket _socketSend = null;
//定义一个集合,存储客户端信息
static Dictionary<string, Socket> clientConnectionItems = new Dictionary<string, Socket> { };
private tm.Timer _timer = null;
private Thread _heartBeatThread = null;
private Thread _watchThread = null;
static Socket _socketWatch = null;
public frmMainReceiver()
{
InitializeComponent();
this.Load += FrmMainReceiver_Load;
}
private void FrmMainReceiver_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
private void btnStartServer_Click(object sender, EventArgs e)
{
try
{
if (string.IsNullOrEmpty(txtIP.Text) || string.IsNullOrEmpty(txtPort.Text))
{
MessageBox.Show("IP及端口不可空!");
return;
}
IPAddress ipAdd = IPAddress.Any;
//new Socket 实例
_socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定下
_socketWatch.Bind(new IPEndPoint(ipAdd, Int32.Parse(txtPort.Text)));
//套接口并监听申请的连接
_socketWatch.Listen(100);
WriteLog("服务已启动!");
btnStartServer.Enabled = false;
//线程
_watchThread = new Thread(Listen);
_watchThread.IsBackground = true;
_watchThread.Start(_socketWatch);
_heartBeatThread = new Thread(HeartBeat);
_heartBeatThread.IsBackground = true;
_heartBeatThread.Start();
}
catch (Exception ec) { }
}
private void HeartBeat()
{
_timer = new tm.Timer(5000);
_timer.Elapsed += _timer_Elapsed;
_timer.Start();
}
private void _timer_Elapsed(object sender, tm.ElapsedEventArgs e)
{
string[] keys = new string[clientConnectionItems.Keys.Count];
clientConnectionItems.Keys.CopyTo(keys, 0);
foreach (string key in keys)
{
Socket socket = clientConnectionItems[key];
if (socket.Poll(10000, SelectMode.SelectRead))
{
WriteLog(key + "---已断开连接!");
clientConnectionItems.Remove(key);
continue;
}
byte[] buffer = Encoding.UTF8.GetBytes("HeartBeat");
socket.Send(buffer);
WriteLog("接收端--向" + key + "-发送---检测心跳包!");
}
}
private void Listen(object o)
{
try
{
Socket socketWatch = o as Socket;
while (true)
{
_socketSend = socketWatch.Accept();//等待接收客户端连接
//客户端网络结点号
string remoteEndPoint = _socketSend.RemoteEndPoint.ToString();
clientConnectionItems.Add(remoteEndPoint, _socketSend);
WriteLog(remoteEndPoint + "连接成功!");
Thread thread = new Thread(ReceiveData);
thread.IsBackground = true;
thread.Start(_socketSend);
}
}
catch (Exception e)
{
}
}
private void ReceiveData(object o)
{
try
{
Socket socketClient = o as Socket;
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 3];
int length = socketClient.Receive(buffer);
if (length == 0)
{
continue;
}
string strMsg = Encoding.UTF8.GetString(buffer, 0, length);// 将接受到的字节数据转化成字符串;
txtReceiveData.Text = strMsg;
if (strMsg.ToLower() == ("heartbeat"))
{
WriteLog("接收端---接收来自" + socketClient.RemoteEndPoint.ToString() + "---检测心跳包!");
byte[] bufferBack = Encoding.UTF8.GetBytes("HeartBeat");
socketClient.Send(bufferBack);
continue;
}
byte[] outputb = Convert.FromBase64String(txtReceiveData.Text.Replace(' ', '+'));
txtNoBase64Data.Text = Encoding.Default.GetString(outputb);
WriteLog("接收端---接收到数据!");
//接收完数据后向客户端发送数据接收成功标志
socketClient.Send(Encoding.UTF8.GetBytes("1"));
}
}
catch (Exception e)
{
}
}
private void WriteLog(string message)
{
txtLogs.AppendText(DateTime.Now.ToString("HH:mm:ss.fff") + "----" + message + "\r\n");
}
private void btnStopServer_Click(object sender, EventArgs e)
{
if (_socketSend == null) return;
if (!_socketSend.Connected) return;
try
{
_timer.Stop();
_timer = null;
_heartBeatThread.Abort();
_watchThread.Abort();
_socketSend.Shutdown(SocketShutdown.Both);
_socketWatch.Shutdown(SocketShutdown.Both);
clientConnectionItems.Clear();
}
catch (Exception)
{
}
try
{
_socketSend.Close();
_socketWatch.Close();
}
catch (Exception)
{
}
btnStartServer.Enabled = true;
WriteLog("服务已关闭!");
}
private void btnClear_Click(object sender, EventArgs e)
{
txtReceiveData.Text = "";
txtNoBase64Data.Text = "";
}
private void btnClearLog_Click(object sender, EventArgs e)
{
txtLogs.Clear();
}
}
以上的就是 socket tcp 的双方的交互验证
希望大家又不懂的地方可以留言。我们互相交流,有欠妥的地方,也希望大家指出来