天天看點

【FFH】Socket通訊,OpenHarmony與HarmonyOS實作資訊交流(一)

  在過去很長一段時間,我都非常希望能夠将OpenHarmony與HarmonyOS裝置進行一個關聯,但是礙于一些底層接口未完善一直無法實作。但是在前幾個月,OpenHarmony3.1 帶來了更多可能。本次,我将分享如何在搭載HarmonyOS的手機和搭載OpenHarmony的開發闆上,實作socket對話!

0. 效果示範

【FFH】Socket通訊,OpenHarmony與HarmonyOS實作資訊交流(一)

1.HarmonyOS側

1.1 建立一個JAVA工程,編寫簡單的測試頁面

【FFH】Socket通訊,OpenHarmony與HarmonyOS實作資訊交流(一)
  • ability_main.xml (首頁面)
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical">
    <TextField
        ohos:id="$+id:sendfield"
        ohos:height="200px"
        ohos:width="800px"
       ohos:bottom_margin="32vp"
        ohos:text_size="32fp"
        ohos:hint="請輸入發送資訊"
        ohos:background_element="$graphic:tfbg"
        />
    <Button
        ohos:id="$+id:btn"
        ohos:height="120px"
        ohos:width="300px"
        ohos:text="啟動"
        ohos:text_size="32fp"
       ohos:background_element="$graphic:btn"
        />
    <Text
        ohos:id="$+id:text"
        ohos:top_margin="50vp"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text_size="32fp"
        ohos:text="暫無消息"


        />
    <Text
        ohos:id="$+id:localip"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text="本機IP"
        ohos:top_margin="30vp"
        ohos:text_size="32fp"
        />
    <TextField
        ohos:id="$+id:textfield"
        ohos:height="200px"
        ohos:width="800px"
        ohos:top_margin="50vp"
        ohos:text_size="32fp"
        ohos:hint="請輸入IP位址"
        ohos:background_element="$graphic:tfbg"
        />

</DirectionalLayout>      

  這裡提供我自己編寫的一個測試頁面,但這并不是最重要的.

1.2 編寫socket功能

目前,鴻蒙的Socket通信功能隻能在JAVA側實作,并且官網對相關的功能解析不夠全面,但是足夠實作UDP通信。

這裡直接參考官網,可以快速實作Socket功能,傳送門:​​JAVA-Socket​​。

在MainAbilitySlice中編寫兩個主要函數,并結合個人情況綁定到測試按鍵上即可。

  • StartServer()
HiLog.info(LABEL_LOG, "StartServer run");

                NetManager netManager = NetManager.getInstance(null);

                if (!netManager.hasDefaultNet()) {
                    HiLog.error(LABEL_LOG,
                            "netManager.hasDefaultNet() failed");

                    return;
                }

                NetHandle netHandle = netManager.getDefaultNet();
                DatagramSocket socket = null;

                // 通過Socket綁定來進行資料傳輸
                try {
                    HiLog.info(LABEL_LOG, "wait receive data");

                    //通過getLocalIpAddress()快速擷取本機的IP位址
                    InetAddress address = netHandle.getByName(getLocalIpAddress());
                    //端口号+主機位址
                    socket = new DatagramSocket(10006, address);
                    netHandle.bindSocket(socket);
                    /*至此綁定Socket結束*/
                    HiLog.info(LABEL_LOG, "綁定成功");

                    /*監聽函數*/
                    byte[] buffer = new byte[1024];
                    DatagramPacket response = new DatagramPacket(buffer,
                            buffer.length);

                    while (true) {
                        //接收資料
                        socket.receive(response);

                        int len = response.getLength();
                        HiLog.info(LABEL_LOG, "接收成功");
                        //将資料列印到螢幕上
                        s = new String(buffer, StandardCharsets.UTF_8).substring(0,
                                len);
                                //一個textfield元件
                                text.setText(s);
                       
                        HiLog.info(LABEL_LOG, "receive data: " + s);
                    }
                } catch (IOException e) {
                    HiLog.error(LABEL_LOG, "rev IOException: ");
                }
            }      
  • sendMessage()
NetManager netManager = NetManager.getInstance(null);

                if (!netManager.hasDefaultNet()) {
                    HiLog.error(LABEL_LOG,
                            "netManager.hasDefaultNet() failed");

                    return;
                }

                NetHandle netHandle = netManager.getDefaultNet();

                // 通過Socket綁定來進行資料傳輸
                DatagramSocket socket = null;

                try {
                    //從textfield元件擷取使用者輸入的對端ip位址
                    HOST = iptf.getText();

                    InetAddress address = netHandle.getByName(HOST);
                    socket = new DatagramSocket();
                    netHandle.bindSocket(socket);

                    /*至此 已綁定對端Socket*/

                    //從一個textfield元件擷取使用者輸入的要發送的資訊。
                    String data = new String(sendtf.getText());

                    //這裡預設還是發送至對端的10006端口
                    DatagramPacket request = new DatagramPacket(data.getBytes(
                            StandardCharsets.UTF_8), data.length(),
                            address, PORT);
                    // buffer指派
                    // 發送資料
                    socket.send(request);
                    HiLog.info(LABEL_LOG, "send data: " + data);
                } catch (IOException e) {
                    HiLog.error(LABEL_LOG, "send IOException: ");
                } finally {
                    if (null != socket) {
                    }
                }      
  • getlocalip()
private String getLocalIpAddress() {
        WifiDevice wifiDevice = WifiDevice.getInstance(getContext());
        Optional<IpInfo> ipInfo = wifiDevice.getIpInfo();
        int ip = ipInfo.get().getIpAddress();
        return (ip & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + (ip >> 24 & 0xFF);
    }      
至此,接收資訊和發送資訊的函數都編寫完了。但并沒有結束,這樣的函數并不能跑在UI線程上,我們必須讓其在其他線程上運作,那必須請出我們最愛的EventRunner.

1.3 編寫Mythread類

​​EventRunner傳送門​​

  • 建立一個Mythread.java 檔案
package com.example.hoop.util;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;


public class Mythread {
    public static void inUI(Runnable runnable) {
        //傳回主線程
        EventRunner runner = EventRunner.getMainEventRunner();

        EventHandler eventHandler = new EventHandler(runner);

        eventHandler.postSyncTask(runnable);
    }

    public static void inBG(Runnable runnable) {
        EventRunner runner = EventRunner.create(true);

        EventHandler eventHandler = new EventHandler(runner);
        //投遞任務,我們的Socket函數應該投遞于此。
        eventHandler.postTask(runnable, 0, EventHandler.Priority.IMMEDIATE);
    }
}      
  • 将任務投遞給EventRunner
隻需要全部投遞給剛剛編寫的兩類函數就可以了
private void sendMessage() {
        Mythread.inBG(new Runnable() {
            @Override
            public void run() {
                NetManager netManager = NetManager.getInstance(null);
                if (!netManager.hasDefaultNet()) {
                    HiLog.error(LABEL_LOG,"netManager.hasDefaultNet() failed");
                    return;
                }
                NetHandle netHandle = netManager.getDefaultNet();
                // 通過Socket綁定來進行資料傳輸
                DatagramSocket socket = null;
                try {
                    //從textfield元件擷取使用者輸入的對端ip位址
                    HOST=iptf.getText();
                    InetAddress address = netHandle.getByName(HOST);
                    socket = new DatagramSocket();
                    netHandle.bindSocket(socket);
                    /*至此 已綁定對端Socket*/

                    //從一個textfield元件擷取使用者輸入的要發送的資訊。
                    String data = new String(sendtf.getText());
                    //這裡預設還是發送至對端的10006端口
                    DatagramPacket request = new DatagramPacket(data.getBytes(StandardCharsets.UTF_8), data.length(), address, PORT);
                    // buffer指派
                    // 發送資料
                    socket.send(request);
                    HiLog.info(LABEL_LOG,"send data: " + data);

                } catch (IOException e) {

                    HiLog.error(LABEL_LOG,"send IOException: ");
                } finally {
                    if (null != socket) {

                    }
                }
            }
        });
    }

    private void StartServer() {
        Mythread.inBG(new Runnable() {
            @Override
            public void run() {

                HiLog.info(LABEL_LOG,"StartServer run");
                NetManager netManager = NetManager.getInstance(null);
                if (!netManager.hasDefaultNet()) {

                    HiLog.error(LABEL_LOG,"netManager.hasDefaultNet() failed");
                    return;
                }
                NetHandle netHandle = netManager.getDefaultNet();
                DatagramSocket socket = null;
                // 通過Socket綁定來進行資料傳輸
                try {

                    HiLog.info(LABEL_LOG,"wait receive data");
                    //通過getLocalIpAddress()快速擷取本機的IP位址
                    InetAddress address = netHandle.getByName(getLocalIpAddress());
                    //端口号+主機位址
                    socket = new DatagramSocket(10006, address);
                    netHandle.bindSocket(socket);
                    /*至此綁定Socket結束*/
                    HiLog.info(LABEL_LOG,"綁定成功");

                    /*監聽函數*/
                    byte[] buffer = new byte[1024];
                    DatagramPacket response = new DatagramPacket(buffer, buffer.length);
                    while(true){
                        //接收資料
                        socket.receive(response);
                        int len = response.getLength();
                        HiLog.info(LABEL_LOG,"接收成功");
                        //将資料列印到螢幕上
                        s = new String(buffer, StandardCharsets.UTF_8).substring(0, len);
                        Mythread.inUI(new Runnable() {
                            @Override
                            public void run() {
                                //一個textfield元件
                                text.setText(s);
                            }
                        });

                        HiLog.info(LABEL_LOG,"receive data: " + s);
                    }
                } catch (IOException e) {
                    HiLog.error(LABEL_LOG,"rev IOException: " );
                }
            }
        });

    }      

2.OpenHarmony側

2.1 建立工程,編寫測試頁面

【FFH】Socket通訊,OpenHarmony與HarmonyOS實作資訊交流(一)
  • index.html
<div class="container"> 
   <text class="title">
     {{ title }} 
   </text> 
   <!--    <div style="width: 30%;height: 30%;background-color: blueviolet;">--> 
   <!--    </div>--> 
   <!--    <input  type="text" id="localip" placeholder="輸入本機IP"--> 
   <!--           @change="newlocalip">--> 
   <!--    </input>--> 
   <text style="width: 300px;height: 70px;">
     本機IP{{getIpAddress()}} 
   </text> 
   <button type="capsule" style="width: 300px;height: 70px;" onclick="creatScoket"> 點選建立 </button> 
   <input id="remoteip" placeholder="輸入伺服器IP" @change="newremoteip" />  
   <button type="capsule" style="width: 300px;height: 70px;margin-top: 5%;" onclick="sendMessage"> 點選連接配接 </button> 
   <input placeholder="輸入需要發送的資訊" type="text" enterkeytype="done" @change="newMessage" />  
  </div>      

2.2 編寫Socket功能

​​OpenHarmony-socket​​

import socket from '@ohos.net.socket';
import wifi from '@ohos.wifi';
export default {
    data: {
        title: "等待新的消息...",
        tcp: socket.constructTCPSocketInstance(),
        remoteip:'none',
        localip:'none',
        msg:"A NEW WORLD",
        udp:socket.constructUDPSocketInstance(),

    },
    onInit() {
      this.getIpAddress();
      this.creatScoket();
      this.title="暫無新消息"
    },
    //建立udpSocket 預設端口10006
    creatScoket: async function(){
        this.udp.bind({address: this.getIpAddress(), port: 10006, family: 1}, err => {
            if (err) {
                console.info('bind fail');
                this.title='bind fail'
                return;
            }
            this.title='bind success';
            console.info('bind success');
        })
//          this.tcp.bind({address: this.localip, port: 10006,family: 1}, err => {
//            if (err) {
//                console.log('bind fail');
//                this.title='bind fail';
//                return;
//            }
//            this.title='bind success';
//            console.log('bind success');
//        })
        //監聽收到的資訊 列印到螢幕上
       this.udp.on('message', value => {
           let buffer = value.message;
           let dataView = new DataView(buffer);
           let str = "";
           for (let i = 0;i < dataView.byteLength; ++i) {
               str += String.fromCharCode(dataView.getUint8(i))
           }
           this.title =str;
        });
    },
    sendMessage: async function(){

//        let promise1 = this.udp.connect({ address: {address: this.remoteip, port: 10006, family: 1} , timeout: 6000});
//        promise1.then(() => {
//            console.log('connect success');
//            let promise2 =this.udp.send({
//                data:this.msg
//            });
//            promise2.then(() => {
//                console.log('send success');
//            }).catch(err => {
//                console.log('send fail');
//            });
//        }).catch(err => {
//            this.title='connect fail';
//            console.log('connect fail');
//        });
        //發送資訊
        let promise = this.udp.send({
            data:this.msg,
            address: {
                address:this.remoteip,
                port:10006,
                family:1
            }
        });
        promise.then(() => {
            this.title='send success';
            console.info('send success');
        }).catch(err => {
            this.title='send fail'
            console.info('send fail');
        });
    },
    newlocalip(e){
       this.localip = e.value;
        this.title = e.value;
    },
    newremoteip(e){
        this.remoteip = e.value;
    },
    newMessage(e){
        this.msg=e.value;
        this.title=e.value;
    },
    //擷取本機ip位址
    getIpAddress(){
       let ip=wifi.getIpInfo().ipAddress;
       this.localip = (ip >> 24 & 0xFF)+"."+ ((ip >> 16) & 0xFF)+"."+((ip >> 8) & 0xFF)+"."+(ip & 0xFF);
    },

}      

3.測試

這裡為了便于測試,我們最好下載下傳一個網絡調試助手

​​網絡調試助手​​

3.1 結果

  • OpenHarmony側
【FFH】Socket通訊,OpenHarmony與HarmonyOS實作資訊交流(一)
  • HarmonyOS側
  • OpenHarmony與HarmonyOS關聯

4 結語