c++ получение данных с серверного сокета

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by realcoder, 18 Jul 2011.

  1. realcoder

    realcoder Member

    Joined:
    9 Dec 2010
    Messages:
    226
    Likes Received:
    11
    Reputations:
    4
    добрый день! у меня такая проблема:
    пишу клиент-сервер(радмин). клиент на дельфе, сервер на с++.
    работает по бинарному протоколу:
    сначала заголовок:
    Code:
    struct header{
    DWORD sing;//сингапура, подтверждающая что это нужный пакет
    DWORD command;//номер команды
    DWORD seq;//id команды(номер пакета). ответ на нее отправляется с таким же id
    DWORD datalen;// длинна данных посылаемых после заголовка(например скриншот, имя пользователя компа или имя директории которую нужно просмотреть)
    };
    //константы такие пока только:
    #define SIGN 0x007754
    #define CS_HELLO 0x001
    #define SC_HELLO_ACK 0x0010
    
    вот код сервера:
    Code:
    #ifndef UNICODE
    #define UNICODE
    #endif
    
    #include <winsock2.h>
    #include <stdio.h>
    #include <windows.h>
    #include "proto.h"
    
    // Need to link with Ws2_32.lib
    #pragma comment(lib, "Ws2_32.lib")
    
    int wmain(void)
    {
    
        //----------------------
        // Initialize Winsock.
        WSADATA wsaData;
        int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != NO_ERROR) {
            wprintf(L"WSAStartup failed with error: %ld\n", iResult);
            return 1;
        }
        //----------------------
        // Create a SOCKET for listening for
        // incoming connection requests.
        SOCKET ListenSocket;
        ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (ListenSocket == INVALID_SOCKET) {
            wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }
        //----------------------
        // The sockaddr_in structure specifies the address family,
        // IP address, and port for the socket that is being bound.
        sockaddr_in service;
        service.sin_family = AF_INET;
        service.sin_addr.s_addr = inet_addr("127.0.0.1");
        service.sin_port = htons(27015);
    
        if (bind(ListenSocket,
                 (SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) {
            wprintf(L"bind failed with error: %ld\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
        //----------------------
        // Listen for incoming connection requests.
        // on the created socket
        if (listen(ListenSocket, 1) == SOCKET_ERROR) {
            wprintf(L"listen failed with error: %ld\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
        //----------------------
        // Create a SOCKET for accepting incoming requests.
        SOCKET AcceptSocket;
        wprintf(L"Waiting for client to connect...\n");
    
        //----------------------
        // Accept the connection.
        AcceptSocket = accept(ListenSocket, NULL, NULL);
        if (AcceptSocket == INVALID_SOCKET) {
            wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        } else
            wprintf(L"Client connected.\n");
    	header pack;
    	ZeroMemory((LPVOID)&pack,sizeof(pack));
    	while(true)
    	{
    	int lResult=0;
    		lResult=recv(ListenSocket,(char *)&pack,sizeof(pack),0);
    	if(lResult==0)
    	continue;
    	else
    	{
    		printf("seq: %d cmd: %d",pack.seq,pack.command);	
    	    pack.command=SC_HELLO_ACK;
    		send(ListenSocket,(char *)&pack,sizeof(pack),0);
    		wprintf(L"cmd sended...");
        // No longer need server socket
        closesocket(ListenSocket);
    	getchar();
    	break;
    	}
    	}
        WSACleanup();
        return 0;
    }
    
    вот код клиента:


    Code:
    unit main;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, Sockets, StdCtrls,winsock,proto;
    
    type
      Tmainfrm = class(TForm)
        ConnBtn: TButton;
        hostedit: TEdit;
        portedit: TEdit;
        procedure ConnBtnClick(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      mainfrm: Tmainfrm;
    
    implementation
    
    {$R *.dfm}
    function pack(cmd,seq,dlen:DWORD):header;
    begin
    result.sing:=SIGN;
    result.command:=cmd;
    result.seq:=seq;
    result.datalen:=dlen;
    end;
    Function LookupName(name:String): TInAddr;
    var
     HostEnt: PHostEnt;
     InAddr: TInAddr;
    begin
      HostEnt := gethostbyname(PChar(name));
      FillChar(InAddr, SizeOf(InAddr), 0);
      if HostEnt <> nil then
       begin
        with InAddr, HostEnt^ do
         begin
          S_un_b.s_b1 := h_addr^[0];
          S_un_b.s_b2 := h_addr^[1];
          S_un_b.s_b3 := h_addr^[2];
          S_un_b.s_b4 := h_addr^[3];
         end;
       end;
      Result := InAddr;
    end;
    
    procedure Tmainfrm.ConnBtnClick(Sender: TObject);
    var WD:WSAData;
    sock:THandle;
    sa:SockAddr_IN;
    data:header;
    begin
    If WSAStartup(MakeWord(2,2),WD)<>0 then
    begin
    MessageBox(mainfrm.Handle,'Initialization failed!','Error',MB_OK or MB_ICONERROR);
    exit;
    end;
    sock:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if sock=INVALID_HANDLE_VALUE then
    begin
    MessageBox(mainfrm.Handle,'winsock socket() failed!','Error',MB_OK or MB_ICONERROR);
    exit;
    end;
    sa.sin_family:=AF_INET;
    sa.sin_port:=htons(StrToInt(portedit.Text));
    sa.sin_addr:=lookupname(pchar(hostedit.Text));
    if connect(sock,sa,sizeof(sa))<>0 then
    begin
    MessageBox(mainfrm.Handle,'connection failed!','Error',MB_OK or MB_ICONERROR);
    exit;
    end;
    data:=pack(CS_HELLO,1,0);
    send(sock,data,sizeof(data),0);
    ZeroMemory(@data,sizeof(data));
    recv(sock,data,sizeof(data),0);
    if (data.command=SC_HELLO_ACK)and(data.sing=SIGN)and(data.seq=0) then
    begin
    showmessage('ok! command hello_ack recived!');
    CLoseSocket(sock);
    WsaCleanUP();
    
    end;
    end;
    
    end.
    
    
    дело в том что на сервер ничего не приходит. когда послыаю заголовок из клиента - сниффер показывает его как надо, но когда ставлю снифер на сервер - на сокет не приходит не чего, хотя там стоит проверка:
    Code:
    	int lResult=0;
    		lResult=recv(ListenSocket,(char *)&pack,sizeof(pack),0);
    	if(lResult==0)
    	continue;
    	else
    	{
    		printf("seq: %d cmd: %d",pack.seq,pack.command);	
    	    pack.command=SC_HELLO_ACK;
    		send(ListenSocket,(char *)&pack,sizeof(pack),0);
    		wprintf(L"cmd sended...");
        // No longer need server socket
        closesocket(ListenSocket);
    	getchar();
    	break;
    	}
    
    подскажите, как решить такую проблему?
     
  2. rudi

    rudi Active Member

    Joined:
    3 Jun 2010
    Messages:
    487
    Likes Received:
    184
    Reputations:
    5
    Клиент отправляет на порт 27015 ?
     
  3. realcoder

    realcoder Member

    Joined:
    9 Dec 2010
    Messages:
    226
    Likes Received:
    11
    Reputations:
    4
    да. в эдит вввожу 27015-й порт и хост 127.0.0.1
    пишет что приконнектился, сокет создался(в сниффере видно), а данных нет
     
  4. alexey-m

    alexey-m Elder - Старейшина

    Joined:
    15 Jul 2009
    Messages:
    518
    Likes Received:
    100
    Reputations:
    37
    вот код сервера:
    Code:
        ...........
        AcceptSocket = accept(ListenSocket, NULL, NULL);
    
        if (AcceptSocket == INVALID_SOCKET) {
        ...........
        } else
        ...........
    	while(true)
    	{
    		lint lResult=0;
    
    		lResult=recv(ListenSocket,(char *)&pack,sizeof(pack),0);
        ...........
    
    А теперь почитай чего возвращает accept в msdn
     
  5. rudi

    rudi Active Member

    Joined:
    3 Jun 2010
    Messages:
    487
    Likes Received:
    184
    Reputations:
    5
    используй select
     
  6. alexey-m

    alexey-m Elder - Старейшина

    Joined:
    15 Jul 2009
    Messages:
    518
    Likes Received:
    100
    Reputations:
    37
    rudi, причем тут select?
    он пытается прочитать\писать данные из "слушающего" сокета, когда надо читать\писать в созданный сокет AcceptSocket который связывается с подключившимся клиентом
     
    1 person likes this.
  7. realcoder

    realcoder Member

    Joined:
    9 Dec 2010
    Messages:
    226
    Likes Received:
    11
    Reputations:
    4
    alexey-m, спасибо
     
  8. realcoder

    realcoder Member

    Joined:
    9 Dec 2010
    Messages:
    226
    Likes Received:
    11
    Reputations:
    4
    возник еще вопрос. как на сервере определить что клиент отключился? переписал код сервера в более удобный вид:
    Code:
    #ifndef UNICODE
    #define UNICODE
    #endif
    
    #include <winsock2.h>
    #include <stdio.h>
    #include <windows.h>
    #include "proto.h"
    
    // Need to link with Ws2_32.lib
    #pragma comment(lib, "Ws2_32.lib")
    
    void newclient(SOCKET AcceptSocket)
    {
    	header pack;
    	ZeroMemory((LPVOID)&pack,sizeof(pack));
    	while(WSAGetLastError()==0)
    	{
    		int lResult=0;
    		lResult=recv(AcceptSocket,(char *)&pack,sizeof(pack),0);
    		if(lResult==0)
    			continue;
    		else
    		{
    			if((pack.command==CS_HELLO)&&(pack.sing==SIGN))
    			{
    			wprintf(L"Hello command recived!\n");
    			pack.command=SC_HELLO_ACK;
    			send(AcceptSocket,(char *)&pack,sizeof(pack),0);
    			wprintf(L"Hello_ack command sended...");
    			
    			}
    	
    		}
    	}
    	wprintf(L"Client Disconnected...\n");
    }
    int CreateServer()
    {
    	 //----------------------
        // Initialize Winsock.
        WSADATA wsaData;
        int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != NO_ERROR) {
            wprintf(L"WSAStartup failed with error: %ld\n", iResult);
            return 1;
        }
        //----------------------
        // Create a SOCKET for listening for
        // incoming connection requests.
        SOCKET ListenSocket;
        ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (ListenSocket == INVALID_SOCKET) {
            wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }
        //----------------------
        // The sockaddr_in structure specifies the address family,
        // IP address, and port for the socket that is being bound.
        sockaddr_in service;
        service.sin_family = AF_INET;
        service.sin_addr.s_addr = htonl(INADDR_ANY);
        service.sin_port = htons(27015);
    
        if (bind(ListenSocket,
                 (SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) {
            wprintf(L"bind failed with error: %ld\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
        //----------------------
        // Listen for incoming connection requests.
        // on the created socket
        if (listen(ListenSocket, 1) == SOCKET_ERROR) {
            wprintf(L"listen failed with error: %ld\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
        //----------------------
        // Create a SOCKET for accepting incoming requests.
        SOCKET AcceptSocket;
        wprintf(L"Waiting for client to connect...\n");
    
        //----------------------
        // Accept the connection.
        while(true)
    	{
    	AcceptSocket = accept(ListenSocket, NULL, NULL);
        if (AcceptSocket == INVALID_SOCKET) {
            wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        } else
    	{
            wprintf(L"Client connected.\n");
    		newclient(AcceptSocket);
    	}
    	}
    
        WSACleanup();
    }
    int wmain(void)
    {
      return CreateServer();
    }
    
     
  9. alexey-m

    alexey-m Elder - Старейшина

    Joined:
    15 Jul 2009
    Messages:
    518
    Likes Received:
    100
    Reputations:
    37
    realcoder, твой сервер сейчас только для оного клиента, остальные в очереди думаешь будут стоять ждать? посмотри тут хотя бы, не идеал но все же =)
     
  10. realcoder

    realcoder Member

    Joined:
    9 Dec 2010
    Messages:
    226
    Likes Received:
    11
    Reputations:
    4
    спасибо.
    как по твоему несколько клиентов будут управлять одной машиной?
    если много клиентов, то я бы тогда функцию newclient сделал потоком:
    unsigned long __stdcall newclient(void *params)
    и создавал бы новый поток при получении соединения
     
  11. Ins3t

    Ins3t Харьковчанин

    Joined:
    18 Jul 2009
    Messages:
    939
    Likes Received:
    429
    Reputations:
    139
    ну так создавай через _beginthreadex() отдельный поток для каждого пользователя и обслуживай его там.
     
  12. realcoder

    realcoder Member

    Joined:
    9 Dec 2010
    Messages:
    226
    Likes Received:
    11
    Reputations:
    4
    я же написал: одновременно к сервере может обслуживаться только один клиент т.к. это РАДМИН