如何使用SOCKET建立通讯机制(500分)(100分)

  • 主题发起人 主题发起人 oalongteng
  • 开始时间 开始时间
O

oalongteng

Unregistered / Unconfirmed
GUEST, unregistred user!
模型:设备N个->采集机N个->告警后台1个<-->前台程序N个。原来是采用DSG通讯机制来自动将三部分的数据包进行发送和接收,至于DSG是用什么来实现通讯的,不清楚。现在想使用SOCKET来实现,要求可靠,稳定,迅速,不能丢失数据包。数据包的数量比较大,而且是多对多,会有一台专门的通信服务器。
问题:请提供详细方案,或DEMO程序。可行者一次性给500大洋。请认真考虑发言。
 
这几种机器用事件模型应该够用了。
下面是一个TCP转发服务器的例子,他的作用是有一些客户端连到服务器后,任何一个客户端发给服务器的数据都被服务器原封不动的转发到所有客户端,类似于多人聊天的概念。
program EventSelectServer;

{$APPTYPE CONSOLE}

uses
SysUtils, WinSock2;

const
Port = 5150;
MaxConnection = 63;
BufSize = 1024;

var
wsaData: TWSAData;
Connection: array [0..MaxConnection] of TSocket;
Events: array [0..MaxConnection] of WSAEvent;
ConnectionNum: Integer;
Index: Integer;
i, j: Integer;
NetworkEvent: TWSANetworkEvents;
ServerAddr: TSockAddrIn;
ClientAddr: TSockAddrIn;
ClientAddrLen: Integer;
Buf: string;
Ret: Integer;

begin
Ret:=WSAStartup($202, wsaData);
if Ret<>0 then
begin
WriteLn('WSAStartup failed with error ', Ret);
ReadLn;
Exit
end;

ConnectionNum:=1;

Connection[0]:=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if Connection[0]=INVALID_SOCKET then
begin
WriteLn('socket failed with error ', WSAGetLastError);
ReadLn;
Exit
end;

ServerAddr.sin_family:=AF_INET;
ServerAddr.sin_port:=htons(Port);
ServerAddr.sin_addr.S_addr:=htonl(INADDR_ANY);
if bind(Connection[0], @ServerAddr, SizeOf(ServerAddr))=SOCKET_ERROR then
begin
WriteLn('bind failed with error ', WSAGetLastError);
ReadLn;
Exit
end;

Events[0]:=WSACreateEvent;
if Events[0]=WSA_INVALID_EVENT then
begin
WriteLn('WSACreateEvent failed with error ', WSAGetLastError);
ReadLn;
Exit
end;

if WSAEventSelect(Connection[0], Events[0], FD_ACCEPT)=SOCKET_ERROR then
begin
WriteLn('WSAEventSelect failed with error ', WSAGetLastError);
ReadLn;
Exit
end;

if listen(Connection[0], 5)=SOCKET_ERROR then
begin
WriteLn('listen failed with error ', WSAGetLastError);
ReadLn;
Exit
end;

while True do
begin
Ret:=WSAWaitForMultipleEvents(ConnectionNum, @Events[0], False, WSA_INFINITE, False);
Index:=Ret-WSA_WAIT_EVENT_0;

for i:=Index to ConnectionNum-1 do
begin
Ret:=WSAWaitForMultipleEvents(1, @Events, True, 0, False);
if (Ret=WSA_WAIT_FAILED) or (Ret=WSA_WAIT_TIMEOUT) then
Continue;

if WSAEnumNetworkEvents(Connection, Events, @NetworkEvent)=SOCKET_ERROR then
Continue;

if (NetworkEvent.lNetworkEvents and FD_ACCEPT)<>0 then
begin
if NetworkEvent.iErrorCode[FD_ACCEPT_BIT]<>0 then
begin
WriteLn('FD_ACCEPT failed with error ', NetworkEvent.iErrorCode[FD_ACCEPT_BIT]);
Continue
end;
if ConnectionNum<=MaxConnection then
begin
ClientAddrLen:=SizeOf(ClientAddr);
Connection[ConnectionNum]:=accept(Connection, ClientAddr, ClientAddrLen);
if Connection[ConnectionNum]=INVALID_SOCKET then
begin
WriteLn('accept failed with error ', WSAGetLastError);
Continue
end;

Events[ConnectionNum]:=WSACreateEvent;
if Events[ConnectionNum]=WSA_INVALID_EVENT then
begin
WriteLn('WSACreateEvent failed with error ', WSAGetLastError);
if closesocket(Connection[ConnectionNum])=SOCKET_ERROR then
WriteLn('closesocket failed with error ', WSAGetLastError);
Continue
end;

if WSAEventSelect(Connection[ConnectionNum], Events[ConnectionNum], FD_READ or FD_CLOSE)=SOCKET_ERROR then
begin
WriteLn('WSAEventSelect failed with error ', WSAGetLastError);
if closesocket(Connection[ConnectionNum])=SOCKET_ERROR then
WriteLn('closesocket failed with error ', WSAGetLastError);
if not WSACloseEvent(Events[ConnectionNum]) then
WriteLn('WSACloseEvent failed with error ', WSAGetLastError);
Continue
end;

WriteLn('accetp ', inet_ntoa(ClientAddr.sin_addr), ':', ntohs(ClientAddr.sin_port));
Inc(ConnectionNum)
end
end;
if (NetworkEvent.lNetworkEvents and FD_READ)<>0 then
begin
if NetworkEvent.iErrorCode[FD_READ_BIT]<>0 then
begin
WriteLn('FD_READ failed with error ', NetworkEvent.iErrorCode[FD_READ_BIT]);
Continue
end;

SetLength(Buf, BufSize);
Ret:=recv(Connection, Buf[1], BufSize, 0);
if Ret=SOCKET_ERROR then
begin
WriteLn('recv failed with error ', WSAGetLastError);
Continue
end;
SetLength(Buf, Ret);

ClientAddrLen:=SizeOf(ClientAddr);
if getpeername(Connection, ClientAddr, ClientAddrLen)=SOCKET_ERROR then
begin
WriteLn('getpeername failed with error ', WSAGetLastError);
Continue
end;
Buf:='来自'+inet_ntoa(ClientAddr.sin_addr)+':'+IntToStr(ntohs(ClientAddr.sin_port))+' '+Buf;
Buf:=TimeToStr(Time)+' '+Buf;

for j:=1 to ConnectionNum do
if send(Connection[j], Buf[1], Length(Buf), 0)=SOCKET_ERROR then
begin
WriteLn('send failed with error ', WSAGetLastError);
Continue
end;

WriteLn(Buf)
end;
if (NetworkEvent.lNetworkEvents and FD_CLOSE)<>0 then
begin
if NetworkEvent.iErrorCode[FD_CLOSE_BIT]<>0 then
begin
WriteLn('FD_CLOSE failed with error ', NetworkEvent.iErrorCode[FD_CLOSE_BIT]);
Continue
end;

ClientAddrLen:=SizeOf(ClientAddr);
if getpeername(Connection, ClientAddr, ClientAddrLen)=SOCKET_ERROR then
begin
WriteLn('getpeername failed with error ', WSAGetLastError);
Continue
end;
Buf:='closesocket '+inet_ntoa(ClientAddr.sin_addr)+':'+IntToStr(ntohs(ClientAddr.sin_port));
WriteLn(Buf);

if closesocket(Connection)=SOCKET_ERROR then
WriteLn('closesocket failed with error ', WSAGetLastError);

if not WSACloseEvent(Events) then
WriteLn('WSACloseEvent failed with error ', WSAGetLastError);

if ConnectionNum>1 then
begin
Connection:=Connection[ConnectionNum-1];
Events:=Events[ConnectionNum-1];
Dec(ConnectionNum)
end
end
end
end;

for i:=0 to ConnectionNum do
if closesocket(Connection)=SOCKET_ERROR then
begin
WriteLn('closesocket failed with error ', WSAGetLastError);
ReadLn;
Exit
end;

if WSACleanup=SOCKET_ERROR then
begin
WriteLn('WSCleanup failed with error ', WSAGetLastError);
ReadLn;
Exit
end
end.
 
我看它的原来代码好象使用到Automation 对象 IDispatch CoClass等东西。里面没有详细实现的代码。比如
定义
IDSGLinkEvents = dispinterface
['{9F3121A0-2C6D-4FF0-851F-7F2E26D16E44}']
procedure OnReceiveData(const SourceConnectionName: WideString; DataType: SYSINT; DataBody: Integer; DataLength: SYSINT); dispid 1;
END;
实现:找不到。是不是已经在Automation对象实现了,这里只是调用接口而已,对IDispatch不熟悉。


To LeeChange
需要详细方案,说明实现方法,可靠性,稳定性。
 
系统内一共有多少台机器参与网络传输?(大约)
传输数据量大约多少bytes/s
 
虚拟机器,比如采集机是C++写的一个采集硬件设备数据的程序。告警后台是DELPHI编写的一个处理数据的程序。这些程序是不管通讯方面的细节的,它们只要将数据写成特定字符串用DSG发送和接收就可以了。DSG会自动处理一切和通讯相关的事务(比如容错、丢包、确认收到,等等)。现在要实现的就是这样的功能。是要用SOCKET来做的。
设备是很多台。数据量很大,多少K没算过,一个设备一分钟发几条数据。20个设备估计发多少。数据包定义不清楚多少字节,估计有300个字节。
 
300Bytes*20设备*9次/60秒。
这才多一点数据呀。
20台设备用事件模型正合适。丢包,确认和传输容错可以由tcp处理,只是逻辑上的容错要自己写程序。
就用时间模型写个tcp服务器,处理所有客户端的连接就可以。
 
To LeeChange
有点对路了哦,请详细点,好不,这方面我不是很懂,还有前面的的问题能帮我解释一下吗,关系到DSG的原理。
 
To LeeChange
我在等待你的回答,谢谢啊,能否详细点介绍下SOCKET如何保证稳定,数据包丢失,重发等问题。对于此问题应该不能使用流套接口来实现,因为需要实现广播、多播等功能。
Please tell me more detail!
 
hehe,要广播呀,那只有用UDP了。
这样一来,丢包,失序和重发就真的要自己处理了。
原理说起来简单,就是在应用层模拟TCP的处理。既然在无连接的IP层上能写出个有连接的TCP来,为什么不能在无连接的UDP上写出个xxx来呢?
而且还没必要实现所有TCP的特性,只挑自己需要的就行了。
 
如何在TCP上处理?如何仅仅靠TCP本身一些安全连接还远不能满足系统对稳定、可靠、等指标的要求?也就是说需要自己编写一大堆代码来满足这些条件。如我上面说的DSG,其实是和CORBA竞争的产品,后来失败了,就没多少人问津了。这些就是在标准协议上进行的大量的工作。我现在其实要实现的只是一小部分工作而已拉,只要满足前面我提的这些功能指标要求就OK了。
能不能提供详细点的方案、或者编程设计思路啊!!有没有人做过呢?提供者另加500分!!!
 
To LeeChange:
你贴出来的程序编译通不过啊,没有WSAEvent、TWSANetworkEvents
 
你有WinSock2单元吗?
 
ftp://delphi-jedi.org/api/Winsock2.zip
 
delphi里只有winsock.pas。。看了一下它源程序,没有定义。。。怎么办,VC6。0有winsock2.h的定义。
 
去上面的地址里DOWN下来WINSOCK2再编译!
300Bytes*20设备*9次/60秒这样的并发不是很多,

LeeChange 继续!
 
编译通过,可惜JUST个DEMO,很简单的东西,我现在需要的是如何控制这些数据包的不丢失、稳定、快速。说白了就是在TCP的基础上如何使用SOCKET来达到性能上的要求,并不是数据量多少问题。。。。
 
继续什么呀?一头雾水。[?]
 
根据这些帖子里的信息,偶也只能just a demo了。[:D]
 
呵呵,你总不能用完成端口来做吧。
其实上面的方法就性能讲足够了,至于你说的稳定性,可以自己定义一个简单的协议!
-》包头—》接受内容-》根据包头信息判断-》
 
请帮忙解释SOCKET的概念,用SOCKET如何来完成数据丢包、超时的控制,数据丢包是很关键的技术,SOCKET怎么实现?
 

Similar threads

D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
873
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部