Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说「12.网络编程」4.Linux环境下Tcp Socket 编程,希望能够帮助你!!!。
在 Linux 环境下使用 Indy 组件进行 TCP Socket 编程也非常的简单,一般情况下,Linux 下编写 TCP Socket 应用程序是不需要有 UI 的,所以,今天我们一起来在 Linux 环境下实现一个 shell 命令行的 TCP Socket 应用程序。
在 Linux 环境下编写 Object Pascal 程序,就需要使用 Lazarus 或者 CodeTyphon 了,我们在本节使用 CodeTyphon 来编写,同时在 Linux 下编写 shell 命令行程序,日志系统是不可缺少的,所以,我先介绍 Lazarus 下的日志系统。
在 Lazarus 中, TEventLog 是一个可用于向系统日志发送消息的组件。如果不存在系统日志(例如在 Windows 95/98 或 DOS 上),则将消息写入文件。消息可以使用一般的 Log 调用,或专门的 Warning、Error、Info或Debug调用来记录,这些调用具有预定义的事件类型。
TEventLog 的主要属性:
TEventLog 的主要方法:
|
方法 |
说明 |
|
Log() |
将消息记录到系统日志中 |
|
Warning() |
记录警告消息 |
|
Error() |
将错误消息记录 |
|
Debug() |
记录调试消息 |
|
Info() |
记录信息性消息 |
在 Linux shell 下开发 Tcp Socket 服务端,需要创建一个 Console Application,然后新建的应用程序类中编写代码。仍然采用上一节的示例来说明。
示例:客户端定时实时检测所在机器的屏幕分辨率上行到服务端,服务端接收到数据后,根据其屏幕分辨率随机生成一个坐标并下发给客户端,客户端将应用程序的窗体位置放置到相应的坐标上。
客户端代码仍然是上一节的代码,在此不再赘述。
首先,打开 CodeTyphon,选择 Project -> New Project,然后选择 Console Application,设置 Application class name 和 Title,单击 Ok,此时生成的代码如下:
program Project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes, SysUtils, CustApp
{ you can add units after this };
type
{ TTcpApplication }
TTcpApplication = class(TCustomApplication)
protected
procedure DoRun; override;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure WriteHelp; virtual;
end;
{ TTcpApplication }
procedure TTcpApplication.DoRun;
var
ErrorMsg: String;
begin
// quick check parameters
ErrorMsg:=CheckOptions('h', 'help');
if ErrorMsg<>'' then begin
ShowException(Exception.Create(ErrorMsg));
Terminate;
Exit;
end;
// parse parameters
if HasOption('h', 'help') then begin
WriteHelp;
Terminate;
Exit;
end;
{ add your program here }
// stop program loop
Terminate;
end;
constructor TTcpApplication.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
StopOnException:=True;
end;
destructor TTcpApplication.Destroy;
begin
inherited Destroy;
end;
procedure TTcpApplication.WriteHelp;
begin
{ add your help code here }
writeln('Usage: ', ExeName, ' -h');
end;
var
Application: TTcpApplication;
begin
Application:=TTcpApplication.Create(nil);
Application.Title:='Tcp Application';
Application.Run;
Application.Free;
end.
生成的代码为设置的类名的类代码,其中:
下面,我们根据需求来实现 Tcp Server 的功能,由于需要在程序执行过程中记录日志,所以先在该类的声明部分添加日志和 TIdTCPServer 变量,如下代码:
private
TcpServer: TIdTCPServer;
Logger: TEventLog;
接下来,声明日志输出功能和 Tcp Server 需要实现的事件处理功能,对于日志,我们需要输出消息类数据和错误类信息,代码如下:
procedure OutputInfoLog(Info: String); // 消息日志
procedure OutputErrorLog(Error: String); // 错误日志
procedure ServeExecute(AContext: TIdContext); // 与客户端交互数据
procedure ServeConnecte(AContext: TIdContext); // 客户端建立连接时触发
procedure ServeDisconnect(AContext: TIdContext); // 客户端断开连接时触发
procedure ServeException(AContext: TIdContext; AException: Exception); // 与客户端交互发生异常时触发
实现日志输出功能:
procedure TTcpApplication.OutputInfoLog(Info: String);
var
LogContent: String;
begin
LogContent:=FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', Now) + ' -info- ' + Info;
Writeln(LogContent);
Logger.Info(LogContent);
end;
procedure TTcpApplication.OutputErrorLog(Error: String);
var
LogContent: String;
begin
LogContent:=FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', Now) + ' -error- ' + Error;
Writeln(LogContent);
Logger.Info(LogContent);
end;
实现客户端建立连接、断开连接、发生异常时触发的事件:
procedure TTcpApplication.ServeConnecte(AContext: TIdContext);
var
Info: String;
begin
// 连接
Info := AContext.Connection.Socket.Binding.PeerIP + ':' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - has created connection.';
OutputInfoLog(Info);
end;
procedure TTcpApplication.ServeDisconnect(AContext: TIdContext);
var
Info: String;
begin
// 断开连接
Info := AContext.Connection.Socket.Binding.PeerIP + ':' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - has closed connection.';
OutputInfoLog(Info);
end;
procedure TTcpApplication.ServeException(AContext: TIdContext; AException: Exception);
var
Info: String;
begin
// 异常
Info := AContext.Connection.Socket.Binding.PeerIP + ' ' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - Error: ' + AException.Message;
OutputErrorLog(Info);
end;
定义交互数据结构:
// 通信数据结构
TCommBlock = Record
Part: String[1]; // 客户端上传: W-屏幕宽度, H-屏幕高度, E-结束; 服务端下发: X-水平坐标, Y-垂直坐标, E-结束
Desc: String[16]; // 描述
Value: Integer; // 数据值
end;
实现数据交互功能:
procedure TTcpApplication.ServeExecute(AContext: TIdContext);
var
CommBlock: TCommBlock;
bytes: TIdBytes;
W, H, X, Y: Integer;
Host: String;
begin
// 执行
if AContext.Connection.Connected then
begin
try
OutputInfoLog('----- Start -----');
Host:=AContext.Connection.Socket.Binding.PeerIP;
AContext.Connection.IOHandler.ReadBytes(bytes, SizeOf(CommBlock), False);
BytesToRaw(bytes, CommBlock, SizeOf(CommBlock));
OutputInfoLog('Recv - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
if CommBlock.Part = 'W' then W:=CommBlock.Value;
if CommBlock.Part = 'H' then H:=CommBlock.Value;
if CommBlock.Part = 'E' then
begin
Randomize;
X:=Random(W);
Y:=Random(H);
// 发送水平坐标
CommBlock.Part:='X';
CommBlock.Desc:='水平坐标';
CommBlock.Value:=X;
OutputInfoLog('Send - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
AContext.Connection.IOHandler.Write(RawToBytes(CommBlock, SizeOf(CommBlock)));
// 发送垂直坐标
CommBlock.Part:='Y';
CommBlock.Desc:='垂直坐标';
CommBlock.Value:=Y;
OutputInfoLog('Send - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
AContext.Connection.IOHandler.Write(RawToBytes(CommBlock, SizeOf(CommBlock)));
// 发送结束标志
CommBlock.Part:='E';
CommBlock.Desc:='结束';
CommBlock.Value:=0;
OutputInfoLog('Send - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
AContext.Connection.IOHandler.Write(RawToBytes(CommBlock, SizeOf(CommBlock)));
end;
except
ON E: Exception do
OutputErrorLog('ERROR: ' + Host + ' - ' + E.Message);
end;
end;
end;
实现主程序:
// 日志初始化
Logger:=TEventLog.Create(Self);
Logger.FileName:='tcp_serve.log';
Logger.LogType:=ltFile;
Logger.DefaultEventType:=etDebug;
Logger.AppendContent:=True;
Logger.Active:=True;
// Server 端初始化
TcpServer:=TIdTCPServer.Create(Self);
TcpServer.DefaultPort:=8081;
// 设置 Server 端事件处理
TcpServer.OnConnect:=@ServeConnecte;
TcpServer.OnDisconnect:=@ServeDisconnect;
TcpServer.OnExecute:=@ServeExecute;
TcpServer.OnException:=@ServeException;
// 启动 Server
TcpServer.StartListening;
TcpServer.Active:=True;
OutputInfoLog('Started at 8081');
while True do readln();
// 释放资源
Logger.Destroy;
TcpServer.StopListening;
TcpServer.Destroy;
// stop program loop
Terminate;
完整代码如下:
program ct1203;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes, SysUtils, CustApp, IdTCPServer, IdContext, IdGlobal, EventLog
{ you can add units after this };
type
{ TTcpApplication }
TTcpApplication = class(TCustomApplication)
private
TcpServer: TIdTCPServer;
Logger: TEventLog;
protected
procedure DoRun; override;
procedure OutputInfoLog(Info: String);
procedure OutputErrorLog(Error: String);
procedure ServeExecute(AContext: TIdContext);
procedure ServeConnecte(AContext: TIdContext);
procedure ServeDisconnect(AContext: TIdContext);
procedure ServeException(AContext: TIdContext; AException: Exception);
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
// procedure WriteHelp; virtual;
end;
// 通信数据结构
TCommBlock = Record
Part: String[1]; // 客户端上传: W-屏幕宽度, H-屏幕高度, E-结束; 服务端下发: X-水平坐标, Y-垂直坐标, E-结束
Desc: String[16]; // 描述
Value: Integer; // 数据值
end;
{ TTcpApplication }
procedure TTcpApplication.OutputInfoLog(Info: String);
var
LogContent: String;
begin
LogContent:=FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', Now) + ' -info- ' + Info;
Writeln(LogContent);
Logger.Info(LogContent);
end;
procedure TTcpApplication.OutputErrorLog(Error: String);
var
LogContent: String;
begin
LogContent:=FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', Now) + ' -error- ' + Error;
Writeln(LogContent);
Logger.Info(LogContent);
end;
procedure TTcpApplication.ServeConnecte(AContext: TIdContext);
var
Info: String;
begin
// 连接
Info := AContext.Connection.Socket.Binding.PeerIP + ':' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - has created connection.';
OutputInfoLog(Info);
end;
procedure TTcpApplication.ServeDisconnect(AContext: TIdContext);
var
Info: String;
begin
// 断开连接
Info := AContext.Connection.Socket.Binding.PeerIP + ':' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - has closed connection.';
OutputInfoLog(Info);
end;
procedure TTcpApplication.ServeException(AContext: TIdContext; AException: Exception);
var
Info: String;
begin
// 异常
Info := AContext.Connection.Socket.Binding.PeerIP + ' ' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - Error: ' + AException.Message;
OutputErrorLog(Info);
end;
procedure TTcpApplication.ServeExecute(AContext: TIdContext);
var
CommBlock: TCommBlock;
bytes: TIdBytes;
W, H, X, Y: Integer;
Host: String;
begin
// 执行
if AContext.Connection.Connected then
begin
try
OutputInfoLog('----- Start -----');
Host:=AContext.Connection.Socket.Binding.PeerIP;
AContext.Connection.IOHandler.ReadBytes(bytes, SizeOf(CommBlock), False);
BytesToRaw(bytes, CommBlock, SizeOf(CommBlock));
OutputInfoLog('Recv - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
if CommBlock.Part = 'W' then W:=CommBlock.Value;
if CommBlock.Part = 'H' then H:=CommBlock.Value;
if CommBlock.Part = 'E' then
begin
Randomize;
X:=Random(W);
Y:=Random(H);
// 发送水平坐标
CommBlock.Part:='X';
CommBlock.Desc:='水平坐标';
CommBlock.Value:=X;
OutputInfoLog('Send - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
AContext.Connection.IOHandler.Write(RawToBytes(CommBlock, SizeOf(CommBlock)));
// 发送垂直坐标
CommBlock.Part:='Y';
CommBlock.Desc:='垂直坐标';
CommBlock.Value:=Y;
OutputInfoLog('Send - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
AContext.Connection.IOHandler.Write(RawToBytes(CommBlock, SizeOf(CommBlock)));
// 发送结束标志
CommBlock.Part:='E';
CommBlock.Desc:='结束';
CommBlock.Value:=0;
OutputInfoLog('Send - ' + Host + ' ' + CommBlock.Desc + ': ' + inttostr(CommBlock.Value));
AContext.Connection.IOHandler.Write(RawToBytes(CommBlock, SizeOf(CommBlock)));
end;
except
ON E: Exception do
OutputErrorLog('ERROR: ' + Host + ' - ' + E.Message);
end;
end;
end;
procedure TTcpApplication.DoRun;
// var
// ErrorMsg: String;
begin
// quick check parameters
(*ErrorMsg:=CheckOptions('h', 'help');
if ErrorMsg<>'' then begin
ShowException(Exception.Create(ErrorMsg));
Terminate;
Exit;
end;*)
// parse parameters
(*if HasOption('h', 'help') then begin
WriteHelp;
Terminate;
Exit;
end;*)
{ add your program here }
Logger:=TEventLog.Create(Self);
Logger.FileName:='tcp_serve.log';
Logger.LogType:=ltFile;
Logger.DefaultEventType:=etDebug;
Logger.AppendContent:=True;
// Logger.TimeStampFormat:='yyyy-mm-dd hh:nn:ss:zzz';
Logger.Active:=True;
TcpServer:=TIdTCPServer.Create(Self);
TcpServer.DefaultPort:=8081;
TcpServer.OnConnect:=@ServeConnecte;
TcpServer.OnDisconnect:=@ServeDisconnect;
TcpServer.OnExecute:=@ServeExecute;
TcpServer.OnException:=@ServeException;
TcpServer.StartListening;
TcpServer.Active:=True;
OutputInfoLog('Started at 8081');
while True do readln();
Logger.Destroy;
TcpServer.StopListening;
TcpServer.Destroy;
// stop program loop
Terminate;
end;
constructor TTcpApplication.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
StopOnException:=True;
end;
destructor TTcpApplication.Destroy;
begin
inherited Destroy;
end;
(*procedure TTcpApplication.WriteHelp;
begin
{ add your help code here }
writeln('Usage: ', ExeName, ' -h');
end;*)
var
Application: TTcpApplication;
begin
Application:=TTcpApplication.Create(nil);
Application.Title:='Tcp Application';
Application.Run;
Application.Free;
end.
程序运行效果:
[2022-06-25 10:30:07.442 Info] 2022-06-25 10:30:07:442 -info- Started at 8081
[2022-06-25 10:30:21.783 Info] 2022-06-25 10:30:21:783 -info- 127.0.0.1:52612 - has created connection.
[2022-06-25 10:30:21.783 Info] 2022-06-25 10:30:21:783 -info- ----- Start -----
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- Recv - 127.0.0.1 宽度: 1920
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- ----- Start -----
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- Recv - 127.0.0.1 高度: 1080
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- ----- Start -----
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- Recv - 127.0.0.1 结束: 0
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- Send - 127.0.0.1 水平坐标: 550
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- Send - 127.0.0.1 垂直坐标: 877
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- Send - 127.0.0.1 结束: 0
[2022-06-25 10:30:31.797 Info] 2022-06-25 10:30:31:797 -info- ----- Start -----
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- Recv - 127.0.0.1 宽度: 1920
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- ----- Start -----
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- Recv - 127.0.0.1 高度: 1080
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- ----- Start -----
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- Recv - 127.0.0.1 结束: 0
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- Send - 127.0.0.1 水平坐标: 777
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- Send - 127.0.0.1 垂直坐标: 950
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- Send - 127.0.0.1 结束: 0
[2022-06-25 10:30:41.794 Info] 2022-06-25 10:30:41:794 -info- ----- Start -----
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- Recv - 127.0.0.1 宽度: 1920
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- ----- Start -----
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- Recv - 127.0.0.1 高度: 1080
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- ----- Start -----
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- Recv - 127.0.0.1 结束: 0
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- Send - 127.0.0.1 水平坐标: 271
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- Send - 127.0.0.1 垂直坐标: 294
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- Send - 127.0.0.1 结束: 0
[2022-06-25 10:30:51.792 Info] 2022-06-25 10:30:51:792 -info- ----- Start -----
[2022-06-25 10:30:53.792 Info] 2022-06-25 10:30:53:792 -error- ERROR: 127.0.0.1 - Connection Closed Gracefully.
[2022-06-25 10:30:53.792 Info] 2022-06-25 10:30:53:792 -info- 127.0.0.1:52612 - has closed connection.
program 程序名;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes, SysUtils, CustApp, IdTCPServer, IdContext, IdGlobal, EventLog
{ you can add units after this };
type
{ TTcpApplication }
TTcpApplication = class(TCustomApplication)
private
TcpServer: TIdTCPServer;
Logger: TEventLog;
protected
procedure DoRun; override;
procedure OutputInfoLog(Info: String);
procedure OutputErrorLog(Error: String);
procedure ServeExecute(AContext: TIdContext);
procedure ServeConnecte(AContext: TIdContext);
procedure ServeDisconnect(AContext: TIdContext);
procedure ServeException(AContext: TIdContext; AException: Exception);
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
end;
{ TTcpApplication }
procedure TTcpApplication.OutputInfoLog(Info: String);
var
LogContent: String;
begin
LogContent:=FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', Now) + ' -info- ' + Info;
Writeln(LogContent);
Logger.Info(LogContent);
end;
procedure TTcpApplication.OutputErrorLog(Error: String);
var
LogContent: String;
begin
LogContent:=FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', Now) + ' -error- ' + Error;
Writeln(LogContent);
Logger.Info(LogContent);
end;
procedure TTcpApplication.ServeConnecte(AContext: TIdContext);
var
Info: String;
begin
// 连接
Info := AContext.Connection.Socket.Binding.PeerIP + ':' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - has created connection.';
OutputInfoLog(Info);
end;
procedure TTcpApplication.ServeDisconnect(AContext: TIdContext);
var
Info: String;
begin
// 断开连接
Info := AContext.Connection.Socket.Binding.PeerIP + ':' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - has closed connection.';
OutputInfoLog(Info);
end;
procedure TTcpApplication.ServeException(AContext: TIdContext; AException: Exception);
var
Info: String;
begin
// 异常
Info := AContext.Connection.Socket.Binding.PeerIP + ' ' + inttostr(AContext.Connection.Socket.Binding.PeerPort) + ' - Error: ' + AException.Message;
OutputErrorLog(Info);
end;
procedure TTcpApplication.ServeExecute(AContext: TIdContext);
begin
// 执行 - 业务逻辑
end;
procedure TTcpApplication.DoRun;
begin
{ add your program here }
Logger:=TEventLog.Create(Self);
Logger.FileName:='tcp_serve.log';
Logger.LogType:=ltFile;
Logger.DefaultEventType:=etDebug;
Logger.AppendContent:=True;
Logger.Active:=True;
TcpServer:=TIdTCPServer.Create(Self);
TcpServer.DefaultPort:=8081;
TcpServer.OnConnect:=@ServeConnecte;
TcpServer.OnDisconnect:=@ServeDisconnect;
TcpServer.OnExecute:=@ServeExecute;
TcpServer.OnException:=@ServeException;
TcpServer.StartListening;
TcpServer.Active:=True;
Writeln('Started at 8081');
Logger.Info('Started at 8081');
while True do readln();
// stop program loop
Terminate;
end;
constructor TTcpApplication.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
StopOnException:=True;
end;
destructor TTcpApplication.Destroy;
begin
inherited Destroy;
end;
var
Application: TTcpApplication;
begin
Application:=TTcpApplication.Create(nil);
Application.Title:='Tcp Application';
Application.Run;
Application.Free;
end.
将上面的代码框架复制后适当修改,实现业务逻辑即可。
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章