自己写了个服务器端的,好象问题多多!大家帮我改改 var Form1: TForm1; servsock:tsocket; servaddr,fromaddr:tsockaddr;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject); var wsaData:TwsaData; begin if WSAStartup (makeword(2,2), wsaData)<>0 then // WSAStartup返回值为0表示成功 begin messagebox(application.handle,'无法启动winsock动态连接库!','警告',MB_OK or MB_APPLMODAL or MB_ICONWARNING); Application.Terminate; end;end;procedure TForm1.N2Click(Sender: TObject);beginservsock:=socket(af_inet,sock_dgram,ipproto_udp); if servSock=INVALID_SOCKET then begin messagebox(application.handle,'无法创建句柄!','警告',MB_OK or MB_APPLMODAL or MB_ICONWARNING); exit; end; servaddr.sin_family:=af_inet; servaddr.sin_addr.S_addr:=inet_addr(pchar('127.0.0.1')); servaddr.sin_port:=htons(5150); if bind(servsock,servaddr,sizeof(servaddr))=SOCKET_ERROR then begin messagebox(application.handle,'绑定地址错误!','警告',MB_OK or MB_APPLMODAL or MB_ICONWARNING); exit; end; statusbar1.SimplePanel:=true; statusbar1.SimpleText:='网络服务器开始运行';end;procedure TForm1.N3Click(Sender: TObject); begin closesocket(servsock); wsacleanup; statusbar1.SimpleText:='网络服务器开始关闭'; end;procedure TForm1.Button1Click(Sender: TObject); var recvbuf: pchar ; relong,fromsize:integer; re:boolean; begin recvbuf:=''; getmem(recvbuf,1024); //fromsize:=sizeof(fromaddr); re:=true; while re do begin relong:=recvfrom(servsock,recvbuf,1024,0,fromaddr,fromsize); if relong=SOCKET_ERROR then statusbar1.SimpleText:='接收错误: '+inttostr(WSAGetLastError) else if relong=0 then statusbar1.SimpleText:='错误:'+inttostr(WSAGetLastError) else begin memo1.Text:=recvbuf; edit1.Text:=inttostr(relong); end; end; freemem(recvbuf);end;
我大致看了看,不知道你要改什么地方? re:=true; while re do ----〉在这个无限循环里接收数据,处理的不太合理,若是阻塞方式的套接字要另外开一个线程,循环里加上Sleep函数。另外就是要处理好一个 recvfrom 没有收到完整的一个包的情况,毕竟不是控件这些都要自己处理的。
var
Form1: TForm1;
servsock:tsocket;
servaddr,fromaddr:tsockaddr;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
var wsaData:TwsaData;
begin
if WSAStartup (makeword(2,2), wsaData)<>0 then // WSAStartup返回值为0表示成功
begin
messagebox(application.handle,'无法启动winsock动态连接库!','警告',MB_OK or MB_APPLMODAL or MB_ICONWARNING);
Application.Terminate;
end;end;procedure TForm1.N2Click(Sender: TObject);beginservsock:=socket(af_inet,sock_dgram,ipproto_udp);
if servSock=INVALID_SOCKET then
begin
messagebox(application.handle,'无法创建句柄!','警告',MB_OK or MB_APPLMODAL or MB_ICONWARNING);
exit;
end;
servaddr.sin_family:=af_inet;
servaddr.sin_addr.S_addr:=inet_addr(pchar('127.0.0.1'));
servaddr.sin_port:=htons(5150);
if bind(servsock,servaddr,sizeof(servaddr))=SOCKET_ERROR then
begin
messagebox(application.handle,'绑定地址错误!','警告',MB_OK or MB_APPLMODAL or MB_ICONWARNING);
exit;
end;
statusbar1.SimplePanel:=true;
statusbar1.SimpleText:='网络服务器开始运行';end;procedure TForm1.N3Click(Sender: TObject);
begin
closesocket(servsock);
wsacleanup;
statusbar1.SimpleText:='网络服务器开始关闭';
end;procedure TForm1.Button1Click(Sender: TObject);
var recvbuf: pchar ;
relong,fromsize:integer;
re:boolean;
begin
recvbuf:='';
getmem(recvbuf,1024);
//fromsize:=sizeof(fromaddr);
re:=true;
while re do
begin
relong:=recvfrom(servsock,recvbuf,1024,0,fromaddr,fromsize);
if relong=SOCKET_ERROR then
statusbar1.SimpleText:='接收错误: '+inttostr(WSAGetLastError)
else
if relong=0 then
statusbar1.SimpleText:='错误:'+inttostr(WSAGetLastError)
else
begin
memo1.Text:=recvbuf;
edit1.Text:=inttostr(relong);
end;
end;
freemem(recvbuf);end;
while re do ----〉在这个无限循环里接收数据,处理的不太合理,若是阻塞方式的套接字要另外开一个线程,循环里加上Sleep函数。另外就是要处理好一个 recvfrom 没有收到完整的一个包的情况,毕竟不是控件这些都要自己处理的。
我大致看了看,不知道你要改什么地方(编译确实通过了的,但是没的实际效果)
这个程序运行后无法接受到客户端发送的数据!
thisisyjs(只是一种感觉) 大哥,能不能和你在qq上聊聊啊!
如果要先调通的话,可以一方用控件,比如调服务器端程序客户端先用 TNMUDP,看能不能通。
基本的 WinSock 类,包含了 TCP/UDP 共有的部分,是一个抽象基类,
其 Open 成员为纯虚
*)
TMCustomSocket = class(TObject)
private
FSocket : TSocket; // WinSock Handle
FLocal : TSockAddr; // Local Address Info
FRemote : TSockAddr; // Remote Address Info(be filled after some operate)
FLocHost : String; // Local Host Name \_ be filled by
FLocAddr : String; // Local IP Address / GetLocalHostAddr procedure
FTimeOut : Integer; // Socket time out, by miliseconds Procedure GetLocalHostAddr;
Function GetActive : Boolean;
Procedure SetActive( aActive : Boolean );
Function GetLocalHost : String;
Function GetLocalAddr : String;
Function GetLocalPort : Integer;
Procedure SetLocalPort( aPort : Integer );
Function GetRemoteHost : String;
Function GetRemoteAddr : String;
Procedure SetRemoteHostAddr( aHostAddr : String );
Function GetRemotePort : Integer;
Procedure SetRemotePort( aPort : Integer ); protected
Procedure SetTimeOut( aTimeOut : Integer ); Virtual; Procedure Open; Virtual; Abstract;
Procedure Close; Virtual; public
Constructor Create;
Destructor Destroy; Override; function WaitForData : Boolean; Property Socket : TSocket Read FSocket;
Property Active : Boolean Read GetActive Write SetActive;
Property LocalHost : String Read GetLocalHost;
Property LocalAddr : String Read GetLocalAddr;
Property LocalPort : Integer Read GetLocalPort Write SetLocalPort;
Property RemoteHost : String Read GetRemoteHost Write SetRemoteHostAddr;
Property RemoteAddr : String Read GetRemoteAddr Write SetRemoteHostAddr;
Property RemotePort : Integer Read GetRemotePort Write SetRemotePort;
Property TimeOut : Integer Read FTimeOut Write SetTimeOut;
end; (*
UDP 通信类,实现纯虚成员 Open 和用于收发消息的两个成员:
SendMessage/RecvMessage
*)
TMUDPSocket = class( TMCustomSocket )
Public
Constructor Create;
Destructor Destroy; Override; Procedure Open; Override;
Procedure Close; Override;
Procedure SendString( aMessage : String );
Function RecvString : String;
End;// TMCustomSocket 类的实现// 构造函数,初始化各成员
Constructor TMCustomSocket.Create;
Begin
Inherited Create;
FSocket := INVALID_SOCKET;
FLocal.sin_family := AF_INET;
FLocal.sin_addr.S_addr := htonl( INADDR_ANY );
FLocal.sin_port := htons( 0 );
FRemote.sin_family := AF_INET;
FRemote.sin_addr.S_addr := htonl( INADDR_ANY );
FRemote.sin_port := htons( 0 );
FLocHost := '';
FLocAddr := '';
FTimeOut := 1000;
End;// 析构函数,析构前关闭 Socket
Destructor TMCustomSocket.Destroy;
Begin
Close;
Inherited Destroy;
End;// 如果 Socket 打开的话,关闭它
Procedure TMCustomSocket.Close;
Begin
If ( FSocket <> INVALID_SOCKET ) Then
Begin
closesocket( FSocket );
FSocket := INVALID_SOCKET;
End;
End;// 等待接收数据
function TMCustomSocket.WaitForData : Boolean;
var
FDSet : TFDSet;
TimeVal : TTimeVal;
begin
TimeVal.tv_sec := FTimeout div 1000;
TimeVal.tv_usec := (FTimeout mod 1000) * 1000;
FD_ZERO(FDSet);
FD_SET(FSocket, FDSet);
Result := select(0, @FDSet, nil, nil, @TimeVal) > 0;
end;// 取得本地 IP Address & Host Name ,只在此两项为空时执行一次
Procedure TMCustomSocket.GetLocalHostAddr;
Var
buf : Array [0..63] Of Char;
phe : PHostEnt;
ppia : PAPInAddr;
Begin
If ( ( FLocHost = '' ) OR ( FLocAddr = '' ) ) Then
Begin
gethostname( buf, sizeof( buf ) );
FLocHost := buf;
phe := gethostbyname( buf );
If ( phe = Nil ) Then
Exit;
ppia := PAPInAddr( phe^.h_addr_list );
FLocAddr := inet_ntoa( ppia^[0]^ );
End;
End;// 判断 Socket 是否打开
Function TMCustomSocket.GetActive : Boolean;
Begin
Result := ( FSocket <> INVALID_SOCKET );
End;// 打开/关闭 Socket
Procedure TMCustomSocket.SetActive( aActive : Boolean );
Begin
If ( aActive <> Active ) Then
Begin
If ( aActive ) Then
Open
Else
Close;
End;
End;// 取得本机 Host Name
Function TMCustomSocket.GetLocalHost : String;
Begin
If ( FLocHost = '' ) Then
GetLocalHostAddr;
Result := FLocHost;
End;// 取得本机 IP Address
Function TMCustomSocket.GetLocalAddr : String;
Begin
If ( FLocAddr = '' ) Then
GetLocalHostAddr;
Result := FLocAddr;
End;// 取得本机端口
Function TMCustomSocket.GetLocalPort : Integer;
Begin
Result := ntohs( FLocal.sin_port );
End;// 设置本机端口
Procedure TMCustomSocket.SetLocalPort( aPort : Integer );
Begin
If ( Active ) Then
Raise Exception.Create( 'This socket is opened, cannot change localport!' );
FLocal.sin_port := htons( aPort );
End;// 取远程主机名
Function TMCustomSocket.GetRemoteHost : String;
Var
phe : PHostEnt;
Begin
Result := '';
phe := gethostbyaddr( @( FRemote.sin_addr.S_addr ), 4, AF_INET );
If ( phe <> Nil ) Then
Result := phe^.h_name;
End;// 取远程主机地址
Function TMCustomSocket.GetRemoteAddr : String;
Begin
Result := inet_ntoa( FRemote.sin_addr );
End;// 设置远程主机名/地址,根据参数是否为 IP 地址自动设置
Procedure TMCustomSocket.SetRemoteHostAddr( aHostAddr : String );
Var
phe : PHostEnt;
Begin
aHostAddr := Trim( aHostAddr );
If ( IsIPAddr( aHostAddr ) ) Then
FRemote.sin_addr.S_addr := inet_addr( PChar( aHostAddr ) )
Else
Begin
phe := gethostbyname( PChar( aHostAddr ) );
If ( phe <> Nil ) Then
FRemote.sin_addr := PAPInAddr( phe^.h_addr_list )^[0]^;
End;
End;// 取远程端口号
Function TMCustomSocket.GetRemotePort : Integer;
Begin
Result := ntohs( FRemote.sin_port );
End;// 设置远程端口号
Procedure TMCustomSocket.SetRemotePort( aPort : Integer );
Begin
FRemote.sin_port := htons( aPort );
End;// 设置超时时间
Procedure TMCustomSocket.SetTimeOut( aTimeOut : Integer );
Begin
If ( aTimeOut <> FTimeOut ) Then
Begin
FTimeOut := aTimeOut;
If ( FSocket <> INVALID_SOCKET ) Then
// SO_RCVTIMO = $1006
SocketError( setsockopt( FSocket, SOL_SOCKET, $1006, @aTimeOut, SizeOf( aTimeOut ) ) );
End;
End;
Begin
Inherited Create;
End;Destructor TMUDPSocket.Destroy;
Begin
Inherited Destroy;
End;// 打开一个 UDP Socket 并绑定本机地址/端口
Procedure TMUDPSocket.Open;
Begin
If ( FSocket = INVALID_SOCKET ) Then
Begin
FSocket := WinSock.socket( PF_INET, SOCK_DGRAM, 0 );
If ( FSocket = INVALID_SOCKET ) Then
SocketError( SOCKET_ERROR );
SocketError( bind( FSocket, FLocal, SizeOf( FLocal ) ) );
End;
End;// 关闭 UDP Socket
procedure TMUDPSocket.Close;
begin
Inherited Close;
end;// 发送一个 UDP 消息
// aMessage 为要发送的消息串
Procedure TMUDPSocket.SendString( aMessage : String );
Var
nSize, nError : Integer;
buf, p : PByte;
Begin
If ( FSocket <> INVALID_SOCKET ) Then
Begin
nSize := Length( aMessage ) + 1;
GetMem( buf, nSize ); // 分配缓冲区
Try
ZeroMemory( buf, nSize );
StrCopy( PChar( buf ), PChar( aMessage ) );
p := buf;
nError := sendto( FSocket, nSize, SizeOf( nSize ), 0, FRemote, SizeOf( FRemote ) );
If ( nError <> SizeOf( nSize ) ) Then
Raise Exception.Create( crsErrInvSize );
While ( nSize > 0 ) Do
Begin
nError := cnMsgSize;
If ( nSize < cnMsgSize ) Then
nError := nSize;
nError := sendto( FSocket, p^, nError, 0, FRemote, SizeOf( FRemote ) );
If ( nError > 0 ) Then
Begin
Inc( p, nError );
nSize := nSize - nError;
End
Else
Raise Exception.Create( crsErrSendData );
End;
Finally
FreeMem( buf );
End;
End;
End;// 接收 UDP 消息
// 返回收到的消息串
Function TMUDPSocket.RecvString : String;
Var
nSize, nError, nFromLen : Integer;
buf, p : PByte;
Begin
Result := '';
If ( FSocket <> INVALID_SOCKET ) Then
Begin
nFromLen := SizeOf( FRemote );
If ( Not WaitForData ) Then // 等待数据到达
Raise EMSocket.Create( crsErrRecvTimeout + SocketErrorCode );
nError := recvfrom( FSocket, nSize, SizeOf( nSize ), 0, FRemote, nFromLen );
SocketError( nError );
If ( nError <> SizeOf( nSize ) ) Then
Raise Exception.Create( crsErrInvSize );
GetMem( buf, nSize ); // 分配缓冲区
Try
FillMemory( buf, nSize, 0 );
p := buf;
While ( nSize > 0 ) Do
Begin
nError := cnMsgSize;
If ( nSize < cnMsgSize ) Then
nError := nSize;
If ( Not WaitForData ) Then // 等待数据到达
Raise EMSocket.Create( crsErrRecvTimeout + SocketErrorCode );
nError := recvfrom( FSocket, p^, nError, 0, FRemote, nFromLen );
SocketError( nError );
If ( nError > 0 ) Then
Begin
Inc( p, nError );
nSize := nSize - nError;
End
Else
Raise Exception.Create( crsErrRecvData );
End;
Result := PChar( buf );
Finally
FreeMem( buf );
End;
End;
End;
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, winsock,
StdCtrls;const
WM_SOCK = WM_USER + 1; //自定义windows消息
UDPPORT = 646; //设定UDP端口号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;
//利用消息实时获知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
// 初始化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;}
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);
FSockAddrIn.SIn_Port := htons(UDPPORT);
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 := inet_addr(pchar(Edit1.text)); //INADDR_BROADCAST; //INADDR_BROADCAST = -1 ?
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.
因为 TNMUDP的revebuff 函数的接收参数是一个数组类型,这个接收就不完整!怎么会收不完整呢?!
是你方法不对。