UDP编程思想求教(100分)

  • 主题发起人 主题发起人 cc007sy1
  • 开始时间 开始时间
C

cc007sy1

Unregistered / Unconfirmed
GUEST, unregistred user!
小弟现在做一个server端,用UDP方式接收client端发送过来的数据然后存到数据库。client端每隔2秒发一次数据,现在我是把添加记录到数据库的命令写在IdUDPServer1UDPRead事件里,也就是说它每隔2秒就会写一次数据进数据库。
提问:
如果我想每隔5分钟存一次数据进数据库呢?
该如何操作?
请达人指点
 
把你每次写数据间隔中收到的数据放到缓冲区内。
到存贮间隔时保存缓冲区中的内容。清空缓冲区。
在种方法,在系统不正常中断时,可能会丢数据。
 
同上
写进临时表或文件里。
 
unit untUDPSock;

interface

uses Windows, SysUtils, Winsock;

const
MAX_BUFCOUNT = 65535; // 64k

type
TBuf = array[0 .. MAX_BUFCOUNT -1] of char; // 64k缓冲区
TReceiveEvent = procedure(RemoteIP: string; Buf: TBuf; RecvLen: integer) of Object;

TUDPSock = class
FThreadHandle: THandle;
private
FLocalPort: DWORD;
FTargetPort: DWORD;
FTargetIP: string;
FOnReceived: TReceiveEvent;
FActive: boolean;
FSock: TSocket;

procedure InitSocket;
procedure Start;
procedure Stop;
procedure SetLocalPort(const Value: DWORD);
procedure SetTargetIP(const Value: string);
procedure SetTargetPort(const Value: DWORD);
procedure SetOnReceived(const Value: TReceiveEvent);
procedure SetActive(const Value: boolean);
procedure SetSock(const Value: TSocket);
protected
procedure DoReceiveBuf;
public
constructor Create();
destructor Destroy(); override;
procedure SendBuf(Buf: TBuf; BufSize: integer);
procedure SendText(Text: string);
property OnReceived: TReceiveEvent read FOnReceived write SetOnReceived;

property Sock: TSocket read FSock write SetSock;
property Active: boolean read FActive write SetActive;
property TargetIP: string read FTargetIP write SetTargetIP;
property TargetPort: DWORD read FTargetPort write SetTargetPort;
property LocalPort: DWORD read FLocalPort write SetLocalPort;
end;

implementation

//-----------------------------------------
// 名称:WSAStartUp
// 参数:无
// 功能:启动Socket
// 返回值:执行成功返回True,失败返回False
//-----------------------------------------
function StartUp: boolean;
var
WSAData: TWSAData;
begin
if WSAStartUp($0202, WSAData) <> 0 then
result := False
else
result := True;
end;

//----------------------------------------
// 名称:WSACleanUp
// 参数:无
// 功能:关闭Socket
// 返回值:执行成功返回True,失败返回False
//----------------------------------------
function CleanUp: boolean;
begin
if WSACleanUp <> 0 then
result := False
else
result := True;
end;

//---------------------------------------------------------
// 名称:InitSockAddrIn
// 参数:
// IP 如果服务器端使用则置为空(''),
// 如果客户端使用则为服务器的IP地址('127.0.0.1');
// Port 端口号。
// 功能:根据所给参数生成一个TSockAddrIn类型的结构并返回
// 以共其他函数使用。
// 返回值:TSockAddrIn
//---------------------------------------------------------
function InitSockAddrIn(IP: string; Port: word): TSockAddrIn;
begin
result.sin_family := PF_INET;
if IP <> '' then
result.sin_addr.S_addr := inet_addr(pchar(IP))
else
result.sin_addr.S_addr := INADDR_ANY;
result.sin_port := htons(Port);
end;

{ TUDPSock }

// 工作线程
procedure WorkThread(ThreadParam: TUDPSock);
var
Sock: TSocket;
fdread: TFDSET;
ret: integer;
begin
Sock := ThreadParam.Sock;
while True do
begin
FD_ZERO(fdread);
FD_SET(Sock, fdread);
ret := select(0, @fdread, nil, nil, nil);
if ret = SOCKET_ERROR then
break;
if ret > 0 then
try
ThreadParam.DoReceiveBuf;
except
ThreadParam.Active := False;
closesocket(Sock);
break;
end;
end;

raise Exception.Create('接收数据时出错!'); // 当出现这个异常时,就必须重新启动TUDPSock了
end;

constructor TUDPSock.Create;
begin
StartUp;
InitSocket;
end;

procedure TUDPSock.InitSocket;
begin
Sock := socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if Sock = INVALID_SOCKET then
raise Exception.Create('socket function error!');
end;

procedure TUDPSock.DoReceiveBuf;
var
Buf: TBuf;
RemoteAddr: TSockAddrIn;
RemoteAddrSize: integer;
RecvLen: integer;
RemoteIP: string;
begin
RecvLen := recvfrom(Sock, Buf, MAX_BUFCOUNT, 0, RemoteAddr, RemoteAddrSize);
case RecvLen of
0: raise Exception.Create('对方已经关闭!');
SOCKET_ERROR: raise Exception.Create('发生未知错误:' + IntToStr(WSAGetLastError));
end;

try
RemoteIP := inet_ntoa(RemoteAddr.sin_addr);
except
RemoteIP := '未提供IP地址解析!';
end;
if Assigned(FOnReceived) then
FOnReceived(RemoteIP, Buf, RecvLen);
end;

procedure TUDPSock.SendBuf(Buf: TBuf; BufSize: integer);
var
TargetAddr: TSockAddrIn;
begin
TargetAddr := InitSockAddrIn(TargetIP, TargetPort);
try
Sendto(Sock, Buf, BufSize, 0, TargetAddr, sizeof(TargetAddr));
except
raise Exception.Create('发送失败!');
end;
end;

procedure TUDPSock.SendText(Text: string);
var
Buf: TBuf;
len: integer;
begin
len := length(Text);
if len < MAX_BUFCOUNT then
begin
move(pointer(Text)^, buf, len);
SendBuf(Buf, len);
end else
raise Exception.Create(format('一次发送不能超过%n字节', [MAX_BUFCOUNT]));
end;

procedure TUDPSock.SetActive(const Value: boolean);
begin
FActive := Value;
if FActive then
Start
else
Stop;
end;

procedure TUDPSock.SetLocalPort(const Value: DWORD);
begin
if FActive then
raise Exception.Create('不可更改一个正在接受数据的Socket的端口!')
else
FLocalPort := Value;
end;

procedure TUDPSock.SetOnReceived(const Value: TReceiveEvent);
begin
FOnReceived := Value;
end;

procedure TUDPSock.SetSock(const Value: TSocket);
begin
FSock := Value;
end;

procedure TUDPSock.SetTargetIP(const Value: string);
begin
FTargetIP := Value;
end;

procedure TUDPSock.SetTargetPort(const Value: DWORD);
begin
FTargetPort := Value;
end;

procedure TUDPSock.Start;
var
thrdID: DWORD;
LocalAddr: TSockAddrIn;
begin
LocalAddr := InitSockAddrIn('', LocalPort);
if bind(Sock, LocalAddr, sizeof(LocalAddr)) = SOCKET_ERROR then
begin
// 重新创建套接字
closesocket(Sock);
InitSocket;
// 还出错就是有问题了
if bind(Sock, LocalAddr, sizeof(LocalAddr)) = SOCKET_ERROR then
raise Exception.Create('Socket bind ERROR!');
end;

FThreadHandle := BeginThread(nil, 0, @WorkThread, pointer(self), 0, thrdID); // BeginThread函数不错哦,从TThread类抄袭
end;

procedure TUDPSock.Stop;
begin
TerminateThread(FThreadHandle, 0);
end;

destructor TUDPSock.Destroy;
begin
if Active then
Active := False;
closesocket(Sock);
CleanUp;
end;

end.










Demo 程序

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, untUDPSock, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Memo1: TMemo;
Button2: TButton;
Button3: TButton;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
Listen: TUDPSock;
aSender: TUDPSock;
public
{ Public declarations }
procedure ReadIt(RemoteIP: string; Buf: TBuf; RecvLen: integer);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
Listen := TUDPSock.Create;
Listen.LocalPort := 5000; // 接收数据的端口
Listen.OnReceived := ReadIt;
Listen.Active := True;

aSender := TUDPSock.Create; // 发送者无需设定Active为True
// 在一台计算机上同一个端口只能有一个TUDPSock可以接受数据
aSender.TargetIP := '127.0.0.1'; // 目的IP地址
aSender.TargetPort := 5000; // 目的端口
end;

procedure TForm1.ReadIt(RemoteIP: string; Buf: TBuf; RecvLen: integer);
//var
// fs: TFileStream;
begin
// fs := TFileStream.Create('d:/aa.jpg', fmCreate);
// fs.Write(buf, recvlen);
// fs.Free;
Memo1.Lines.Add(format('[IP]%s->[DATA]%s', [RemoteIP, string(Buf)]));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
aSender.SendText(Edit1.Text);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Listen.Active := False;
Listen.Active := True;
end;

procedure TForm1.Button3Click(Sender: TObject);
//var
/// fs: TFileStream;
// buf: TBuf;
// s: Cardinal;
begin
// fs := TFileStream.Create('G:/desktop/overload_jpg.jpg', 0); // 文件大小不可以大于64k
// s := fs.Size;
// fs.ReadBuffer(buf, s);
// aSender.SendBuf(buf, s);
// fs.Free;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Listen.Free;
aSender.Free;
end;

end.

 
多人接受答案了。
 
后退
顶部