socket通信——多角度控制LED灯亮灭

简介: 今天以物联网网关(网关链接)以服务器,在多个客户端就做一个非常简单的功能:点亮或熄灭网关上的LED灯。目前想到了三种方式,分别是:TCP&UDP测试工具、自编Java客户端和Mono Android客户端。相信这会很有意思的。   1、服务器端 在使用或编写客户端之前,首先来看看服务器端代码,其专门通过串口烧进网关内部 OutputPort led = new O

今天以物联网网关(网关链接)以服务器,在多个客户端就做一个非常简单的功能:点亮或熄灭网关上的LED灯。目前想到了三种方式,分别是:TCP&UDP测试工具、自编Java客户端和Mono Android客户端。相信这会很有意思的。得意  


1、服务器端

在使用或编写客户端之前,首先来看看服务器端代码,其专门通过串口烧进网关内部

OutputPort led = new OutputPort((Cpu.Pin)GPIO_NAMES.PF8, false);

Socket sc;
Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建tcp套接字
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("192.168.13.200"), 8888);//网关IP已静态配置,端口自选
try
{
	ss.Bind(iep);
	ss.Listen(3);
	Debug.Print("create server ok");
}
catch
{
	Debug.Print("connection failed");
}           

然后等待客户端的数据并在终端打印,根据所收信息来判断灯亮灭,最后向客户端发反馈

while (true)
{
	if ((sc = ss.Accept()) != null)
	{
		Debug.Print("someone connected:" + sc.RemoteEndPoint.ToString());
		while (true)
		{
			Array.Clear(data, 0, data.Length);//一定要清
			sc.Receive(data, 16, 0);//从java客户端收到的包括\r\n
			string str = new string(System.Text.Encoding.UTF8.GetChars(data));
			Debug.Print(str);
			Debug.Print(str.Length.ToString());
			if (str.IndexOf("ON") >= 0) // = 号不要忽略了
			{
				led.Write(true);
			}
			else if (str.IndexOf("OFF") >= 0)
			{
				led.Write(false);
			}

			Debug.Print("recv ok,about to send ");
			sc.Send(System.Text.Encoding.UTF8.GetBytes(str.IndexOf("ON") >= 0 ? "ON\r\n" : "OFF\r\n"));//为方便java中的readline,添加了行结束符
		}
	}
 }

注释的地方基本上是我犯错的地方


2、客户端

2.1、TCP&UDP测试工具

测试效果如下:



2.2、Java客户端

代码如下:

public class LightLED {

	private Socket client;
	private String host = "192.168.13.200";
	private int port = 8888;
	
	private String on = "ON";
	private String off = "OFF";
	
	public static void main(String[] args) throws InterruptedException,IOException {
		// TODO Auto-generated method stub
		new LightLED().doLED();
	}
	
	public LightLED() throws IOException{
		client = new Socket(host,port);
		System.out.println("connected...");
	}
	
	public void doLED() throws IOException,InterruptedException {
		try {
			BufferedReader br = getReader(client);
			PrintWriter pw = getWriter(client);
			while (true){
				pw.println(on); //不能用print,为什么?而且发过去的包括\r\n
				System.out.println("LED: " + br.readLine());				
				Thread.sleep(1000);
				
				pw.println(off);
				System.out.println("LED: " + br.readLine());				
				Thread.sleep(1000);
			}
		} catch (IOException e) {
			// TODO: handle exception
		}
	}
	
	private PrintWriter getWriter(Socket s) throws IOException{
		OutputStream ous = s.getOutputStream();
		return new PrintWriter(ous,true);
	}
	
	private BufferedReader getReader(Socket s) throws IOException {
		InputStream ins = s.getInputStream();
		return new BufferedReader(new InputStreamReader(ins));
	}
}


VS调试窗口:



Java调试窗口:



2.3、Mono Android客户端

其实用C#也可以写android程序的,并且还可跨平台,也轻松移植到IOS上。它使用的框架是Mono,开发环境是Xamarin,以前叫Mono Develop。网上有很多教程,我在这里就不细说了。我本人的相关软件放在了这里

之前我也没用过mono写过android程序,所以今天是个很好的尝试。事实证明算这一客户端最有趣了,待我细细道来。。

我突然想改变一下前面的想法,由于网关上有3个LED灯,于是这次想让客户端同时操作这3个灯。当我发一个数值时,相应的灯亮或灭。具体细则是这样规定的:我所发送的数值范围是0-7,共8个数,化成二进制恰好可以代表3个灯的状态,1为亮,0为灭,就这样简单定义。而且,为了使得多个android客户端可同时登陆服务器,利用多线程方式来实现。主线程只负责接收客户端连接,每个客户端对应一个单独线程来与服务器通信。

既然这样,服务器端代码就得变了,见下:

首先还是服务器的初使化:

Socket sc = null;
Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建tcp套接字
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("192.168.13.200"), 8888);//网关IP已静态配置,端口自选
try
{
	ss.Bind(iep);
	ss.Listen(5);
	Debug.Print("create server ok");
}
catch
{
	Debug.Print("connection failed");
}           

然后无限循环接收客户连接:

while (true)
{
	byte[] recedata = new byte[1];
	if ((sc = ss.Accept()) != null)
	{
		Debug.Print("someone connected:" + sc.RemoteEndPoint.ToString());
		ClientThread ch = new ClientThread(sc);//创建客户线程类
		Thread t = new Thread(new ThreadStart(ch.service));
		t.Start();                                                       
	}
}

相信已发现上面用到了一个类ClientThread,这是我自定义的:

internal class ClientThread
{
	enum GPIO_NAMES
	{
		PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15,
		PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15,
		PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15,
		PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15,
		PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7, PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15,
		PF0, PF1, PF2, PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10, PF11, PF12, PF13, PF14, PF15,
		PG0, PG1, PG2, PG3, PG4, PG5, PG6, PG7, PG8, PG9, PG10, PG11, PG12, PG13, PG14, PG15
	};

	private Socket sc;
	OutputPort led1 = new OutputPort((Cpu.Pin)GPIO_NAMES.PF8, false);//第一个LED
	OutputPort led2 = new OutputPort((Cpu.Pin)GPIO_NAMES.PF7, false);//第二个LED
	OutputPort led3 = new OutputPort((Cpu.Pin)GPIO_NAMES.PF6, false);//第三个LED

	public ClientThread(Socket s)
	{
		sc = s;
		//sc.ReceiveTimeout = 60000; // 1分钟内若未收到数据,则关闭连接。由于在虚拟设备中反应很慢,所以就注释掉了          
	}

	public void service()
	{           
		byte[] recedata = new byte[1];//目前只接收0-7的某个数
		try
		{
			while (sc.Receive(recedata) != 0)
			{
				Debug.Print(recedata[0].ToString());
				doLED(recedata);//根据数值点亮或熄灭灯
				Array.Clear(recedata,0,1);
			}
		}
		catch (System.Exception ex)
		{
			sc.Close();
			Debug.Print("rece timeout" + ex.Message);
		}
		
	}
	private void doLED(byte[] recedata)
	{           
		//感觉下面写得不太简洁
		byte L1 = (byte)(recedata[0] >> 2);
		byte L2 = (byte)((recedata[0] & 3) >> 1);
		byte L3 = (byte)(recedata[0] & 1);
		if (L1 == 1)
		{
			led1.Write(true);
		}
		else
		{
			led1.Write(false);
		}
		if (L2 == 1)
		{
			led2.Write(true);
		}
		else
		{
			led2.Write(false);
		}
		if (L3 == 1)
		{
			led3.Write(true);
		}
		else
		{
			led3.Write(false);
		}
	}
}

好,以上就是修改过的服务器端。下面是用c#写的android客户端。安装好相关软件,打开Xamarin,创建android工程:



相关细节就不详述了,具体可参考官方文档,写得很详细,其实还是用到了android开发的相关概念比如Activity

IDE已经为你生成了相关框架代码,生成了类MainActivity,继承自Activity,重点修改其OnCreate方法,它在界面出现时被调用。

首先创建一些私有量:

private Socket sc = null;
private string host = "192.168.13.200";//服务器端IP和端口
private int port = 8888;

修改OnCreate方法如下:

protected override void OnCreate (Bundle bundle)
{
	base.OnCreate (bundle);
	var layout = new LinearLayout (this);
	layout.Orientation = Orientation.Vertical;

	var lbl = new TextView (this);
	lbl.Text = "hello,xmarin.android";

	sc= new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
	try
	{
		sc.Connect(IPAddress.Parse(host),port);//连接服务器
		lbl.Text = "connected with gateway server";
	}
	catch {
		lbl.Text = "conn failed";
	}

	EditText et = new EditText (this);
	var btn = new Button (this);
	btn.Text = "click and send";
	btn.Click += (sender, e) => {	//添加事件处理
		doLED(et.Text);
	};

	layout.AddView (lbl);
	layout.AddView (et);
	layout.AddView (btn);
	SetContentView (layout);
}


一旦button被点击,执行发送:
public void doLED(string leddata)
{
	byte num = Convert.ToByte (leddata);//获取EditText的数值
	byte[] senddata = new byte[1] {num};
	sc.Send (senddata);
}

最后来一张界面截图:



Yoxi.. 客户端代码就是这样,接下来配置好android模拟器,先在模拟器中跑一跑。在服务器端设一个断点,首先开启服务器,然后再开启Xamarin中的程序。一段时间的等待后(android模拟器启动忒慢),部署程序到模拟器,自动启动了客户端。于是首先在文本框中输入7,使3个灯全亮,见下图:



果然,三灯全亮。再输入4,使第1灯亮,其余二个灯全灭:



最后输入0,不用说,全灭。如果没有反应,可尝试给网关重新上电。

好,最后一招,将客户端部署到android手机。注意,要提前破解Mono for Android,而且不要简简单单地把bin目录中的apk安装到手机中,我试过,不能启动。正确做法是将手机连接PC,安装好驱动后,在IDE中可看到设备:



我在第一次部署到手机时,碰到了这个问题:FastDev directory creation failed。经查询这里有了答案:

https://bugzilla.xamarin.com/show_bug.cgi?id=14474

所以我首先采用release模式部署,再改成debug模式,最后部署成功。

启动android客户端,连上路由器使手机与网关在同一个局域网内。先后输入7,4,0,三灯反应正常:



VS调试窗口如下:


OK,到目前为止,我的目标总算是实现了,能在android跑还是挺欢喜的。逻辑上没问题,至于界面的美化嘛,慢慢修改呗。

其实,又有了个新想法,可创建一个html5 移动web应用,放在手机上运行,应该也可以。那个前端东西我不是很熟悉就不做了,感觉应该也不难。或者这样也可以,以网关为服务器,以Netduino为客户端,通过红外操作netduino,从而控制网关,这样也不错。今天暂搁笔于此,以后有想法再补充。再见

目录
相关文章
|
14天前
|
缓存 监控 Java
Java Socket编程最佳实践:优化客户端-服务器通信性能
【6月更文挑战第21天】Java Socket编程优化涉及识别性能瓶颈,如网络延迟和CPU计算。使用非阻塞I/O(NIO)和多路复用技术提升并发处理能力,减少线程上下文切换。缓存利用可减少I/O操作,异步I/O(AIO)进一步提高效率。持续监控系统性能是关键。通过实践这些策略,开发者能构建高效稳定的通信系统。
|
14天前
|
Java 应用服务中间件 开发者
【实战指南】Java Socket编程:构建高效的客户端-服务器通信
【6月更文挑战第21天】Java Socket编程用于构建客户端-服务器通信。`Socket`和`ServerSocket`类分别处理两端的连接。实战案例展示了一个简单的聊天应用,服务器监听端口,接收客户端连接,并使用多线程处理每个客户端消息。客户端连接服务器,发送并接收消息。了解这些基础,加上错误处理和优化,能帮你开始构建高效网络应用。
|
14天前
|
IDE Java 开发工具
从零开始学Java Socket编程:客户端与服务器通信实战
【6月更文挑战第21天】Java Socket编程教程带你从零开始构建简单的客户端-服务器通信。安装JDK后,在命令行分别运行`SimpleServer`和`SimpleClient`。服务器监听端口,接收并回显客户端消息;客户端连接服务器,发送“Hello, Server!”并显示服务器响应。这是网络通信基础,为更复杂的网络应用打下基础。开始你的Socket编程之旅吧!
|
14天前
|
Java 数据挖掘 开发者
Java网络编程进阶:Socket通信的高级特性与应用
【6月更文挑战第21天】Java Socket通信是分布式应用的基础,涉及高级特性如多路复用(Selector)和零拷贝,提升效率与响应速度。结合NIO和AIO,适用于高并发场景如游戏服务器和实时数据分析。示例展示了基于NIO的多路复用服务器实现。随着技术发展,WebSockets、HTTP/2、QUIC等新协议正变革网络通信,掌握Socket高级特性为应对未来挑战准备。
|
14天前
|
网络协议 安全 Java
Java网络编程入门涉及TCP/IP协议理解与Socket通信。
【6月更文挑战第21天】Java网络编程入门涉及TCP/IP协议理解与Socket通信。TCP/IP协议包括应用层、传输层、网络层和数据链路层。使用Java的`ServerSocket`和`Socket`类,服务器监听端口,接受客户端连接,而客户端连接指定服务器并交换数据。基础示例展示如何创建服务器和发送消息。进阶可涉及多线程、NIO和安全传输。学习这些基础知识能助你构建网络应用。
23 1
|
14天前
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
|
14天前
|
安全 Java 网络安全
Java Socket编程教程:构建安全可靠的客户端-服务器通信
【6月更文挑战第21天】构建安全的Java Socket通信涉及SSL/TLS加密、异常处理和重连策略。示例中,`SecureServer`使用SSLServerSocketFactory创建加密连接,而`ReliableClient`展示异常捕获与自动重连。理解安全意识,如防数据截获和中间人攻击,是首要步骤。通过良好的编程实践,确保网络应用在复杂环境中稳定且安全。
|
5天前
|
Java API 开发者
Java网络编程基础与Socket通信实战
Java网络编程基础与Socket通信实战
|
3天前
|
Java API 开发者
Java网络编程基础与Socket通信实战
Java网络编程基础与Socket通信实战
|
14天前
|
Java 数据安全/隐私保护
深入剖析:Java Socket编程原理及客户端-服务器通信机制
【6月更文挑战第21天】Java Socket编程用于构建网络通信,如在线聊天室。服务器通过`ServerSocket`监听,接收客户端`Socket`连接请求。客户端使用`Socket`连接服务器,双方通过`PrintWriter`和`BufferedReader`交换数据。案例展示了服务器如何处理每个新连接并广播消息,以及客户端如何发送和接收消息。此基础为理解更复杂的网络应用奠定了基础。