OpenHarmony之 网络管理 Socket 模块的使用 原创 精华

Buty9147
发布于 2022-5-25 13:30
浏览
4收藏

OpenHarmony之 网络管理 Socket 模块

@toc

1.介绍

Socket 模块可以用来进行数据传输,支持TCP和UDP两种协议。

本期将为您展示一下:
如何 使用 Socket模块实现 DAYU200开发板 和 Windows PC (SocketTool 工具)的之间的数据传输。

演示环境:

DAYU200 软件版本:OpenHarmony 3.1.5.5

DevEco Studio 3.0 Beta3
Build Version: 3.0.0.900, built on March 30, 2022
Runtime version: 11.0.13+7-b1751.21 amd64

样例使用OpenHarmony SDK版本:
“compileSdkVersion”: 8,
“compatibleSdkVersion”: 8,

2.开发步骤

2.1 权限声明

创建项目后,打开项目config.json 配置文件,在module内添加权限声明

    "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET"  //socket权限
      },
      {
        "name": "ohos.permission.GET_WIFI_INFO" //获取wifi地址权限
      }
    ]

2.2通过TCP协议方式实现

  1. import需要的socket模块及辅助模块。

    Logger是一个自定义的日志模块,方便快速记录和输出统一格式的日志。

    import socket from '@ohos.net.socket';
    //wifi模块,用于获取当前IP地址
    import wifi from '@ohos.wifi';
    //日志模块
    import logger from '../common/Logger'
    
  2. 创建一个TCPSocket连接,返回一个TCPSocket对象。

//tcp连接对象
let tcp = socket.constructTCPSocketInstance();
//目标地址和端口
let targetAddr = {
	address: '192.168.0.168',  //要通信的 PC地址,CMD--->ipconfig查看
	family: 1,
	port: 0 //UDP:7001/TCP:8001
}

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

  1. (可选)订阅TCPSocket相关的订阅事件。

    订阅了connect 、message、close 事件。message事件的回调中可以获取接收到的数据,但是一个ArrayBuffer,需要通过resolveArrayBuffer函数进行进一步解析。

    
      tcpInit() {
        tcp.on('connect', () => {
          this.status = '已连接'
          logger.getInstance(this).debug("on tcp connect success");
        });
        tcp.on('message', value => {
          this.message_recv = this.resolveArrayBuffer(value.message)
          logger.getInstance(this)
            .debug(`on tcp message:${this.message_recv},remoteInfo:${JSON.stringify(value.remoteInfo)}`);
        });
        tcp.on('close', () => {
          logger.getInstance(this).debug("on tcp close success");
        });
    
      }
    
      //解析ArrayBuffer
      resolveArrayBuffer(message: ArrayBuffer): string {
        if (message instanceof ArrayBuffer) {
          let dataView = new DataView(message)
          logger.getInstance(this).debug(`length ${dataView.byteLength}`)
          let str = ""
          for (let i = 0;i < dataView.byteLength; ++i) {
            let c = String.fromCharCode(dataView.getUint8(i))
            if (c !== "\n") {
              str += c
            }
          }
          logger.getInstance(this).debug(`message aray buffer:${str}`)
          return str;
        }
      }
    
    
  2. 绑定IP地址和端口,端口可以指定或由系统随机分配。

        //本地地址和端口
        let localAddr = {
            address: this.resolveIP(wifi.getIpInfo().ipAddress),
            port: 0
        }
    
        //bind本地地址
        tcp.bind({ address: this.localAddr.address, port: 8000, family: 1 })
          .then(() => {
            logger.getInstance(this).debug(`bind tcp success`);
          }).catch(err => {
          logger.getInstance(this).error(`bind tcp failed ${err}`);
          return
        });
        
    }
    
  3. 连接到指定的IP地址和端口。

    Button('连接TCP')
      .width('90%')
      .height(80)
      .margin({ top: 20 })
      .type(ButtonType.Capsule)
      .onClick(() => {
    	  this.tcpConnect()
      })
    
      //连接TCP
      tcpConnect() {
        tcp.getState()
          .then((data) => {
            logger.getInstance(this).debug(`====${JSON.stringify(data)}`)
            if (data.isClose) {
              this.tcpInit()
            }
    
            //开始连接
            tcp.connect(
              {
                address: { address: targetAddr.address, port: 8001, family: 1 }, timeout: 6000
              }
            ).then(() => {
              logger.getInstance(this).debug(`connect success`);
            }).catch((error) => {
              logger.getInstance(this).error(`connect failed ${JSON.stringify(error)}`);
            })
    
          })
      }
    
  4. 发送数据。

    Button('发送TCP消息')
        .width('90%')
        .height(80)
        .margin({ top: 20 })
        .type(ButtonType.Capsule)
        .onClick(() => {
        	this.tcpSend()
    	})
    
    //发送TCP消息
    tcpSend() {
        //查看状态
        tcp.getState().then((data) => {
            logger.getInstance(this).debug(`====${JSON.stringify(data)}`)
            //已连接,就发送数据
            if (data.isConnected) {
                //发送消息
                tcp.send(
                    { data: this.message_send, }
                ).then(() => {
                    logger.getInstance(this).debug(`send success`);
                }).catch((error) => {
                    logger.getInstance(this).error(`send failed ${JSON.stringify(error)}`);
                })
            } else {
                logger.getInstance(this).error(`not connect`);
            }
        })
    }
    
  5. Socket连接使用完毕后,主动关闭。

Button('关闭TCP连接')
    .width('90%')
    .height(80)
    .margin({ top: 20 })
    .type(ButtonType.Capsule)
    .onClick(() => {
    	this.tcpClose()
	})

//关闭TCP连接
tcpClose() {
    tcp.close().then(() => {
        this.status = '已断开'
        logger.getInstance(this).debug(`tcp.close success`)
    }).catch((err) => {
        logger.getInstance(this).error(`tcp.close error:${JSON.stringify(err)}`)
    })
    tcp.off('close');
    tcp.off('message');
    tcp.off('connect');
}

2.3通过UDP协议方式实现

  1. import需要的socket模块及辅助模块。

    Logger是一个自定义的日志模块,方便快速记录和输出统一格式的日志。

    import socket from '@ohos.net.socket';
    //wifi模块,用于获取当前IP地址
    import wifi from '@ohos.wifi';
    //日志模块
    import logger from '../common/Logger'
    
  2. 创建一个UDPSocket连接,返回一个UDPSocket对象。

    //udp连接对象
    let udp = socket.constructUDPSocketInstance();
    
    //目标地址和端口
    let targetAddr = {
      address: '192.168.0.168',
      family: 1,
      port: 0 //7001/8001
    }
    
    
  3. (可选)订阅UDPSocket相关的订阅事件。

    订阅了listening、message、close 事件。message事件的回调中可以获取接收到的数据,但是一个ArrayBuffer,需要通过resolveArrayBuffer函数进行进一步解析。

      aboutToAppear() {
        this.udpInit()
      }
    
      udpInit() {
        udp.on('listening', () => {
          logger.getInstance(this).debug("on udp listening success");
        });
    
        udp.on('message', value => {
          this.message_recv = this.resolveArrayBuffer(value.message)
          logger.getInstance(this)
            .debug(`on udp message:${this.message_recv},remoteInfo:${JSON.stringify(value.remoteInfo)}`);
        });
        udp.on('close', () => {
          logger.getInstance(this).debug(`on udp close success`);
        });
    
      }
    
      //解析ArrayBuffer
      resolveArrayBuffer(message: ArrayBuffer): string {
        if (message instanceof ArrayBuffer) {
          let dataView = new DataView(message)
          logger.getInstance(this).debug(`length ${dataView.byteLength}`)
          let str = ""
          for (let i = 0;i < dataView.byteLength; ++i) {
            let c = String.fromCharCode(dataView.getUint8(i))
            if (c !== "\n") {
              str += c
            }
          }
          logger.getInstance(this).debug(`message aray buffer:${str}`)
          return str;
        }
      }
    
    
  4. 绑定IP地址和端口,端口可以指定或由系统随机分配。

        //本地地址和端口
        let localAddr = {
            address: this.resolveIP(wifi.getIpInfo().ipAddress),
            port: 0
        }
    
        //bind本地地址
        udp.bind({ address: this.localAddr.address, family: 1 })
          .then(() => {
            logger.getInstance(this).debug(`bind upd success`);
          }).catch((err) => {
          logger.getInstance(this).error(`bind upd failed ${JSON.stringify(err)}`);
        })
        
    }
    
  5. 发送数据。

    Button('发送UDP消息')
        .width('90%')
        .height(80)
        .margin({ top: 50 })
        .type(ButtonType.Capsule)
        .onClick(() => {
        	this.udpSend()
    	})  
    
    //发送UDP消息
    udpSend() {
        //查看状态
        udp.getState().then((data) => {
          logger.getInstance(this).debug(`====${JSON.stringify(data)}`)
          //如果已关闭,就重新初始化
          if (data.isClose) {
            this.udpInit()
          }
          //已绑定,就发送数据
          if (data.isBound) {
            udp.send(
              {
                data: this.message_send,
                address: { address: targetAddr.address, port: 7001, family: 1 }
              }
            )
              .then(() => {
                logger.getInstance(this).debug(`send success`);
              })
              .catch((error) => {
                logger.getInstance(this).error(`send failed ${typeof error.code}`);
              })
          }
        })
      }
    
  6. Socket连接使用完毕后,主动关闭。

    Button('关闭UDP')
        .width('90%')
        .height(80)
        .margin({ top: 20 })
        .type(ButtonType.Capsule)
        .onClick(() => {
        	this.udpClose()
    	})
    
    udpClose() {
        udp.close().then(() => {
            logger.getInstance(this).debug("udp.close success");
        }).catch((err) => {
            logger.getInstance(this).error(`udp.close error:${JSON.stringify(err)}`)
        })
        udp.off('message');
        udp.off('listening');
        udp.off('close');
    }
    

3.效果展示

1). 把DAYU200开发板连上wifi(demo会显示获取的地址),注意和电脑连接的wifi相同。
2). windows系统上打开SocketTool 工具(附件已上传),分别创建TCP Server、UDP Server,监听端口分别为8001、7001

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

3). 点击,DAYU200开发板应用上 “连接” 按钮,Socket工具显示已连接。

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

4). 点击 DAYU200开发板应用上 “发送TCP消息” 按钮,Socket工具显示接收到的消息。

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

5). Socket工具上输入要回复的消息,点击 “发送数据” 按钮
OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

6). 点击 DAYU200开发板应用上 “发送UDP消息” 按钮,Socket工具UDP Server 栏 显示接收到的消息。

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

7). Socket工具上输入要回复的消息 ,点击 “发送数据” 按钮。

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

OpenHarmony之  网络管理  Socket 模块的使用-鸿蒙开发者社区

4.思考总结

  1. TCP是面向连接的协议,在收发数据前必须和对方建立可靠的连接,UDP是一个面向无连接的协议,数据传输前,源端和终端不建立连接,所以TCP的方式比UDP多一个connect的过程。

  2. getState接口的状态值有3个,isBound、isClose 是udp/tcp 都会用,isConnected是给tcp用的 。{“isBound”:true,“isClose”:false,“isConnected”:true}

  3. 用Socket 工具回复消息后,DAYU200侧如果要显示接收到的消息,需要再点发送消息一次(该问题已通过升级DAYU200 软件版本 OpenHarmony 3.2.2.3+ 得到解决)。

5.参考资料

1.HarmonyOS API参考

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-net-socket-0000001144636978

2.OpenHarmony Gitee 样例指导

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/connectivity/socket-connection.md

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
SocketSample.zip 5M 281次下载
已于2022-5-27 10:39:51修改
7
收藏 4
回复
举报
10条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

很实用的讲解,收藏了

回复
2022-5-25 14:17:57
Hello_Kun
Hello_Kun

nice!您好,这是在3.1release 运行通过的么? 我记得之前测试这个只能bind和连接上ip,收发数据没反应。😂

回复
2022-5-26 02:18:49
离北况归
离北况归 回复了 Hello_Kun
nice!您好,这是在3.1release 运行通过的么? 我记得之前测试这个只能bind和连接上ip,收发数据没反应。😂

您好,可以看了一下这个文档(https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/connectivity/socket-connection.md)是在哪个分支上的,文档是在最新master分支上的,3.1release分支上没有这个文档。应该不是在3.1release上运行通过的

回复
2022-5-26 07:40:51
Buty9147
Buty9147 回复了 Hello_Kun
nice!您好,这是在3.1release 运行通过的么? 我记得之前测试这个只能bind和连接上ip,收发数据没反应。😂

是。

如果把DAYU200 系统升级到 OpenHarmony_3.2.2.3(每日构建下载),“用Socket 工具回复消息后,DAYU200侧如果要显示接收到的消息,需要再点发送消息” 这个问题也得到了解决。

2
回复
2022-5-26 07:42:11
Hello_Kun
Hello_Kun 回复了 Buty9147
是。 如果把DAYU200 系统升级到 OpenHarmony_3.2.2.3(每日构建下载),“用Socket 工具回复消息后,DAYU200侧如果要显示接收到的消息,需要再点发送消息” 这个问题也得到了解决。

好嘞,谢谢啦,我去试试。

1
回复
2022-5-26 08:31:15
Hello_Kun
Hello_Kun 回复了 离北况归
您好,可以看了一下这个文档(https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/connectivity/socket-connection.md)是在哪个分支上的,文档是在最新master分支上的,3.1release分支上没有这个文档。应该不是在3.1release上运行通过的

好的,我去仔细看下版本区别。

1
回复
2022-5-26 08:32:29
Hello_Kun
Hello_Kun 回复了 离北况归
您好,可以看了一下这个文档(https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/connectivity/socket-connection.md)是在哪个分支上的,文档是在最新master分支上的,3.1release分支上没有这个文档。应该不是在3.1release上运行通过的

确认了,这是master分支,不是release上的。release网络与连接文档只有RPC通信。

1
回复
2022-5-26 10:22:26
入门大师小波哥
入门大师小波哥

厉害了,之前研究了半天,刷了3.2才能收发。Socket目前把广播禁止了,比较的坑

2
回复
2022-5-26 10:34:59
Buty9147
Buty9147 回复了 Buty9147
是。 如果把DAYU200 系统升级到 OpenHarmony_3.2.2.3(每日构建下载),“用Socket 工具回复消息后,DAYU200侧如果要显示接收到的消息,需要再点发送消息” 这个问题也得到了解决。

但是我发现通过每日构建下载的OpenHarmony_3.2.2.3,升级后,查看系统版本是3.2.2.5  😂

回复
2022-5-26 18:51:44
Hello_Kun
Hello_Kun 回复了 Buty9147
但是我发现通过每日构建下载的OpenHarmony_3.2.2.3,升级后,查看系统版本是3.2.2.5 😂

我也发现这个问题了,昨天安装26号编译的3.2.2.2版本,设置里看是3.2.3.2.

另外我看您这个帖子ets源码后确实3.1release能发数据,推理可知js也可以。所以今天我退回到了3.1release,发现socket可以发送数据的(js实现),官方的示例代码不可直接放在一个函数里使用,把tcp.send拆开就可以发送了。

1
回复
2022-5-28 15:51:49
回复
    相关推荐