为什么在服务端不能接收到客户端的数据,难道没有发送成功,又如何测试客户端发送出去数据包了呢。
或者说我的DEMO本身就有严重问题,谁来帮我看看,分不够了可以在加!

解决方案 »

  1.   

    服务端端代码:
    var
      ErrorCode,AddrSize  : integer;
      FSock : TSocket;                  
      SockAddr_In,Add: TSockAddrIn;
      nrev:Integer;
      revMsg:array[0..1024] of Char;
    begin
      winsock.WSAStartup($0101,WSADATA);
      FSock := socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if FSock = SOCKET_ERROR then
      begin
        showmessage('创建套接字失败' );
        Exit;
      end;
      SockAddr_In.sin_family := AF_INET;
      SockAddr_In.sin_port := htonl(9315);
      SockAddr_In.sin_addr.S_addr := htonl(INADDR_ANY);
      ErrorCode := bind(FSock,SockAddr_In,sizeof(SockAddr_In));
      if ErrorCode = SOCKET_ERROR then
      begin
        showmessage(Format('%s;ErrorCode:%d',['绑定失败:',WSAGetLastError]) );
        Exit;
      end;
      while true do
      begin     nrev:=recv(FSock,FBuf,1024,1); //为什么nrev的返回值一直为-1
         if nrev>0 then
         begin
           test:=inet_ntoa(Add.sin_addr);
           ShowMessage('接受到来自:'+inet_ntoa(Add.sin_addr));
         end;
      end;
      closesocket(FSock);
    end;
    客户端代码
    procedure TForm1.Button1Click(Sender: TObject);
    var
      ErrorCode:Integer;
      msg:array[0..100] of Char;
      SockAdd_Inc:TSockAddrIn;
      Skc:TSocket;
    begin
      ErrorCode:=WSAStartup($0101,WSAData);
      if ErrorCode<>0 then
      begin
        ShowMessage('加载WinSock DLL失败!');
      end;
      Skc:=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
      if Skc=SOCKET_ERROR then
      begin
        ShowMessage('创建套接字失败!');
        Exit;
      end;
      SockAdd_Inc.sin_family:=AF_INET;
      SockAdd_Inc.sin_port:=9315;
      SockAdd_Inc.sin_addr.S_addr:=inet_addr(PChar('192.168.1.90'));
      msg:='Test';
      sendto(Skc,msg,SizeOf(msg),1,SockAdd_Inc,SizeOf(SockAdd_Inc));
      closesocket(Skc);
    end;
      

  2.   

    给你我写的个例子吧,不过用的不是阻塞模式unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls,WinSock, Buttons;
    const
      WM_SERVERSOCK = WM_USER + 100;
      WM_CORRESPONDSOCK = WM_USER + 101;
      WM_CLIENTSOCK = WM_USER  + 102;
    type
      TForm1 = class(TForm)
        chkLock: TCheckBox;
        btn1: TButton;
        btn2: TButton;
        mmo1: TMemo;
        edtSever: TEdit;
        btnSever: TSpeedButton;
        edtClient: TEdit;
        btnClient: TSpeedButton;
        btnServerClose: TButton;
        btnClientClose: TButton;
        btn3: TButton;
        procedure btn1Click(Sender: TObject);
        procedure btn2Click(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure btnSeverClick(Sender: TObject);
        procedure btnClientClick(Sender: TObject);
        procedure btnServerCloseClick(Sender: TObject);
        procedure btnClientCloseClick(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        procedure WMSERVERSOCK(var message : TMessage); message WM_SERVERSOCK;
        procedure WMCORRESPONDSOCK(var message : TMessage); message WM_CORRESPONDSOCK;
        procedure WMCLIENTSOCK(var message : TMessage); message WM_CLIENTSOCK;
      end;
      TSockReadThread= class(TThread)
        private
          FSocket : TSocket;
          FBuf : array[0..255] of Char;
          FMemo : TMemo;
          procedure GetResult;
        protected
          procedure Execute;override;
        public
          constructor Create(pSocket : TSocket;mm : TMemo);
      end;
    var
      Form1: TForm1;
      WSAData : TWSAData;
      FSock,AcceptSock, SkC :  TSocket;
      pData : array[0..255] of Char;
      procedure StartUp;
    implementation{$R *.dfm}
    procedure StartUp;
    var
    ErrorCode : integer;
    begin
      //加载winSock dll
      ErrorCode := WSAStartup($0101, WSAData);
      if ErrorCode <> 0 then
      begin
          ShowMessage('加载失败');
          exit;
      end;
    end;
    { TSockReadThread }constructor TSockReadThread.Create(pSocket: TSocket; mm: TMemo);
    begin
     FMemo := mm;
     FSocket := pSocket;
     inherited Create(false);
    end;procedure TSockReadThread.Execute;
    var
      ret : integer;
      FdSet : TFDSet;
      TimeVal : TTimeVal;
    begin
      inherited;
      FreeOnTerminate := True;
      while not terminated do
      begin
        FD_ZERO(FdSet);
        FD_SET(FSocket,FdSet);
        TimeVal.tv_sec := 0;
        TimeVal.tv_usec := 500;
        if (select(0,@fdSet,nil,nil,@TimeVal) > 0) and not terminated then
        begin
        {
          利用select函数,判断套接字上是否存在数据,或者能否向一个套接字写入数据
          防止应用程序在套接字处于锁定模式中时,在一次I / O绑定调用(如send或recv)过程中,
          被迫进入“锁定”状态;同时防止在套接字处于非锁定模式中时,产生WSAEWOULDBLOCK错误。
          除非满足事先用参数规定的条件,否则select函数会在进行I/O操作时锁定
        }
          ret := recv(FSocket,fbuf,256,0);      if ret > 0 then Synchronize(GetResult)
          else Break;
        end;
      end;
    end;procedure TSockReadThread.GetResult;
    begin
      FMemo.Lines.Add(FBuf);
    end;procedure TForm1.btn1Click(Sender: TObject);
    var
      ErrorCode,AddSize : integer;
      SockAdd_In,Add: TSockAddrIn;
      tm : Longint;
      FdSet : TFDSet;
      TimeVal : TTimeVal;
      buf : array[0..9] of Char;
    begin
      //创建一个使用TCP协议的套接字 SOCK_STREAM  IPPROTO_TCP
      FSock := socket(PF_INET,SOCK_STREAM , IPPROTO_TCP);
      if FSock = SOCKET_ERROR then
      begin
        showmessage(Format('%s;ErrorCode:%d',['套接字创建失败',WSAGetLastError]) );
        Exit;
      end;
      //根据一个TCheckBox控件的选择情况来决定使用锁定模式还是非锁定模式
      if chkLock.Checked then
         tm := 1 //非锁定模式
      else tm := 0; //锁定模式
      {
        在锁定模式下,在I/O操作完成前,执行操作的Winsock函数(比如send和recv)会一直等候下去,不会立即返回程序
        (将控制权交还给程序)。而在非锁定模式下, Winsock函数无论如何都会立即返回。
        在阻塞模式下,当没有客户端请求发送时,调用accept函数的线程(这里是主线程)将一直阻塞下去,不会返回
        在非锁定模式下 Winsock API调用会立即返回。大多数情况下,这些调用都会“失败”,并返回一个WSAEWOULDBLOCK错误。
        它意味着请求的操作在调用期间没有时间完成。需要重复调用同一个函数,直至获得一个成功返回代码
      }
      ioctlsocket(FSock,FIONBIO,tm);
      {
        FIONBIO  : 该命令可在套接字s上允许或禁止“非锁定”(Nonblocking)模式 ,argp 为非0 表示非锁定 为0 表示锁定
        FIONREAD : 用于决定可从套接字上自动读入的数据量
      }
      SockAdd_In.sin_family := PF_INET;
      SockAdd_In.sin_port := htons(5151);
      SockAdd_In.sin_addr.S_addr := INADDR_ANY; //允许服务器应用监听主机计算机上面每个网络接口上的客户机活动
      //绑定
      ErrorCode := bind(FSock,SockAdd_In,sizeof(SockAdd_In));  if ErrorCode = SOCKET_ERROR then
      begin
        showmessage(Format('%s;ErrorCode:%d',['绑定失败:',WSAGetLastError]) );
        Exit;
      end;
      //置为监听模式 backlog参数指定了正在等待连接的最大队列长度
      listen(FSock,5);
      //调用WSAAsyncSelect或WSAEventSelect函数的时候,会将套接字自动设为非锁定模式
      if SOCKET_ERROR=WSAAsyncSelect(FSock,Handle,WM_SERVERSOCK,FD_READ or FD_ACCEPT) then
        ShowMessage('WM_SOCKET Error');
    end;procedure TForm1.btn2Click(Sender: TObject);
    var
      buf : array[0..10] of Char;
      SockAdd_Inc : TSockAddrIn;
    begin                    // SOCK_STREAM  IPPROTO_TCP
      skc := socket(PF_INET,SOCK_STREAM , IPPROTO_TCP);
      if skc = SOCKET_ERROR then
      begin
        showmessage('创建失败');
        Exit;
      end;
      SockAdd_Inc.sin_family := PF_INET;
      SockAdd_Inc.sin_port := htons(5151);
      SockAdd_Inc.sin_addr.S_addr := inet_addr(pchar('127.0.0.1'));  //服务端地址
      connect(skc,SockAdd_Inc,sizeof(SockAdd_Inc));
      if SOCKET_ERROR=WSAAsyncSelect(SkC,Handle,WM_CLIENTSOCK,FD_READ or FD_CLOSE) then
        ShowMessage('WM_SOCKET Error');
    end;
    //与客户端连接后的SOCK所产生的网络事件
    procedure TForm1.WMCORRESPONDSOCK(var message: TMessage);
    var
      ret : integer;
      fbuf : array[0..255] of Char;
    begin
      case  WSAGetSelectEvent(message.LParam) of
        FD_CLOSE :
        begin
          mmo1.Lines.Add('客户端关闭');
        end;  
        FD_READ :
        begin
            ret := recv(AcceptSock,fbuf,256,0);
            mmo1.Lines.Add('客户端来的消息 : ' + fbuf);
        end;
      end;
    end;
    //监视SOCK 所产生的网络消息事件
    procedure TForm1.WMSERVERSOCK(var message: TMessage);
      var  FdSet : TFDSet;
           TimeVal : TTimeVal;
           AddSize : integer;
           Add: TSockAddrIn;
    begin
      case  WSAGetSelectEvent(message.LParam) of
        FD_ACCEPT :
        begin
          //FD_ZERO(set) -- 将 set 的值清乾净
          //FD_SET(s, *set) -- 将 s 加到 set 中
          //FD_CLR(s, *set) -- 将 s 从 set 中删除
          //FD_ISSET(s, *set) -- 检查 s 是否存在於 set 中
          FD_ZERO(FdSet);
          FD_SET(FSock,FdSet);
          TimeVal.tv_sec := 0;
          TimeVal.tv_usec := 500;      //检查一整组(set)的 sockets 是否可以读、写资料,也可以用来检查 socket 是否已和对方连接成功,
          //或者是对方是否已将相对的socket 关闭等
    //      if (select(0,@fdSet,nil,nil,@TimeVal) > 0) then   //查询读写连接中断状态,此处无需要使用该判断
                                                            //果 timeout 设为「NULL」,那麽 select() 就会一直等到「至少」
                                                            //某一个 socket 的事件成立了才会 return
    //      begin                                             //如果 timeout 的值设为 {0, 0} (秒, 微秒)管有没有 socket 的事件成立,都会马上 return,而不会停留
            AddSize := sizeof(Add);
            AcceptSock := accept(FSock,@Add,@AddSize); //返回值为一新的 Socket,此新建之 Socket 不可再用来接受其它的连接要求;
                                                       //但是原先监听之 Socket 仍可接受其他人的连接要求
    //        ShowMessage(inet_ntoa(Add.sin_addr));
            if AcceptSock <> INVALID_SOCKET then
              WSAAsyncSelect(AcceptSock,Handle,WM_CORRESPONDSOCK,FD_READ or FD_WRITE  or FD_CLOSE);
    //      end;
        end;
      end;
    end;procedure TForm1.FormDestroy(Sender: TObject);
    begin
      shutdown(skc,SD_SEND);
      closesocket(FSock);
      closesocket(skc);
      closesocket(AcceptSock);
    end;procedure TForm1.WMCLIENTSOCK(var message: TMessage);
    var
      ret : integer;
      fbuf : array[0..255] of Char;
    begin
      case WSAGetSelectEvent(message.LParam) of
        FD_CLOSE :
        begin
          shutdown(SkC,SD_BOTH) ;
          closesocket(SkC);
          mmo1.Lines.Add('服务器关闭');
        end;
        FD_READ :
        begin
          ret := recv(SkC,fbuf,256,0);
          mmo1.Lines.Add('服务器端来的消息 :' + fbuf);
        end;
      end;
    end;procedure TForm1.btnSeverClick(Sender: TObject);
    begin
      FillChar(pData,Length(pData),0);
      StrCopy(pData,PChar(edtSever.Text));
      send(AcceptSock,pData,Length(pData) * SizeOf(Char) ,0);
    end;procedure TForm1.btnClientClick(Sender: TObject);
    begin
      FillChar(pData,Length(pData),0);
      StrCopy(pData,PChar(edtClient.Text));
      send(SkC,pData,Length(pData) * SizeOf(Char) ,0);
      {
          呼叫 send 或 sendto 传送资料时,系统告知错误,且错误码为10035 WSAEWOULDBLOCK(呼叫 WSAGetLastError 得知这项错误),
          这时表示 output buffer 已经满了,无法再写入任何资料(此时即令呼叫再多次的send 也都一定失败);
          一旦系统将部份资料成功送抵对方,空出 output buffer後,便会送一个 FD_WRITE 给使用者,
          告知可继续传送资料了。换句话说,在呼叫 send 传送资料时,只要不是返回错误 10035 的话,
          便可一直继续呼叫 send 来传送资料;一旦 send 回返错误 10035,那麽便不要再呼叫 send传送资料,
          而须等收到 FD_WRITE 後,再继续传送资料。
      }
    end;procedure TForm1.btnServerCloseClick(Sender: TObject);
    begin
      shutdown(AcceptSock,SD_BOTH);
      closesocket(AcceptSock);
    end;procedure TForm1.btnClientCloseClick(Sender: TObject);
    begin
      //断开连接 how参数可以是下面的任何一个值: SD_RECEIVE、SD_SEND或SD_BOTH。如果是SD_RECEIVE,
      //就表示不允许再调用接收函数
      shutdown(SkC,SD_BOTH);
      closesocket(SkC)
    end;initialization
    StartUp;
    finalization
    WSACleanup;
    end.
      

  3.   

    能把你的代码给我发到邮箱吗?[email protected]
      

  4.   

    弄个抓包工具,如smsniff之类的,看你的数据有没有发送出去
      

  5.   

    给你写了个简单的例子Serve
    const
      BUF_SIZE = 64;
    var
      wsd: TWSADATA;
      s: TSOCKET;
      nRet, socketSrv: Integer;
      addrClient, addrSrv: SOCKADDR_IN;
      buf: array[0..BUF_SIZE] of Char;
      len: Integer;
    begin
        // 初始化套接字动态库
      if WSAStartup(MAKEWORD(2, 2), wsd) <> 0 then
      begin
        ShowMessage('WSAStartup failed !');
        Exit;
      end;    // 创建套接字
      S := socket(AF_INET, SOCK_DGRAM, 0);
      if (s = INVALID_SOCKET) then
      begin
        ShowMessage(Format('socket() failed ,Error Code: %d', [WSAGetLastError]));
        WSACleanup();
        Exit;
      end;  socketSrv := socket(AF_INET, SOCK_DGRAM, 0);  len := sizeof(SOCKADDR);    // 设置服务器地址
      ZeroMemory(@buf[0], BUF_SIZE);
      addrSrv.sin_addr.S_addr := htonl(INADDR_ANY);
    //    addrSrv.sin_addr.S_un.S_addr := htonl(INADDR_ANY);
      addrSrv.sin_family := AF_INET;
      addrSrv.sin_port := htons(5000);    // 绑定套接字
      nRet := bind(socketSrv, PSOCKADDR(@addrSrv), sizeof(SOCKADDR));
      if (SOCKET_ERROR = nRet) then
      begin
        ShowMessage('bind failed !');
        closesocket(s);
        WSACleanup();
        Exit
      end;    // 从客户端接收数据
      nRet := recvfrom(socketSrv, buf, BUF_SIZE, 0, addrClient, len);
      if (SOCKET_ERROR = nRet) then
      begin
        ShowMessage('recvfrom failed !');
        closesocket(s);
        WSACleanup();
      end;    // 打印来自客户端发送来的数据
      ShowMessage('Recv From Client' + buf);    // 向客户端发送数据
      buf := 'UDP Hello World ';
      sendto(socketSrv, buf, Length('UDP Hello World !'), 0, addrClient, len);
      closesocket(s);
      WSACleanup();
    end;Clientconst
      BUF_SIZE = 64;
    var
      wsd: TWSADATA;
      s: TSOCKET;
      nRet, sockClient: Integer;
      servAddr, addrSrv: SOCKADDR_IN;
      buf: array[0..BUF_SIZE] of Char;
      nServAddLen: Integer;
    begin
        // 初始化套接字动态库
      if WSAStartup(MAKEWORD(2, 2), wsd) <> 0 then
      begin
        ShowMessage('WSAStartup failed !');
        Exit;
      end;    // 创建套接字
      S := socket(AF_INET, SOCK_DGRAM, 0);
      if (s = INVALID_SOCKET) then
      begin
        ShowMessage(Format('socket() failed ,Error Code: %d', [WSAGetLastError]));
        WSACleanup();
        Exit;
      end;  sockClient := socket(AF_INET, SOCK_DGRAM, 0);
      ZeroMemory(@buf[0], BUF_SIZE);
      StrCopy(buf, 'UDP Hello World !');    // 设置服务器地址
      servAddr.sin_family := AF_INET;
      servAddr.sin_addr.S_addr := inet_addr('127.0.0.1');
      servAddr.sin_port := htons(5000);    // 向服务器发送数据
      nServAddLen := sizeof(servAddr);
      if (sendto(sockClient, buf, BUF_SIZE, 0, servAddr, nServAddLen) = SOCKET_ERROR)
        then
      begin
        ShowMessage(Format('recvfrom() failed', [WSAGetLastError()]));
        closesocket(s);
        WSACleanup();
        Exit;
      end;
      nRet := recvfrom(sockClient, buf, BUF_SIZE, 0, servAddr, nServAddLen);
      if (SOCKET_ERROR = nRet) then
      begin
        ShowMessage('recvfrom failed !\n');
        closesocket(s);
        WSACleanup();
        Exit;
      end;    // 打印来自服务端发送来的数据
      ShowMessage('Recv From Server: ' + buf);
      closesocket(s);
      WSACleanup();
    end;
      

  6.   

    [email protected]你发这个里面,谢谢啦
      

  7.   

    楼主的代码中错误很多:
    服务器端:
    1、SockAddr_In.sin_port := htonl(9315); 
    这里要改成 htons(9315);2、nrev:=recv(FSock,FBuf,1024,1); //为什么nrev的返回值一直为-1
    因为 Recv的Flag不支持 1,要修改为0,不知道FBuf为何物,应该是这样吧?
    nrev:=recv(FSock,RevMsg,1024,0); 另外,下面要取发过来的地址,应该采用recvFrom()
    完整的代码应该是这样:var
      ErrorCode, AddrSize : integer;
      FSock : TSocket;
      SockAddr_In,Add: TSockAddrIn;
      nrev:Integer;
      revMsg:array[0..1024] of Char;
      Str : string;
      WSADATA: TWSAData;
    begin
      winsock.WSAStartup($0101,WSADATA);
      FSock := socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if FSock = SOCKET_ERROR then 
      begin
        showmessage('创建套接字失败' );
        Exit;
      end;
      SockAddr_In.sin_family := AF_INET; 
      SockAddr_In.sin_port := htons(9315);
      SockAddr_In.sin_addr.S_addr := INADDR_ANY;
      ErrorCode := bind(FSock,SockAddr_In,sizeof(SockAddr_In));
      if ErrorCode = SOCKET_ERROR then
      begin
        showmessage(Format('%s;ErrorCode:%d',['绑定失败:',WSAGetLastError]) ); 
        Exit; 
      end;
      while true do
      begin
    //    nrev:=recv(FSock,revMsg,1024,0); //为什么nrev的返回值一直为-1
        AddrSize := sizeof(Add);
        nrev := RecvFrom(FSock, revMsg, 1024, 0, Add, AddrSize);
        if nrev>0 then
        begin
          SetString(Str, revMsg, nRev);
          ShowMessage('接收到来自:'+inet_ntoa(Add.sin_addr) + ': ' + Str);
          Break;
        end;
      end;
      closesocket(FSock);
      WsaCLeanup;
      ShowMessage('UDP服务测试结束');
    end;
    客户端:
    1、SockAdd_Inc.sin_port:=9315;
    改为
    SockAdd_Inc.sin_port:=htons(9315);
    2、sendto的Flag也不能用1完整的代码应该是这样:
    var
      ErrorCode:Integer; 
      msg:array[0..100] of Char; 
      SockAdd_Inc:TSockAddrIn;
      Skc:TSocket;
      WSAData: TWSAData;
    begin
      ErrorCode:=WSAStartup($0101,WSAData);
      if ErrorCode <>0 then
      begin 
        ShowMessage('加载WinSock DLL失败!'); 
      end; 
      Skc:=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); 
      if Skc=SOCKET_ERROR then 
      begin
        ShowMessage('创建套接字失败!');
        Exit;
      end;
      FillChar(SockAdd_Inc, sizeof(SockAdd_Inc), 0);
      SockAdd_Inc.sin_family:=AF_INET;
      SockAdd_Inc.sin_port:=htons(9315);
      SockAdd_Inc.sin_addr.S_addr:=inet_addr('127.0.0.1');
      StrPCopy(msg, 'Test');
      ErrorCode := sendto(Skc,msg,StrLen(msg),0,SockAdd_Inc,SizeOf(SockAdd_Inc));
      if ErrorCode < 0 then
      begin
        ShowMessage('发送失败!'+inttostr(WSAGetLastError()));
      end;
      closesocket(Skc);
      wsaCleanup;
    end;
      

  8.   

    我自己在看看,在请教下,recvfrom()中的flag怎么理解
    SDK中这样解释Specifies the way in which the call is made.(指定以何种方式实现调用)
    这个参数都可以填哪些值,什么情况下用哪些值。
      

  9.   

    SDK说可以填 MSG_OOB MSG_PEEK,与所使用的协议支持有关,OOB(Out of Band)用于有边界的协议,如X.25、OSI、TCP等,呃,还是看SDK吧...
      

  10.   

    仔细看了一下,MSG_PEEK:查看数据(返回栈缓冲中的数据大小而不从缓冲中移出数据),MSG_OOB:带外数据(为紧急的数据查看的需要),实际编程中建议都不要使用,没什么好处,填0就好了
      

  11.   

    谢谢各位了,帖子先放这里了,noeyes277 正解,sanguomi的也不错,2楼的完全是从别地COPY来的,明天来结。在问个小小的问题,老板刚给我说
    "做线程的时候 不能用Delphi中的那个Thread类,那个性能不好,而且不利于移植,说是要自己写,"请问下哪位高手这样的话我该用什么,怎么用。也可以稍微提示下,我在查相关资料。子。我连菜鸟也够不上,最好能说的通俗点,谢谢了。
    要是复杂了,我在另开个帖子,令加分。
      

  12.   

    THREAD 对付你们这种应用完全够用了
     
      

  13.   

    当我什么都没说吧,sorry...
    我不知道你是在哪里找到跟我一模一样的代码的
      

  14.   

    楼上的,还是要谢谢你,虽然别地也有类似的代码但是没有你的完整,你的DEMO还是很有参考价值,也许真正的高手写的DEMO都是一样的吧,同样谢谢其他高手。