快捷搜索:  汽车  科技

delphi 局域网用tcp还是udp(TcpSocket编程之Delphi与其他语言的字节码通信)

delphi 局域网用tcp还是udp(TcpSocket编程之Delphi与其他语言的字节码通信)该类定义在 IdBuffer 单元中,定义如下:TBytes = array of Byte;可见是一个字节数组。由于是不同语言之间的数据通信,显然,在 Delphi 中使用 Record 进行通信的可能性就不大了。所以,在 Delphi 中采用 Indy 进行数据通信的情况下,主要会使用到如下一些数据类型:该类型被定义在 IdGlobal 单元中,定义如下:TIdBytes = TBytes;TBytes 定义如下:

关键字:Tcp Scoket、Delphi、Indy、Python、Twisted

对于 Tcp Socket 编程,异种语言之间的通信在日常开发中经常会用到。今天,我们通过 Delphi 和 Python 语言来做一个 Socket 通信的示例。通信数据采用字节码(也就是字节数组)形式来直接传输。

客户端:Delphi、Indy

服务端:Python、Twisted

由于是不同语言之间的数据通信,显然,在 Delphi 中使用 Record 进行通信的可能性就不大了。所以,在 Delphi 中采用 Indy 进行数据通信的情况下,主要会使用到如下一些数据类型:

  • TIdBytes
  • TIdBuffer
8.1 TIdBytes

该类型被定义在 IdGlobal 单元中,定义如下:

TIdBytes = TBytes;

TBytes 定义如下:

TBytes = array of Byte;

可见是一个字节数组。

8.2 TIdBuffer

该类定义在 IdBuffer 单元中,定义如下:

TIdBuffer = class(TIdBaseObject);

TIdBuffer 是一个 TObject 子类,它实现了一个用于 Indy 库中输入和输出操作的缓冲区。TIdBuffer用作通信层的读和/或写缓冲器。TIdBuffer 针对在缓冲区末尾添加数据和从缓冲区开始提取数据进行了优化。

TIdBuffer 隔离了在 Indy 库支持的平台上实现的内存分配和指针操作之间的差异。

TIdBuffer实现了用于管理缓冲区类实例的大小、增长和编码的属性,包括:

Property

Usage

Capacity

为缓冲区分配的最大尺寸。

Encoding

用于添加到缓冲区的字符串值。

GrowthFactor

分配额外缓冲区存储的阈值。

Size

缓冲区存储中的字节数。

AsString

作为字符串数据类型的缓冲区内容。

在开发中,我们主要会使用其 Write 方法和 Extract 系列方法。

Write 方法:

  • TIdBuffer.Write (Byte Integer)
  • TIdBuffer.Write (Cardinal Integer)
  • TIdBuffer.Write (Int64 Integer)
  • TIdBuffer.Write (TIdBytes Integer)
  • TIdBuffer.Write (TIdIPv6Address Integer)
  • TIdBuffer.Write (TIdStream Integer)
  • TIdBuffer.Write (Word Integer)
  • TIdBuffer.Write (string TIdEncoding Integer)

Extract 系列方法:

  • Extract
  • ExtractToByte
  • ExtractToBytes
  • ExtractToCardinal
  • ExtractToIdBuffer
  • ExtractToInt64
  • ExtractToIPv6
  • ExtractToStream
  • ExtractToWord

具体可以参考 Indy 的帮助文档。

8.3 示例阐述

示例:客户端定时实时检测所在机器的屏幕分辨率上行到服务端,服务端接收到数据后,根据其屏幕分辨率随机生成一个坐标并下发给客户端,客户端将应用程序的窗体位置放置到相应的坐标上。

  • 当发送屏幕宽度时,回复 x 坐标;
  • 当发送屏幕高度时,回复 y 坐标;
  • 当发送结束时,回复结束;客户端收到结束时更改窗体位置;

通信协议:

delphi 局域网用tcp还是udp(TcpSocket编程之Delphi与其他语言的字节码通信)(1)

8.4 服务端

服务端采用 Python 及其 Twisted 框架开发,业务逻辑也比较简单,所以,不做赘述,代码如下:

# coding: utf-8 from twisted.internet.Protocol import Protocol from twisted.internet.protocol import Factory from twisted.internet.endpoints import TCP4ServerEndpoint from twisted.internet import reactor import struct import random class Device(Protocol): def connectionMade(self): print('Connected from %s:%s.' % (self.transport.getPeer().host self.transport.getPeer().port)) self.transport.write('$Welcome!$'.encode('utf-8')) def connectionLost(self reason): print('Disconnected from %s:%s.' % (self.transport.getPeer().host self.transport.getPeer().port)) print(reason) def dataReceived(self data): # 接收数据 print('Recieved from %s:%s.' % ( self.transport.getPeer().host self.transport.getPeer().port)) # 输出十六进制表示 print(data.hex()) total_length = len(data) print('命令长度: ' total_length) # 初始化宽度和高度 width = height = 0 # 解包,取长度 start length tmp end = struct.unpack('>1s H {0}s 1s'.format(total_length - 4) data) # 解包,取各个字段 start length part desc value end = struct.unpack('>1s H 1s {0}s H 1s'.format(length - 3) data) print(start length part desc value end) if part == b'W': # 宽度处理 width = value print(desc.decode('utf-8') width) # 计算 x 坐标 x = random.randint(0 width) command = struct.pack('>1s H 1s {0}s H 1s'.format(4) b'#' 4 3 b'X' b'left' x b'#') print(command.hex()) # 下发指令 self.transport.write(command) if part == b'H': # 宽度处理 height = value print(desc.decode('utf-8') height) # 计算 y 坐标 y = random.randint(0 height) command = struct.pack('>1s H 1s {0}s H 1s'.format(3) b'#' 3 3 b'Y' b'top' y b'#') print(command.hex()) # 下发指令 self.transport.write(command) if part == b'E': # 结束处理 print(desc.decode('utf-8')) command = struct.pack('>1s H 1s {0}s H 1s'.format(3) b'#' 3 3 b'E' b'end' 0 b'#') print(command.hex()) # 下发结束指令 self.transport.write(command) class DeviceFactory(Factory): def buildProtocol(self addr): print(addr) # addr为创建连接时客户端的地址 return Device() endpoint = TCP4ServerEndpoint(reactor 9123) endpoint.listen(DeviceFactory()) reactor.run()

注:代码仅用于测试,没有异常处理,多线程处理等

8.5 客户端

客户端采用 Typhon 、Indy 组件进行开发,界面如下:

delphi 局域网用tcp还是udp(TcpSocket编程之Delphi与其他语言的字节码通信)(2)

界面主要组件元素及其属性:

组件

属性

说明

TEdit

name

HostEdit

主机

TSpinEdit

name

PortSpinEdit

端口

TButton

name

ConnectButton

连接按钮

caption

连接

TButton

name

DisconnectButton

断开按钮

caption

断开

TMemo

name

DataMemo

传输的数据内容显示

readonly

True

TIdTcpClient

name

IdTCPClient

TTimer

name

Timer1

interval

1000

客户端主要代码包括:TTimer 的定时事件发送数据和读取线程接收数据。

  • TTimer 的定时事件发送数据

procedure TForm1.Timer1Timer(Sender: TObject); var w h: Integer; bytes: TIdBytes; begin // 定时器 Count:=Count 1; if Count > 99 then Count:=1; w:=Screen.Width; h:=Screen.Height; if IdTCPClient.Connected then try if Count mod 3 = 1 then begin bytes:=BuildOrder('W' 'width' w); IdTCPClient.IOHandler.Write(bytes); DataMemo.Lines.Add('发送 width: ' inttostr(w)); end; if Count mod 3 = 2 then begin bytes:=BuildOrder('H' 'height' h); IdTCPClient.IOHandler.Write(bytes); DataMemo.Lines.Add('发送 height: ' inttostr(h)); end; if Count mod 3 = 0 then begin bytes:=BuildOrder('E' 'end' 0); IdTCPClient.IOHandler.Write(bytes); DataMemo.Lines.Add('发送 end: ' inttostr(0)); end; except end; end;

在上面的代码中主要完成每次定时器触发事件,根据计数器 Count 的值来决定发送什么,发送数据时,调用了一个 BuildOrder 函数,该函数完成指令构建的过程,代码如下:

function BuildOrder(Part Desc: String; Value: UInt16): TIdBytes; var buffer: TIdBuffer; bytes: TIdBytes; l: UInt16; begin buffer:=TIdBuffer.Create; l := length(Desc) 3; buffer.Write(bytesof('#')); buffer.Write(l); buffer.Write(bytesof(Part)); buffer.Write(bytesof(Desc)); buffer.Write(Value); buffer.Write(bytesof('#')); buffer.ExtractToBytes(bytes -1 False); buffer.Free; Result:=bytes; end;

构建指令时,主要采用了 TIdBuffer 来组装数据,然后导出 TIdBytes,在组装过程中,对于字符串可以使用 bytesof 函数获取其字节数据,返回值为字节数组。

  • 读取线程接收数据

unit unitreadthread; {$mode ObjFPC}{$H } interface uses Classes SysUtils IdGlobal IdBuffer; type TReadThread = class(TTHread) private procedure AddTextToMainDataMemo; procedure ChangeMainPos; protected procedure Execute; Override; end; var info: String; X Y: Integer; implementation uses unitmain; procedure TReadThread.Execute; var bytes: TIdBytes; buffer: TIdBuffer; startflag endflag part desc: String; length value: UInt16; begin X:=0; Y:=0; while not Self.Terminated do begin if not Form1.IdTCPClient.Connected then Self.Terminate else try Form1.IdTCPClient.IOHandler.ReadBytes(bytes -1 False); buffer:=TIdBuffer.Create(bytes); startflag := buffer.ExtractToString(1); length := buffer.ExtractToUInt16(-1); if length > 10 then continue; part := buffer.ExtractToString(1); desc := buffer.ExtractToString(length - 3); value := buffer.ExtractToUInt16(-1); endflag := buffer.ExtractToString(1); info:= startflag ' ' inttostr(length) ' ' part ' ' desc ' ' inttostr(value) ' ' endflag; Synchronize(@AddTextToMainDataMemo); buffer.Free; if part = 'X' then X:=value; if part = 'Y' then Y:=value; if part = 'E' then begin info:='改变窗体位置:(' inttostr(X) ' ' inttostr(Y) ')'; Synchronize(@AddTextToMainDataMemo); Synchronize(@ChangeMainPos); end; except end; end; end; procedure TReadThread.AddTextToMainDataMemo; begin Form1.DataMemo.Lines.Add(info); end; procedure TReadThread.ChangeMainPos; begin Form1.Left:=X; Form1.Top:=Y; end; end.

读取线程在收到数据时,首先通过 IdTCPClient.IOHandler.ReadBytes(bytes -1 False) 来获取接收到的数据,然后采用 TIdBuffer.Create 创建 TIdBuffer 的实例,其参数为 bytes,最后通过 TIdBuffer 的 Extract 系列函数分解其中的数据。

注:以上代码仅用于测试

8.6 运行效果

delphi 局域网用tcp还是udp(TcpSocket编程之Delphi与其他语言的字节码通信)(3)

猜您喜欢: