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

今天以物联网网关(网关链接)以服务器,在多个客户端就做一个非常简单的功能:点亮或熄灭网关上的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,从而控制网关,这样也不错。今天暂搁笔于此,以后有想法再补充。再见

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值