项目功能:
上位机生成图片信息,发送给下位机(stm32),下位机控制多个电子标签显示其内容。
程序包含的模块:
1. 自动获取本机的IPV4地址。
2. tcp\ip建立与下位机的连接。
3. 多线程编程,可以同时发送图片信息给多个下位机。
4.GDI绘图。
5.图片信息的进制转换。
6.利用扩展dll进行二维码绘制(QR)
运行图片:
程序代码:
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net; // IP,IPAddress, IPEndPoint,端口等;
using System.Threading;
using System.Drawing.Imaging;
/*程序介绍:
******************************************************************************************
*******************************2016,10,11***********************************************
*/
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
#region//全局变量
DotNetBarcode bc = new DotNetBarcode();
string s;
Thread threadWatch = null; // 负责监听客户端连接请求的 线程;
Socket socketWatch = null;
Dictionary<string, Socket> dict = new Dictionary<string, Socket>(); //哈希key-value
Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
#endregion
#region//窗口的初始化
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;//跨线程
}
private void Form1_Load(object sender, EventArgs e)
{ bc.Type = DotNetBarcode.Types.QRCode;
bc.PrintCheckDigitChar = true;
IPAddress ipaddress=null;
string HostName = Dns.GetHostName(); //得到主机名
IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
for (int i = 0; i < IpEntry.AddressList.Length; i++)
{
//从IP地址列表中筛选出IPv4类型的IP地址/AddressFamily.InterNetwork表示此IP为IPv4,
if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
{
ipaddress = IpEntry.AddressList[i];
}
}
txtIp.Text = ipaddress.ToString();
}
#endregion
#region//实现方法
void WatchConnecting()
{
while (true) // 持续不断的监听客户端的连接请求;
{
// 开始监听客户端连接请求,Accept方法会阻断当前的线程;
Socket sokConnection = socketWatch.Accept(); // 一旦监听到一个客户端的请求,就返回一个与该客户端通信的 套接字;
// 想列表控件中添加客户端的IP信息;
lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
// 将与客户端连接的 套接字 对象添加到集合中;
dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);
ShowMsg("客户端连接成功!");
Thread thr = new Thread(RecMsg);
thr.IsBackground = true;
thr.Start(sokConnection);
dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr); // 将新建的线程 添加 到线程的集合中去。
}
}
void RecMsg(object sokConnectionparn)
{
Socket sokClient = sokConnectionparn as Socket;
while (true)
{
// 定义一个2M的缓存区;
byte[] arrMsgRec = new byte[1024 * 1024 * 2];
// 将接受到的数据存入到输入 arrMsgRec中;
int length = -1;
try
{
length = sokClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
}
catch (SocketException se)
{
ShowMsg("异常:" + se.Message);
// 从 通信套接字 集合中删除被中断连接的通信套接字;
dict.Remove(sokClient.RemoteEndPoint.ToString());
// 从通信线程集合中删除被中断连接的通信线程对象;
dictThread.Remove(sokClient.RemoteEndPoint.ToString());
// 从列表中移除被中断的连接IP
lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
break;
}
catch (Exception e)
{
ShowMsg("异常:" + e.Message);
// 从 通信套接字 集合中删除被中断连接的通信套接字;
dict.Remove(sokClient.RemoteEndPoint.ToString());
// 从通信线程集合中删除被中断连接的通信线程对象;
dictThread.Remove(sokClient.RemoteEndPoint.ToString());
// 从列表中移除被中断的连接IP
lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
break;
}
if (arrMsgRec[0] == 0) // 表示接收到的是数据;
{
string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);// 将接受到的字节数据转化成字符串;
ShowMsg(strMsg);
}
}
}
void ShowMsg(string str)
{
txtMsg.AppendText(str + "\r\n");
}
#endregion
#region//click事件
private void btnBeginListen_Click(object sender, EventArgs e)
{
// 创建负责监听的套接字,注意其中的参数; ip地址类型是ipv4,socket流形式,网络协议tcp类型
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(txtIp.Text.Trim());// 将 IP 地址字符串转换为 IPAddress 实例。
//获得文本框中的IP对象;trim去掉前后空格
// 创建包含ip和端口号的网络节点对象;
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
try
{
// 将负责监听的套接字绑定到唯一的ip和端口上;
socketWatch.Bind(endPoint);
}
catch (SocketException se)
{
MessageBox.Show("异常:" + se.Message);
return;
}
// 设置监听队列的长度; 最多可容纳的等待接受的传入连接数
socketWatch.Listen(10);
// 创建负责监听的线程;
threadWatch = new Thread(WatchConnecting);
threadWatch.IsBackground = true;
threadWatch.Start();
ShowMsg("服务器启动监听成功!");
//}
}
private void btnSend_Click(object sender, EventArgs e)
{
string strMsg = txtMsgSend.Text.Trim() ;
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
byte[] arrSendMsg = new byte[arrMsg.Length + 1];
arrSendMsg[0] = 0; // 表示发送的是消息数据
Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
string strKey = "";
strKey = lbOnline.Text.Trim();
if (string.IsNullOrEmpty(strKey)) // 判断是不是选择了发送的对象;
{
MessageBox.Show("请选择你要发送的客户端!!!");
}
else
{
dict[strKey].Send(arrSendMsg);// 解决了 sokConnection是局部变量,不能再本函数中引用的问题;
ShowMsg(strMsg);
txtMsgSend.Clear();
}
}
private void btnSendToAll_Click(object sender, EventArgs e)
{
string strMsg = txtMsgSend.Text.Trim();
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
//< span style = "font-size:14px;" >
byte[] arrSendMsg = new byte[arrMsg.Length + 1]; // 上次写的时候把这一段给弄掉了,实在是抱歉哈~ 用来标识发送是数据而不是文件,如果没有这一段的客户端就接收不到消息了~~~
arrSendMsg[0] = 0; // 表示发送的是消息数据
Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);//</ span >
foreach (Socket s in dict.Values)
{
s.Send(arrMsg);
}
ShowMsg(strMsg);
txtMsgSend.Clear();
ShowMsg(" 群发完毕~~~");
}
private void button4_Click(object sender, EventArgs e)
{
Bitmap b = new Bitmap(251, 128);
ptQRCode.DrawToBitmap(b, new Rectangle(0, 0, 251, 128));
// Bitmap bmp2 = GetImageBW(b);
s = string.Join("", Enumerable.Range(0,32128).Select(a => new { x = a /128, y = a %128 })
.Select(x => b.GetPixel(x.x, x.y).GetBrightness() > 0.5f ? "1" : "0"));
string[] a1 = new string[4016];
for (int i = 0; i < 4016; i++)
{
a1[i] = string.Format("{0:X000}", Convert.ToInt32(s.Substring(i * 8, 8), 2));
if (a1[i].Length==1) a1[i] = a1[i].Insert(0,"0");
}
string a2 = string.Join("", a1);
// string a3 = a2.Insert(0,"0x");
txtMsgSend.Text = a2;
b.Save("D:\\Picture1.bmp");
}
private void over_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void btnCreate_Click(object sender, EventArgs e)
{
this.ptQRCode.Refresh();
}
#endregion
#region//其他事件
private void ptQRCode_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.White, new Rectangle() { X = 0, Y = 0, Width = 251, Height = 126 });
bc.WriteBar(textBox4.Text.Trim(), 190, 70, 80, 80, e.Graphics);
SolidBrush b1 = new SolidBrush(Color.Black);//定义单色画刷
e.Graphics.DrawString("商品名:"+textBox3.Text, new Font("宋体", 11), b1, new PointF(0, 10));
e.Graphics.DrawString("生产日期:"+textBox5.Text, new Font("宋体", 11), b1, new PointF(0, 30));
e.Graphics.DrawString("生产厂家:"+textBox6.Text, new Font("宋体", 11), b1, new PointF(0, 50));
e.Graphics.DrawString("保质期:"+textBox8.Text, new Font("宋体", 11), b1, new PointF(0, 70));
e.Graphics.DrawString("售价:" + textBox1.Text, new Font("宋体", 11), b1, new PointF(0, 90));
}
#endregion
}
}
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net; // IP,IPAddress, IPEndPoint,端口等;
using System.Threading;
using System.Drawing.Imaging;
/*程序介绍:
******************************************************************************************
*******************************2016,10,11***********************************************
*/
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
#region//全局变量
DotNetBarcode bc = new DotNetBarcode();
string s;
Thread threadWatch = null; // 负责监听客户端连接请求的 线程;
Socket socketWatch = null;
Dictionary<string, Socket> dict = new Dictionary<string, Socket>(); //哈希key-value
Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
#endregion
#region//窗口的初始化
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;//跨线程
}
private void Form1_Load(object sender, EventArgs e)
{ bc.Type = DotNetBarcode.Types.QRCode;
bc.PrintCheckDigitChar = true;
IPAddress ipaddress=null;
string HostName = Dns.GetHostName(); //得到主机名
IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
for (int i = 0; i < IpEntry.AddressList.Length; i++)
{
//从IP地址列表中筛选出IPv4类型的IP地址/AddressFamily.InterNetwork表示此IP为IPv4,
if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
{
ipaddress = IpEntry.AddressList[i];
}
}
txtIp.Text = ipaddress.ToString();
}
#endregion
#region//实现方法
void WatchConnecting()
{
while (true) // 持续不断的监听客户端的连接请求;
{
// 开始监听客户端连接请求,Accept方法会阻断当前的线程;
Socket sokConnection = socketWatch.Accept(); // 一旦监听到一个客户端的请求,就返回一个与该客户端通信的 套接字;
// 想列表控件中添加客户端的IP信息;
lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
// 将与客户端连接的 套接字 对象添加到集合中;
dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);
ShowMsg("客户端连接成功!");
Thread thr = new Thread(RecMsg);
thr.IsBackground = true;
thr.Start(sokConnection);
dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr); // 将新建的线程 添加 到线程的集合中去。
}
}
void RecMsg(object sokConnectionparn)
{
Socket sokClient = sokConnectionparn as Socket;
while (true)
{
// 定义一个2M的缓存区;
byte[] arrMsgRec = new byte[1024 * 1024 * 2];
// 将接受到的数据存入到输入 arrMsgRec中;
int length = -1;
try
{
length = sokClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
}
catch (SocketException se)
{
ShowMsg("异常:" + se.Message);
// 从 通信套接字 集合中删除被中断连接的通信套接字;
dict.Remove(sokClient.RemoteEndPoint.ToString());
// 从通信线程集合中删除被中断连接的通信线程对象;
dictThread.Remove(sokClient.RemoteEndPoint.ToString());
// 从列表中移除被中断的连接IP
lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
break;
}
catch (Exception e)
{
ShowMsg("异常:" + e.Message);
// 从 通信套接字 集合中删除被中断连接的通信套接字;
dict.Remove(sokClient.RemoteEndPoint.ToString());
// 从通信线程集合中删除被中断连接的通信线程对象;
dictThread.Remove(sokClient.RemoteEndPoint.ToString());
// 从列表中移除被中断的连接IP
lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
break;
}
if (arrMsgRec[0] == 0) // 表示接收到的是数据;
{
string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);// 将接受到的字节数据转化成字符串;
ShowMsg(strMsg);
}
}
}
void ShowMsg(string str)
{
txtMsg.AppendText(str + "\r\n");
}
#endregion
#region//click事件
private void btnBeginListen_Click(object sender, EventArgs e)
{
// 创建负责监听的套接字,注意其中的参数; ip地址类型是ipv4,socket流形式,网络协议tcp类型
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(txtIp.Text.Trim());// 将 IP 地址字符串转换为 IPAddress 实例。
//获得文本框中的IP对象;trim去掉前后空格
// 创建包含ip和端口号的网络节点对象;
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
try
{
// 将负责监听的套接字绑定到唯一的ip和端口上;
socketWatch.Bind(endPoint);
}
catch (SocketException se)
{
MessageBox.Show("异常:" + se.Message);
return;
}
// 设置监听队列的长度; 最多可容纳的等待接受的传入连接数
socketWatch.Listen(10);
// 创建负责监听的线程;
threadWatch = new Thread(WatchConnecting);
threadWatch.IsBackground = true;
threadWatch.Start();
ShowMsg("服务器启动监听成功!");
//}
}
private void btnSend_Click(object sender, EventArgs e)
{
string strMsg = txtMsgSend.Text.Trim() ;
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
byte[] arrSendMsg = new byte[arrMsg.Length + 1];
arrSendMsg[0] = 0; // 表示发送的是消息数据
Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
string strKey = "";
strKey = lbOnline.Text.Trim();
if (string.IsNullOrEmpty(strKey)) // 判断是不是选择了发送的对象;
{
MessageBox.Show("请选择你要发送的客户端!!!");
}
else
{
dict[strKey].Send(arrSendMsg);// 解决了 sokConnection是局部变量,不能再本函数中引用的问题;
ShowMsg(strMsg);
txtMsgSend.Clear();
}
}
private void btnSendToAll_Click(object sender, EventArgs e)
{
string strMsg = txtMsgSend.Text.Trim();
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
//< span style = "font-size:14px;" >
byte[] arrSendMsg = new byte[arrMsg.Length + 1]; // 上次写的时候把这一段给弄掉了,实在是抱歉哈~ 用来标识发送是数据而不是文件,如果没有这一段的客户端就接收不到消息了~~~
arrSendMsg[0] = 0; // 表示发送的是消息数据
Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);//</ span >
foreach (Socket s in dict.Values)
{
s.Send(arrMsg);
}
ShowMsg(strMsg);
txtMsgSend.Clear();
ShowMsg(" 群发完毕~~~");
}
private void button4_Click(object sender, EventArgs e)
{
Bitmap b = new Bitmap(251, 128);
ptQRCode.DrawToBitmap(b, new Rectangle(0, 0, 251, 128));
// Bitmap bmp2 = GetImageBW(b);
s = string.Join("", Enumerable.Range(0,32128).Select(a => new { x = a /128, y = a %128 })
.Select(x => b.GetPixel(x.x, x.y).GetBrightness() > 0.5f ? "1" : "0"));
string[] a1 = new string[4016];
for (int i = 0; i < 4016; i++)
{
a1[i] = string.Format("{0:X000}", Convert.ToInt32(s.Substring(i * 8, 8), 2));
if (a1[i].Length==1) a1[i] = a1[i].Insert(0,"0");
}
string a2 = string.Join("", a1);
// string a3 = a2.Insert(0,"0x");
txtMsgSend.Text = a2;
b.Save("D:\\Picture1.bmp");
}
private void over_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void btnCreate_Click(object sender, EventArgs e)
{
this.ptQRCode.Refresh();
}
#endregion
#region//其他事件
private void ptQRCode_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.White, new Rectangle() { X = 0, Y = 0, Width = 251, Height = 126 });
bc.WriteBar(textBox4.Text.Trim(), 190, 70, 80, 80, e.Graphics);
SolidBrush b1 = new SolidBrush(Color.Black);//定义单色画刷
e.Graphics.DrawString("商品名:"+textBox3.Text, new Font("宋体", 11), b1, new PointF(0, 10));
e.Graphics.DrawString("生产日期:"+textBox5.Text, new Font("宋体", 11), b1, new PointF(0, 30));
e.Graphics.DrawString("生产厂家:"+textBox6.Text, new Font("宋体", 11), b1, new PointF(0, 50));
e.Graphics.DrawString("保质期:"+textBox8.Text, new Font("宋体", 11), b1, new PointF(0, 70));
e.Graphics.DrawString("售价:" + textBox1.Text, new Font("宋体", 11), b1, new PointF(0, 90));
}
#endregion
}
}