winsock问题(200分)

  • 主题发起人 主题发起人 maze
  • 开始时间 开始时间
数据肯定接收完毕,而且数据包也肯定完整。
因为我曾经是判断recv连续3次返回-1就跳过循环,解析数据包,这时候数据包完全正确
但是这个现象让我百思不得其解
 
应该是你的网络有广播消息。
 
我是tcp的连接,广播消息对这个有影响吗?这个说法有没官方的资料说明?
 
因为你每个问题的分都很多啊
 
丁页 一一 口合!
 
问题: 如何用delphi实现局域网内的UDP组播的发送与接收? ( 积分: 100 )
分类: 局域网 / 通讯

来自: yyy_fcz, 时间: 2005-05-23 22:43:00, ID: 3082274
如何用delphi实现局域网内的UDP组播的发送与接收?

来自: skywhl, 时间: 2005-05-30 18:31:09, ID: 3089215
我也想知道啊~~~

来自: yyy_fcz, 时间: 2005-05-30 19:10:01, ID: 3089242
已经解决了!
unit udp;

interface

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

const
WM_SOCK = WM_USER + 1; //自定义windows消息
UDPPORT = 6543; //设定UDP端口号

//D类地址224.0.0.0 - 239.255.255.255
//若为224.0.0.1则本机也能收到,否则本机收不到,其它机器能收到。
MY_GROUP = '224.0.0.2';


(*
* Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
* Delphi5自带的winsock.pas中没有ip_mreq的定义。
*)

type
ip_mreq = record
imr_multiaddr: in_addr; (* IP multicast address of group *)
imr_interface: in_addr; (* local IP address of interface *)
end;
TIpMReq = ip_mreq;
PIpMReq = ^ip_mreq;

type
Tfrmmain = class(TForm)
Button1: TButton;
Edit1: TEdit;
Memo1: TMemo;
Edit2: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
s: TSocket;
addr: TSockAddr;
FSockAddrIn : TSockAddrIn;
mreq:ip_mreq;
//利用消息实时获知UDP消息
procedure ReadData(var Message: TMessage); message WM_SOCK;
public
{ Public declarations }
procedure SendData(Content: String);
end;

var
frmmain: Tfrmmain;

implementation

{$R *.DFM}

procedure Tfrmmain.FormCreate(Sender: TObject);
var
TempWSAData: TWSAData;
//optval: integer;
begin
Edit1.Text := MY_GROUP;
// 初始化SOCKET
if WSAStartup($101, TempWSAData)=1 then
showmessage('StartUp Error!');

s := Socket(AF_INET, SOCK_DGRAM, 0);
if (s = INVALID_SOCKET) then //Socket创建失败
begin
showmessage(inttostr(WSAGetLastError())+' Socket创建失败');
CloseSocket(s);
//exit;
end;
//发送方SockAddr绑定
addr.sin_family := AF_INET;
addr.sin_addr.S_addr := INADDR_ANY;
addr.sin_port := htons(UDPPORT);
if Bind(s, addr, sizeof(addr)) <> 0 then
begin
showmessage('bind fail');
end;

{optval:= 1;
if setsockopt(s,SOL_SOCKET,SO_BROADCAST,pchar(@optval),sizeof(optval)) = SOCKET_ERROR then
begin
showmessage('无法进行UDP广播');
end;}

mreq.imr_multiaddr.S_addr := inet_addr(pchar(MY_GROUP));//htonl(INADDR_ALLHOSTS_GROUP);
mreq.imr_interface.S_addr := htonl(INADDR_ANY);
if setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,pchar(@mreq),sizeof(mreq)) = SOCKET_ERROR then
begin
showmessage('无法进行UDP组播');
end;


WSAAsyncSelect(s, frmmain.Handle , WM_SOCK, FD_READ);
//接收端SockAddrIn设定
FSockAddrIn.SIn_Family := AF_INET;
FSockAddrIn.SIn_Port := htons(UDPPORT);

label3.Caption := '端口:'+inttostr(UDPPORT);
end;

procedure Tfrmmain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CloseSocket(s);
end;

procedure Tfrmmain.ReadData(var Message: TMessage);
var
buffer: Array [1..4096] of char;
len: integer;
flen: integer;
Event: word;
value: string;
begin
flen:=sizeof(FSockAddrIn);
Event := WSAGetSelectEvent(Message.LParam);
if Event = FD_READ then
begin
len := recvfrom(s, buffer, sizeof(buffer), 0, FSockAddrIn, flen);
value := copy(buffer, 1, len);
Memo1.Lines.add(value)
end;
end;

procedure Tfrmmain.SendData(Content: String);
var
value{,hostname}: string;
len: integer;
begin
//FSockAddrIn.SIn_Addr.S_addr := INADDR_BROADCAST;
FSockAddrIn.SIn_Addr.S_addr := inet_addr(pchar(MY_GROUP));
value := Content;
len := sendto(s, value[1], Length(value), 0, FSockAddrIn, sizeof(FSockAddrIn));
if (WSAGetLastError() <> WSAEWOULDBLOCK) and (WSAGetLastError() <> 0) then
showmessage(inttostr(WSAGetLastError()));
if len = SOCKET_ERROR then
showmessage('send fail');
if len <> Length(value) then
showmessage('Not Send all');
end;

procedure Tfrmmain.Button1Click(Sender: TObject);
begin
senddata(Edit2.text);
end;

end.

问题讨论没有结束 ...
 
不知楼上引用的大段文字想说明什么?不知道有没有仔细看过之前的讨论。
 
用WSAAsyncSelect模型不行,那其他的模型会有这样的问题吗?
 
WSAAsyncEventSelect模型也有
 
WSAAsyncEventSelect也有,不会吧,我的程序都没有,你有没有在别的机上测试
 
因为困扰很久了才有此一问
现在的解决方法有些简单,计数3次这样的情况,就跳过
jfyes,能把你接收的代码贴出来一点看看么?
 
WSAAsyncSelect 我很少用。
下面是
WSAAsyncEventSelect
这个是我的一个客户端Socket,用了很久
{ TClientWorkThread }

constructor TClientWorkThread.Create(AClientSocket: TClientSocket);
var
ErrorCode: Integer;
begin
hEvent := WSACreateEvent;
if hEvent = WSA_INVALID_EVENT then
raise ESocketError.Create(SysErrorMessage(GetLastError));
ErrorCode := WSAEventSelect(AClientSocket.FSocket, hEvent, FD_READ or FD_CLOSE); // 'WSAEventSelect';
if ErrorCode <> 0 then begin
AClientSocket.Error(eeConnect, ErrorCode);
if ErrorCode <> 0 then
AClientSocket.DoException(AClientSocket, Format(sWindowsSocketError, [SysErrorMessage(ErrorCode), ErrorCode, 'WSAEventSelect']));
end;
inherited Create(AClientSocket);
end;

procedure TClientWorkThread.Execute;
var
ErrorCode: Integer;
WNet: TWSANETWORKEVENTS;
begin
while not Terminated do
begin
ErrorCode := WSAWaitForMultipleEvents(
1, //事件数量
@hEvent, //事件处理函数
False, //有一个事件触发时就返回
FClientSocket.FTimeOut, // 超时时间 无穷等待
False //函数返回时,不执行I/O例程
);

if Terminated or (ErrorCode = WAIT_IO_COMPLETION) then
begin
Break;
end
else
begin
WSAResetEvent(hEvent);
if ErrorCode = WSA_WAIT_TIMEOUT then
begin
FClientSocket.DoReadTimeOut;
end
else if (ErrorCode = WAIT_OBJECT_0) then
begin
FillChar(WNet, SizeOF(WNet), 0);
ErrorCode := WinSock2.WSAEnumNetworkEvents(FClientSocket.SocketHandle, HEvent, @WNet);
case WNet.lNetworkEvents of
FD_READ: FClientSocket.DoClientRead(Self);
FD_CLOSE: begin
FClientSocket.Close;
end;
else FClientSocket.Error(eeGeneral, ErrorCode);
end;
end;
end;
end;
//线程完成送闭事件
if HEvent <> WSA_INVALID_EVENT then begin
WSACloseEvent(HEvent);
HEvent := WSA_INVALID_EVENT;
end;
end;
 
FD_READ: FClientSocket.DoClientRead(Self);

jfyes兄弟,问题就在这里边,可是你没有代码。
 
{

function ReceiveBuf(const FSocket: TSocket; var Buf;
const Count: Integer = -1): Integer;
begin
Result := -1;
if Count = -1 then
ioctlsocket(FSocket, FIONREAD, Longint(Result))
else
Result := recv(FSocket, Buf, Count, 0);
end;
}

procedure TClientSocket.DoClientRead(Sender: TObject);
var
Buffer: PChar;
Buf: array[0..MAX_BUFSIZE - 1] of Char;
ALength: Integer;
ReadLen: Integer;
begin
ReadLen := 0;
ALength := 0;
try
ReadLen := ReceiveBuf(SocketHandle, Buf);
//这里如果要统计总流所有Socket的,就得要加上所有
Inc(FRecvAllBytes, ReadLen);
if Assigned(OnSocketDataTrans) then OnSocketDataTrans(self, tsRecv, ReadLen);
except on E: Exception do
DoException(Self, 'DoClientRead: OnSocketDataTrans');
end;

while ReadLen > 0 do
begin
try
FillChar(Buf, SizeOf(Buf), #0);
ALength := ReceiveBuf(SocketHandle, Buf, SizeOf(Buf));
//DoException(Self, Format('接收一次总长度:%d, Len: %d', [ReadLen, ALength]));
if ALength < 0 then begin
DoException(Self, 'DoClientRead: A-ReceiveBuf ALength < 0');
Exit;
end;
except on E: Exception do begin
if ALength < 0 then begin
DoException(Self, 'DoClientRead: B-ReceiveBuf ALength < 0');
Exit;
end;
DoException(Self, 'DoClientRead: ReceiveBuf error');
end;
end;

Dec(ReadLen, ALength);
try
OnClientRead(Self, @Buf, ALength);
except on E: Exception do begin
DoException(Self, 'TClientSocket.DoClientRead faild!');
Exit;
end;
end;
end; //while do
end;
 
jfyes:
看明白了,不过你的处理方法还是和我的问题不一样。你在每次接收前都用ioctlsocket(FSocket, FIONREAD, Longint(Result)) 察看了一下接收缓冲区中的数据长度,然后根据这个长度接收。
但是我的问题是我不需要察看缓冲区中的数据长度,我的协议已经告诉我要接受的数据长度。
另外多说一句,《windows网络编程》中不推荐使用ioctlsocket查询缓冲区长度,意思好像是效率不高
 
把你的代码贴出来看看
 
var
buf:array[0..bufsize-1] of char;
leftlen,count:integer;
begin
//********** 接收数据长度**************
leftlen:=4;
while (leftlen>0) do
begin
count:=recv(sock,buf,leftlen,0);
if count=0 then
exit; //出错
if count=SOCKET_ERROR then
begin
if wsagetlasterror=WSAEWOULDBLOCK then
begin
sleep(10);
continue;
end else
exit; //出错
end;
dec(leftlen,count);
end;
move(buf,leftlen,4);
//**************************************
//********** 接收数据******************
while (leftlen>0) do
begin
count:=recv(sock,buf,leftlen,0);
if count=0 then
exit; //出错
if count=SOCKET_ERROR then
begin
if wsagetlasterror=WSAEWOULDBLOCK then
begin
sleep(10);
continue;
end else
exit; //出错
end;
dec(leftlen,count);
end;
//******** 接收完毕,处理。。。
end;
 
把你的程序发给我看看。jf_yes@126.com
 
不好意思,单位的东西,不便传播
 

Similar threads

回复
0
查看
866
不得闲
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
后退
顶部