基于TLSSocket的通信能力

基于TLSSocket的通信能力

HarmonyOS
2024-07-25 10:08:30
浏览
已于2024-7-25 18:24:44修改
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
莫莫莫小贝

Socket进行数据传输的能力,支持TLSSocket能力。

场景一 :用户场景需要实现网上银行叫号及其他需要建立即时通信传输数据时,可通过TCP/TLSSocket方式加密传输数据。

场景二 :数据发送过程中 当发送方每次写入数据 < 套接字(Socket)缓冲区大小;接收方读取套接字(Socket)缓冲区数据不够及时,可能存在粘包/黏包情况,需要做对应的接收处理。

方案描述

场景一

用户场景需要实现网上银行叫号及其他需要建立即时通信传输数据时,可通过tcp/tlsSocket方式加密传输数据。

方案

调用网络模块socket中TLSSocket能力建立通信连接,有关能力可参考文档:@ohos.socket.TLSSocket

主要步骤可参考如下:

1. 引入socket能力,创建一个TLSSocket连接。

2. connect连接绑定服务器 IP 和端口号,bind绑定本地网络ip地址。

3. 双向认证上传客户端 CA 证书及数字证书;单向认证上传客户端 CA 证书。

4. 订阅TLSSocket相关消息事件。

5. 发送数据。

6. TLSSocket连接使用完毕后,主动关闭。

核心代码

//获取本机ip地址信息 
function getLocalIp(): string { 
  const ipInfo = wifiManager.getIpInfo() 
  console.log(tag + 'ipInfo : ' + JSON.stringify(ipInfo)); 
  const localIp = int2ip(ipInfo.ipAddress) 
  console.log(tag + 'localHost : ' + localIp); 
  return localIp; 
}
//从wifi获取地址格式转换 
function int2ip(n: number): string { 
  return `${(n & 0xFF000000) >>> 24}.${(n & 0x00FF0000) >>> 16}.${(n & 0x0000FF00) >>> 8}.${(n & 0xFF)}` 
} 
 
class SocketInfo { 
  message: ArrayBuffer = new ArrayBuffer(1); 
  remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo; 
} 
 
export class TlsSocketTest { 
  host: string = 'test.com'; 
  port: number = 443; 
  ca: string[] = [ 
    "-----BEGIN CERTIFICATE-----\n... (certificate data) ...\n-----END CERTIFICATE-----" 
  ]; 
 
  async connectTest(): Promise<void> { 
    try { 
      console.debug(tag + 'connectTest'); 
      // 本地IP 
      const localIp = getLocalIp(); 
      // 网络是否正常 
      let net = connection.hasDefaultNetSync(); 
      console.log(tag + 'net : ' + JSON.stringify(net)); 
      console.log(tag + 'host : ' + this.host + ', port : ' + this.port); 
      // 服务器的域名解析,得到服务器IP 
      const handler = await connection.getDefaultNet(); 
      const netAddressArray: connection.NetAddress[] = await handler.getAddressesByName(this.host); 
      console.log(tag + 'netAddressArray : ' + JSON.stringify(netAddressArray)); 
      const serverIp = netAddressArray[0].address; 
      console.log(tag + 'serverIp : ' + serverIp); 
      // 创建TLSSocket 
      const tlsSocket = socket.constructTLSSocketInstance(); 
      let tlsConnectOptions: socket.TLSConnectOptions = { 
        address: { 
          address: serverIp, 
          port: this.port 
        }, 
        secureOptions: { 
          ca: this.ca, 
        } 
      }; 
      // 绑定本地IP和Port 
      tlsSocket.bind({ address: localIp, family: 1, port: 0 }, (err:BusinessError) => { 
        if (err) { 
          console.log('bind fail'); 
          return; 
        } 
        console.log('bind success'); 
      }); 
 
      tlsSocket.on('message', (msgFromServer: SocketInfo) => { 
        console.log(tag + 'on(message) : ' + JSON.stringify(msgFromServer)); 
        let uint8Array = new Uint8Array(msgFromServer.message); 
        let textDecoder = util.TextDecoder.create(); 
        let res = textDecoder.decodeWithStream(uint8Array); 
        console.log(tag + 'on(message) , res : ' + JSON.stringify(res)); 
      }) 
      // 注册连接成功事件 
      tlsSocket.on('connect', () => { 
        console.log(tag + 'on(connect)'); 
      }) 
      // 开始连接服务器 
      tlsSocket.connect(tlsConnectOptions).then(() => { 
        console.log(tag + 'tlsSocket connect success'); 
      }) 
 
      tlsSocket.send("hello zhtest").then(() => { 
        console.log(tag + 'tlsSocket send success'); 
      }) 
      tlsSocket.close().then(() => { 
        console.log(tag + 'tlsSocket close success'); 
      }) 
    } catch (err) { 
      console.log(tag + 'connectTest err : ' + JSON.stringify(err)); 
    } 
  } 
}

场景二

数据发送过程中 当发送方每次写入数据 < 套接字(Socket)缓冲区大小;接收方读取套接字(Socket)缓冲区数据不够及时,可能存在粘包/黏包情况,需要做对应的接收处理。

方案

1. 发送方和接收方规定固定大小的缓冲区,也就是发送和接收都使用固定大小的byte[]数组长度,当字符长度不够时使用空字符弥补。

2. 以特殊的字符结尾,比如以“\n”结尾,这样我们就知道结束字符,从而避免了粘包问题。

核心代码

可根据当前服务端/客户端收发数据的实际情况,调用socket中提供setextraoptions能力设置较合适发送/接收数据的缓存区间大小做处理。

let tls: socket.TLSSocket = socket.constructTLSSocketInstance(); 
let bindAddr: socket.NetAddress = { 
  address: '192.168.xx.xxx', 
  port: 8080 
} 
tls.bind(bindAddr, (err: BusinessError) => { 
  if (err) { 
    console.log('bind fail'); 
    return; 
  } 
  console.log('bind success'); 
}); 
//额外参数设置 
let tcpExtraOptions: socket.TCPExtraOptions = { 
  receiveBufferSize: 1000,  //接收数据的缓存大小 
  sendBufferSize: 1000,  //发送数据的缓存大小 
  socketTimeout: 15000 
} 
tls.setExtraOptions(tcpExtraOptions, (err: BusinessError) => { 
  if (err) { 
    console.log('setExtraOptions fail'); 
    return; 
  } 
  console.log('setExtraOptions success'); 
});

2. 客户端发送数据时携带特殊符号做区分处理。

// 创建TLSSocket 
let tlsSocket = socket.constructTLSSocketInstance(); 
let tlsConnectOptions: socket.TLSConnectOptions = { 
  address: { 
    address: this.serverIp, 
    port: this.port 
  }, 
  secureOptions: {} 
}; 
// 绑定本地IP和Port 
tlsSocket.bind({ address: localIp, family: 1, port: 0 }) 
  .then(() => { 
    console.log(tag + 'tlsSocket bind success'); 
    // 注册接收服务器响应事件 
    tlsSocket.on('message', (msgFromServer: SocketInfo) => { 
      console.log(tag + 'on(message) : ' + JSON.stringify(msgFromServer)); 
    }) 
    // 注册连接成功事件 
    tlsSocket.on('connect', () => { 
      console.log(tag + 'on(connect)'); 
    }) 
    // 开始连接服务器 
    tlsSocket.connect(tlsConnectOptions) 
      .then(() => { 
        console.log(tag + 'tlsSocket connect success'); 
 
        tlsSocket.send("aaaaaccc \n").then(() => { 
          console.log(tag + 'msg1 send success'); 
        }).catch((err: BusinessError) => { 
          console.log(tag + 'msg1 send err : ' + JSON.stringify(err)); 
        }); 
 
        tlsSocket.send("bbbbbbddd \n").then(() => { 
          console.log(tag + 'msg2 send success'); 
        }).catch((err: BusinessError) => { 
          console.log(tag + 'msg2 send err : ' + JSON.stringify(err)); 
        }); 
 
      }) 
      .catch((err: BusinessError) => { 
        console.log(tag + 'tlsSocket connect err : ' + JSON.stringify(err)); 
      }) 
  }) 
  .catch((err: BusinessError) => { 
    console.log(tag + 'tlsSocket bind err : ' + JSON.stringify(err)); 
  })

常见问题

Q:socket调用返回2301115原因及解决方式。

A : 可能是连接参数中超时时间设置短导致,可将timeout参数设置为15s,如不设置默认超时时间为5s。

Q:socket连接时on('error')错误码返回为-1原因。

A:TCP Socket返回-1错误码通常表示连接失败或发送数据失败。

这个错误码可能由:1、当前通信连接被拒绝;2、网络连接失败 3、连接或发送数据时超时导致。

Q:socket中ping命令方式实现情况。

A:为防止泛洪破坏,网络模块暂不直接提供ping能力接口,参考三方库中能力封装处理:https://github.com/dgibson/iputils/blob/master/ping.c

分享
微博
QQ
微信
回复
2024-07-25 11:39:11
相关问题
基于原生能力组件封装
364浏览 • 1回复 待解决
基于原生水印添加能力
659浏览 • 1回复 待解决
基于原生能力网络状态感知
439浏览 • 1回复 待解决
基于@ohos/axios网络请求能力
392浏览 • 1回复 待解决
基于libuv异步库进行线程通信
1773浏览 • 0回复 待解决
基于原生能力跨应用跳转
744浏览 • 1回复 待解决
基于原生能力网络加载性能分析
669浏览 • 1回复 待解决
HarmonyOS事件通信能力解决方案
706浏览 • 1回复 待解决
基于ArkUI实现类似.9图拉伸能力
450浏览 • 1回复 待解决
基于原生能力实现图文混排
381浏览 • 1回复 待解决
基于原生能力设备唯一ID方案
671浏览 • 1回复 待解决
针对IPv6地址TLSSocket bind操作无响应
238浏览 • 1回复 待解决
基于webView嵌套滚动
450浏览 • 1回复 待解决
基于RichEditor评论编辑
604浏览 • 1回复 待解决
需要web组件JSBridge通信demo
150浏览 • 1回复 待解决
鸿蒙是基于Android吗?
11706浏览 • 2回复 已解决
基于WebDownloadDelegatePDF下载预览
420浏览 • 1回复 待解决
基于原生应用主题开发
430浏览 • 1回复 待解决
基于bindSheet半模态弹窗
842浏览 • 1回复 待解决
基于measure实现文本测量
619浏览 • 1回复 待解决
基于滚动组件手势处理
360浏览 • 1回复 待解决