2013-10-05 301 views
-1

我決定創建一個應用程序,只添加到聊天列表中的人聊天。服務器和客戶端有一個結構User.I需要你的幫助,因爲在從客戶端發送到服務器的數據 我沒有它在服務器端,它不工作...它不是很酷。聊天客戶端服務器C++

服務器:

sock=socket(AF_INET,SOCK_STREAM,0); 
    listen(sock,5); 
    accept(dlg->sock,(sockaddr*)&(dlg->serv),&(dlg->addlen)); 
    AfxBeginThread(thread,0); 
    memset(dlg->logined.login,NULL,sizeof(dlg->logined.login)); 
    memset(dlg->logined.pass,NULL,sizeof(dlg->logined.pass)); 
    recv(dlg->sock, dlg->logined.login, sizeof(dlg->logined.login),0); 
    dlg->m_list.InsertItem(dlg->count++,dlg->logined.login); 
    ... 

和客戶端:

UINT thread(LPVOID v) 
{ 
    char buff[100] 
    CSize size; 
    size.cx=0; 
    size.cy=30; 
    int s=1,addcount=0; 

CClisockDlg *dlg=(CClisockDlg*) AfxGetApp()->GetMainWnd(); 

dlg->m_connect.EnableWindow(FALSE); 
dlg->m_disconnect.EnableWindow(TRUE); 
while(connect(dlg->clisock,(sockaddr*)&(dlg->cli),sizeof(dlg->cli)) && dlg->ee!=0) 
{ 
    dlg->m_edit.SetWindowText("Connection...wait"); 
    for (int i=0;i<=65000;i++) 
     for(int j=0;j<=200;j++); 
    if (addcount==25) 
     addcount=0; 
    dlg->cli.sin_addr.s_addr=inet_addr(dlg->user.ip); 
} 
    if (dlg->ee==1) 
    dlg->m_list.InsertItem(dlg->count++,"Connected"); 
    dlg->SetForegroundWindow(); 
while((s=recv(dlg->clisock,buff,100,0))!=SOCKET_ERROR && dlg->ee!=0) 
{ 

    dlg->SetForegroundWindow(); 
    if (s!=SOCKET_ERROR && dlg->ee!=0) 
    dlg->m_list.InsertItem(dlg->count++,buff); 
    dlg->m_list.Scroll(size); 
} 
send(dlg->clisock,"Disconnected",100,0); 
dlg->m_connect.EnableWindow(TRUE); 
dlg->m_disconnect.EnableWindow(FALSE); 
closesocket(dlg->clisock); 
AfxEndThread(0); 
return 0; 
} 


void CClisockDlg::OnButton2() // Button m_connect  
{ 

    m_edit2.GetWindowText(user.ip,sizeof(user.ip)); 
    m_edit3.GetWindowText(user.login,sizeof(user.login)); 
    m_edit4.GetWindowText(user.pass,sizeof(user.pass)); 
    cli.sin_family=AF_INET; 
    cli.sin_port=htons(5000); 
    cli.sin_addr.s_addr=inet_addr(user.ip); 
    clisock=socket(AF_INET,SOCK_STREAM,0); 
    send(clisock,user.login,sizeof(user.login),0); 
    send(clisock, user.pass, sizeof(user.pass), 0); 
    AfxBeginThread(thread,0); 
} 

如何使數據結構已在服務器上被採納?現在連接已建立,數據登錄和密碼丟失。

+0

您應指定特定的問題,預計投入和產出。 _'doesn't工作...它不是很酷'_是一些備用信息,不是嗎? –

+0

顯示您的文本緩衝區的數據類型。 –

+0

我有三個字段的形式 - IP,登錄,密碼。在填充字段後,您按下唯一的按鈕如何讓添加客戶端通過單擊進行聊天?到ScottMcP-MVP結構用戶:{char ip [30]; char login [16]; char password [16]} – user2849744

回答

1

你可以做我所做的事我猜。先創建一個套接字通信類,並確保它的工作原理。

我有一些工作代碼,我前面寫了一段時間。你可以看到它是如何做到以下..我也上載的服務器端和客戶端的源(使用代碼塊和GCC/G ++ 4.8.1編譯):http://www.mediafire.com/download/6j84bedkp3s3sq5/Socket+Chat.zip

Socket.hpp:

#ifndef SOCKETS_HPP_INCLUDED 
#define SOCKETS_HPP_INCLUDED 

#include <Winsock2.h> 
#include <Windows.h> 
#include <Ws2tcpip.h> 
#include <iostream> 
#include <stdexcept> 

#define WM_SOCKET 0x10000 

class Socket 
{ 
    private: 
     SOCKET socket; 
     std::uint32_t Port; 
     std::string Address; 
     HWND WindowHandle; 
     bool Listen, Initialized, Asynchronous; 

    public: 
     Socket(){}; 
     Socket(std::uint32_t Port, std::string Address, bool Listen = false, HWND WindowHandle = nullptr, bool Asynchronous = false); 
     ~Socket(); 
     int Recv(void* Buffer, std::uint32_t BufferLength); 
     int Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength); 
     int Send(void* Buffer, std::size_t BufferSize); 
     int Send(SOCKET S, void* Buffer, std::size_t BufferSize); 

     void Connect(std::uint32_t Port, std::string Address, bool Listen, HWND WindowHandle, bool Asynchronous); 
     SOCKET Accept(sockaddr* ClientInfo, int* ClientInfoSize); 
     SOCKET GetSocket() const; 
     void Close(); 
}; 

#endif // SOCKETS_HPP_INCLUDED 

Socket.cpp:

#include "Sockets.hpp" 

std::string ErrorMessage(std::uint32_t Error, bool Throw = true) 
{ 
    LPTSTR lpMsgBuf = nullptr; 
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, Error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, nullptr); 
    if (Throw) 
    { 
     throw std::runtime_error(lpMsgBuf); 
    } 
    return lpMsgBuf; 
} 

Socket::~Socket() 
{ 
    Close(); 
} 

void Socket::Close() 
{ 
    if (socket) 
    { 
     shutdown(socket, SD_BOTH); 
     closesocket(socket); 
     socket = 0; 
    } 

    if (Initialized) 
    { 
     WSACleanup(); 
    } 
} 

SOCKET Socket::GetSocket() const {return this->socket;} 

Socket::Socket(std::uint32_t Port, std::string Address, bool Listen, HWND WindowHandle, bool Asynchronous) : socket(0) 
{ 
    Connect(Port, Address, Listen, WindowHandle, Asynchronous); 
} 

void Socket::Connect(std::uint32_t Port, std::string Address, bool Listen, HWND WindowHandle, bool Asynchronous) 
{ 
    if (!socket) 
    { 
     this->Port = Port; 
     this->Address = Address; 
     this->Listen = Listen; 
     this->WindowHandle = WindowHandle; 
     this->Asynchronous = Asynchronous; 
     this->Initialized = true; 

     WSADATA wsaData; 
     struct sockaddr_in* sockaddr_ipv4; 

     if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) 
     { 
      throw std::runtime_error("Error: " + ErrorMessage(WSAGetLastError())); 
     } 

     if ((this->socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) 
     { 
      this->Close(); 
      throw std::runtime_error("Error: " + ErrorMessage(WSAGetLastError())); 
     } 

     if (Address != "INADDR_ANY") 
     { 
      struct addrinfo *result = nullptr; 
      getaddrinfo(Address.c_str(), nullptr, nullptr, &result); 
      struct addrinfo* it; 
      for (it = result; it != nullptr; it = it->ai_next) 
      { 
       sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr); 
       Address = inet_ntoa(sockaddr_ipv4->sin_addr); 
       if (Address != "0.0.0.0") break; 
      } 
      freeaddrinfo(result); 
     } 

     SOCKADDR_IN SockAddr; 
     memset(&SockAddr, 0, sizeof(SockAddr)); 
     SockAddr.sin_port = htons(Port); 
     SockAddr.sin_family = AF_INET; 
     SockAddr.sin_addr.s_addr = (Address == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(Address.c_str())); 

     if (Listen && (bind(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR)) 
     { 
      this->Close(); 
      std::string Error = ErrorMessage(WSAGetLastError()); 
      throw std::runtime_error("Error: " + ErrorMessage(WSAGetLastError())); 
     } 

     if (Asynchronous && WindowHandle) 
     { 
      if(WSAAsyncSelect(socket, WindowHandle, WM_SOCKET, FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE | FD_ACCEPT) != 0) 
      { 
       this->Close(); 
       throw std::runtime_error("Error: " + ErrorMessage(WSAGetLastError())); 
      } 
     } 

     if (Listen && (listen(this->socket, SOMAXCONN) == SOCKET_ERROR)) 
     { 
      this->Close(); 
      throw std::runtime_error("Error: " + ErrorMessage(WSAGetLastError())); 
     } 

     if(!Listen && (connect(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR)) 
     { 
      if(Asynchronous && WindowHandle && (WSAGetLastError() != WSAEWOULDBLOCK)) 
      { 
       this->Close(); 
       throw std::runtime_error("Error: " + ErrorMessage(WSAGetLastError())); 
      } 
     } 
    } 
} 

SOCKET Socket::Accept(sockaddr* ClientInfo, int* ClientInfoSize) 
{ 
    static int Size = sizeof(sockaddr); 
    return accept(this->socket, ClientInfo, (ClientInfo && ClientInfoSize ? ClientInfoSize : &Size)); 
} 
int Socket::Recv(void* Buffer, std::uint32_t BufferLength) 
{ 
    return recv(this->socket, reinterpret_cast<char*>(Buffer), BufferLength, 0); 
} 

int Socket::Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength) 
{ 
    return recv(S, reinterpret_cast<char*>(Buffer), BufferLength, 0); 
} 

int Socket::Send(void* Buffer, std::size_t BufferSize) 
{ 
    return send(this->socket, reinterpret_cast<char*>(Buffer), BufferSize, 0); 
} 

int Socket::Send(SOCKET S, void* Buffer, std::size_t BufferSize) 
{ 
    return send(S, reinterpret_cast<char*>(Buffer), BufferSize, 0); 
} 

以上僅僅是圍繞一個插座,使事情非常容易使用的包裝。

現在對於客戶端和服務器窗口,我創建了另一個WinAPI函數的包裝器,以使窗口的創建變得容易!

Window.hpp:

#ifndef WINDOW_HPP_INCLUDED 
#define WINDOW_HPP_INCLUDED 

#include <windows.h> 
#include <string> 

class Window 
{ 
    private: 
     HWND WindowHandle; 
     static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam); 

    public: 
     void Create(std::string ClassName, std::string Title, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT, WNDPROC WindowProcedure = nullptr, WNDCLASSEX WndClass = {0}); 
     HWND GetWindowHandle(); 
}; 

#endif // WINDOW_HPP_INCLUDED 

Window.cpp:

#include "Window.hpp" 

LRESULT __stdcall Window::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam) 
{ 
    switch(Msg) 
    {  
     case WM_DESTROY: 
      PostQuitMessage(0); 
      return 0; 

     default: 
      return DefWindowProc(Hwnd, Msg, wParam, lParam); 
    } 
    return 0; 
}; 

void Window::Create(std::string ClassName, std::string Title, int Width, int Height, WNDPROC WindowProcedure, WNDCLASSEX WndClass) 
{ 
    if (WindowProcedure == nullptr) 
    { 
     WindowProcedure = Window::WindowProcedure; 
    } 

    if (WndClass.cbSize == 0) 
    { 
     WndClass = 
     { 
      sizeof(WNDCLASSEX), CS_DBLCLKS, WindowProcedure, 
      0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION), 
      LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_WINDOW), 
      nullptr, ClassName.c_str(), LoadIcon (nullptr, IDI_APPLICATION) 
     }; 
    } 

    if(RegisterClassEx(&WndClass)) 
    { 
     this->WindowHandle = CreateWindowEx(0, ClassName.c_str(), Title.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); 
     if(WindowHandle) 
     { 
      MSG msg = {nullptr}; 
      ShowWindow(WindowHandle, SW_SHOWDEFAULT); 
      while(GetMessage(&msg, nullptr, 0, 0)) 
      { 
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      } 
     } 
    } 
} 

Protocol.hpp:

#include "Sockets.hpp" 
    #include <iostream> 
/** 
     Packet Protocol Definition. Can probably make this into an Enum later when it gets more complex. 
     This protocol determines how a packet is read. Is it a packet for the server? The client? What type? etc.. 
    **/ 
    const int PACKET_PROTOCOL_SERVER_ID = -3; //A Server packet telling all clients a global message or Sends clients a unique ID upon connect. 
    const int PACKET_PROTOCOL_UPDATE_ID = -2; //Sends a packet to the server telling it we want to update other clients with our new info. 
    const int PACKET_PROTOCOL_CLIENT_DISC = -1; //A client has disconnected, update our contacts list. 
    const int PACKET_PROTOCOL_CLIENT_CONN = 0; //A client has connected, update our contacts list. 

    //const int PACKET_PROTOCOL_    //Add other protocols such as admin-login, authenticate, etc.. 
               //If you add more, don't forget to update the Packet struct. 


    /** 
     A structure that represents a packet to be sent over a network/socket. 
    **/ 
    struct Packet 
    { 
     std::int32_t ID; 
     std::int32_t To; 
     std::int32_t From; 

     std::string Name; 
     std::string Message; 
    }; 

    template <typename T> 
    T ReadPointer(char*& Pointer) 
    { 
     T Result = *(reinterpret_cast<T*>(Pointer)); 
     Pointer += sizeof(T); 
     return Result; 
    } 

    template <typename T> 
    void WritePointer(char*& Pointer, const T& Value) 
    { 
     *(reinterpret_cast<T*>(Pointer)) = Value; 
     Pointer += sizeof(T); 
    } 

    /** 
     Serializes a packet into a buffer of unsigned-chars.. aka bytes. Then sends it through the socket. 
    **/ 
    bool WritePacket(SOCKET s, Packet &packet) 
    { 
     if (s) 
     { 
      std::vector<char> Buffer((sizeof(int32_t) * 3) + sizeof(packet.Name.size()) + packet.Name.size() + sizeof(packet.Message.size()) + packet.Message.size(), 0); 
      char* Ptr = Buffer.data(); 

      WritePointer(Ptr, packet.ID); 
      WritePointer(Ptr, packet.To); 
      WritePointer(Ptr, packet.From); 
      WritePointer(Ptr, packet.Name.size()); 

      for (auto it = packet.Name.begin(); it != packet.Name.end(); ++it) 
       WritePointer(Ptr, *it); 

      WritePointer(Ptr, packet.Message.size()); 
      for (auto it = packet.Message.begin(); it != packet.Message.end(); ++it) 
       WritePointer(Ptr, *it); 

      send(s, Buffer.data(), Buffer.size(), 0); 
      return true; 
     } 
     return false; 
    } 

    /** 
     Deserializes a buffer of unsigned-chars.. aka bytes back into a packet. 
    **/ 
    bool ReadPacket(SOCKET s, Packet &packet) 
    { 
     if (s) 
     { 
      packet.Name.clear(); 
      recv(s, reinterpret_cast<char*>(&packet.ID), sizeof(packet.ID), 0); 
      recv(s, reinterpret_cast<char*>(&packet.To), sizeof(packet.To), 0); 
      recv(s, reinterpret_cast<char*>(&packet.From), sizeof(packet.From), 0); 

      decltype(packet.Name.size()) Size = 0; 
      recv(s, reinterpret_cast<char*>(&Size), sizeof(Size), 0); 

      std::vector<char> Buffer(Size, 0); 
      recv(s, Buffer.data(), Buffer.size(), 0); 
      packet.Name.append(Buffer.begin(), Buffer.end()); 
      Buffer.clear(); 

      Size = 0; 
      recv(s, reinterpret_cast<char*>(&Size), sizeof(Size), 0); 

      recv(s, Buffer.data(), Buffer.size(), 0); 
      packet.Message.append(Buffer.begin(), Buffer.end()); 
      return true; 
     } 
     return false; 
    } 

上述所有標頭和源文件的將被用於BOTH客戶端和服務器。該協議是客戶端和服務器之間的鏈接。它描述了數據包以及如何讀取/寫入數據包。服務器和客戶端將如何來回傳遞!


對於服務器我所做的:

在開始: 正如你可以在上面看到,服務器必須先運行。它會聽本地主機上,它會在端口27015.

被監聽客戶端連接: 在連接客戶端,客戶端發出一個唯一的ID,並添加到服務器上的列表中。你可以看到這發生在FD_ACCEPT中。

收到消息: 接下來,如果客戶端發送消息,FD_READ被觸發並且服務器開始讀取客戶端發送的數據包。如果它是更新數據包,則將數據包發送到服務器上的所有其他客戶端,以便其他客戶端可以更新他們關於發送數據包的客戶端的信息。我們還更新存儲在服務器上的客戶端信息。 如果它是一個服務器數據包,我們會使用它並將其發送回客戶端。

客戶端斷開: 一旦斷開,又名FD_CLOSE,你可以看到,服務器會發送一個數據包斷開所有其他客戶,通知他們說,「一些客戶」已經斷開。然後它從列表中刪除客戶端並關閉客戶端的套接字。

服務器的main.cpp中:

#include "Sockets.hpp" 
#include "Window.hpp" 
#include "Protocol.hpp" 
#include <vector> 
#include <map> 


/** 
    Packet Protocol Definition. Can probably make this into an Enum later when it gets more complex. 
    This protocol determines how a packet is read. Is it a packet for the server? The client? What type? etc.. 
**/ 
Socket* sock = nullptr; 
bool SocketConnected = false; 

/** 
    Stores information about each client that connects. 
**/ 
std::vector<std::tuple<int, SOCKET, Packet>> Clients; 

auto FindClient(int ID) -> decltype(Clients.begin()) 
{ 
    for (auto it = Clients.begin(); it != Clients.end(); ++it) 
    { 
     if (std::get<0>(*it) == ID) 
      return it; 
    } 
    return Clients.end(); 
} 

auto FindClient(SOCKET socket) -> decltype(Clients.begin()) 
{ 
    for (auto it = Clients.begin(); it != Clients.end(); ++it) 
    { 
     if (std::get<1>(*it) == socket) 
      return it; 
    } 
    return Clients.end(); 
} 

void SendAll(Packet &packet) 
{ 
    for (auto it = Clients.begin(); it != Clients.end(); ++it) 
    { 
     if (std::get<0>(*it) != packet.From) 
     { 
      packet.To = std::get<0>(*it); 
      WritePacket(std::get<1>(*it), packet); 
     } 
    } 
} 

LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam) 
{ 
    switch(Msg) 
    { 
     case WM_CREATE: 
     { 
      sock = new Socket(27015, "INADDR_ANY", true, Hwnd, true); 
     } 
     break; 

     case WM_SOCKET: /** We received a socket event **/ 
     { 
      switch(WSAGETSELECTEVENT(lParam)) 
      { 
       case FD_WRITE: 
       { 
        SocketConnected = true; 
       } 
       break; 

       case FD_READ: /** We have received a packet from the client. Read the ID and interpret the packet information. **/ 
       { 
        Packet P; 
        ReadPacket(reinterpret_cast<SOCKET>(wParam), P); 

        if (P.ID == PACKET_PROTOCOL_UPDATE_ID) 
        { 
         auto it = FindClient(P.From); 
         if (it != Clients.end()) 
         { 
          Packet* Client = &std::get<2>(*it); 
          Client->Name = P.Name; 

          for (auto it = Clients.begin(); it != Clients.end(); ++it) 
          { 
           P.ID = PACKET_PROTOCOL_UPDATE_ID; 
           P.From = std::get<0>(*it); 
           SendAll(P); 
          } 
         } 
        } 
        else if (P.ID == PACKET_PROTOCOL_SERVER_ID) 
        { 
         auto it = FindClient(P.To); 
         if (it != Clients.end()) 
         { 
          WritePacket(std::get<1>(*it), P); 
         } 
        } 

        SocketConnected = true; 
       } 
       break; 

       case FD_ACCEPT: //A client wants to connect. We accept them and store them in our list. 
       { 
        int ClientID = 1; 
        while(FindClient(ClientID) != Clients.end()) 
        { 
         ++ClientID; 
        } 

        Packet Client; 
        sockaddr_in ClientAddressInfo = {0}; 
        Clients.push_back(std::make_tuple(ClientID, sock->Accept(reinterpret_cast<sockaddr*>(&ClientAddressInfo), nullptr), Client)); 

        Packet PacketInfo; 
        PacketInfo.ID = PACKET_PROTOCOL_SERVER_ID; 
        PacketInfo.To = ClientID; 
        SocketConnected = true; 
        WritePacket(std::get<1>(Clients.back()), PacketInfo); 
       } 
       break; 

       case FD_CLOSE: //A client has disconnected. Notify all other clients and remove the client from our list. 
       { 
        auto it = FindClient(reinterpret_cast<SOCKET>(wParam)); 
        if (it != Clients.end()) 
        { 
         Packet PacketInfo; 
         PacketInfo.ID = PACKET_PROTOCOL_CLIENT_DISC; 
         PacketInfo.From = std::get<0>(*it); 
         SendAll(PacketInfo); 
         Clients.erase(it); 
        } 
       } 
       break; 

       default: 
        break; 
      } 
      break; 
     } 

     case WM_DESTROY: 
     { 
      sock->Close(); 
      delete sock; 
      WSACleanup(); 
      PostQuitMessage(0); 
     } 
     return 0; 

     default: 
      return DefWindowProc(Hwnd, Msg, wParam, lParam); 
    } 

    return 0; 
} 

int main() 
{ 
    Window().Create("Server", "Server", 200, 100, WindowProcedure, {0}); 
} 

對於客戶我所做的:

在開始: 當創建窗口,它會嘗試連接到服務器。

連接: 連接到服務器時,接收到FD_WRITE消息。在FD_WRITE開關的情況下,我們簡單地構建一個基本的數據包,並將其發送到服務器,讓他們知道我們的名字,我們的信息等。該服務器將收到更新報文,並更新我們的信息以及通知所有其他客戶,我們存在。

在接收: 一旦接收到消息,FD_READ被觸發。我們需要讀取從服務器收到的數據包,並將其解讀爲我們的願望。目前,從服務器接收到的第一個數據包是我們發給我們的唯一ID以及連接的所有聯繫人列表。

而且,如果該數據包是不是服務器的分組,我們需要簡單地讀取數據包和文本追加到接收信箱!這將顯示服務器上其他聯繫人或客戶端收到的所有消息。

的斷開連接: 一旦斷開消息被接收(FD_CLOSE)我決定關閉插座而不是重試的連接。它是如此簡單。如果你願意,你可以做delete sock; sock = new....重新連接。

客戶的main.cpp中:

#include "Sockets.hpp" 
#include "Window.hpp" 
#include "Protocol.hpp" 
#include <vector> 

/** 
    Global variables: 
     Socket 
     ClientID 
     Handles for controls 
     IDs for controls 
**/ 

int ClientID = -1; 
int ReceiverID = -1; 
std::string ClientName = "Client"; 
Socket* sock = nullptr; 
bool SocketConnected = false; 
HWND SendBox, ReceiveBox, SendButton; 
enum {SENDBOX_ID, RECEIVEBOX_ID, SENDBUTTON_ID}; 


LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam) 
{ 
    switch(Msg) 
    { 
     case WM_CREATE: 
     { 
      ReceiveBox = CreateWindowEx(WS_EX_STATICEDGE, "Edit", nullptr, WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, 10, 10, 465, 275, Hwnd, (HMENU)RECEIVEBOX_ID, nullptr, nullptr); 
      SendBox = CreateWindowEx(WS_EX_STATICEDGE, "Edit", nullptr, WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL, 10, 315, 465, 110, Hwnd, (HMENU)SENDBOX_ID, nullptr, nullptr); 
      SendButton = CreateWindowEx(0, "Button", "Send", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 385, 430, 90, 25, Hwnd, (HMENU)SENDBUTTON_ID, nullptr, nullptr); 

      sock = new Socket(27015, "localhost", false, Hwnd, true); 
     } 
     break; 

     case WM_COMMAND: /** We received an event from a button/control **/ 
     { 
      switch(LOWORD(wParam)) 
      { 
       case SENDBUTTON_ID: //The send button was pressed so we want to construct a packet from the sendbox's contents and send it to the server. 
       { 
        Packet P; 
        std::vector<std::uint8_t> Buffer(GetWindowTextLength(SendBox) + 1); 
        GetWindowText(SendBox, reinterpret_cast<char*>(Buffer.data()), Buffer.size()); 
        P.Message.append(Buffer.begin(), Buffer.end()); 

        if (!P.Message.empty()) 
        { 
         P.ID = ReceiverID; //The packet is NOT meant for the server. It is meant for the client. 
         P.To = ReceiverID; //We will be sending the packet to some other client. 
         P.From = ClientID; //The packet is from this client. 
         P.Name = ClientName; //Our name.. 
         WritePacket(sock->GetSocket(), P); 
        } 
       } 
       break; 

       case RECEIVEBOX_ID: 
       { 
        if (HIWORD(wParam) == EN_SETFOCUS) 
        { 
         HideCaret(ReceiveBox); 
        } 
        else if (HIWORD(wParam) == EN_KILLFOCUS) 
        { 
         ShowCaret(ReceiveBox); 
        } 
       } 
       break; 
      } 
     } 
     break; 

     case WM_SOCKET: /** We received a socket event **/ 
     { 
      switch(WSAGETSELECTEVENT(lParam)) 
      { 
       case FD_WRITE: /** We connected to the server successfully so we need to send an initialization packet. **/ 
       { 
        SocketConnected = true; 

        Packet P; 
        P.ID = PACKET_PROTOCOL_UPDATE_ID; 
        P.To = PACKET_PROTOCOL_SERVER_ID; 
        P.From = -1;  /** The server will send us a unique Identifier. **/ 
        P.Name = "ICantChooseUsernames"; 
        P.Message = "Hello"; 
        WritePacket(sock->GetSocket(), P); 
       } 
       break; 

       case FD_READ: /** We have received a packet from the server. Read the ID and interpret the packet information. **/ 
       { 
        Packet P; 
        ReadPacket(sock->GetSocket(), P); 

        if (P.ID == PACKET_PROTOCOL_SERVER_ID) //If the packet is a server packet, then it is sending us our Unique client ID. 
        { 
         ClientID = P.To; 
         SetWindowText(Hwnd, (ClientName + ": " + std::to_string(P.ID)).c_str()); //Set the window title to "OurName: " + OurID. 
        } 
        else if (P.ID == PACKET_PROTOCOL_CLIENT_DISC) 
        { 
         //Delete the specified contact from our contacts list.. 
        } 
        else if (P.ID == PACKET_PROTOCOL_CLIENT_CONN) 
        { 
         //Add the client to the contacts list.. 
        } 
        else //Else print the packet's message in the received box.. 
        { 
         std::string Sender = P.Name; 
         std::string Message = P.Message; 
         int ReceiveBoxLength = GetWindowTextLength(ReceiveBox); 
         Message = ReceiveBoxLength == 0 ? Sender + ": " + Message : "\r\n\r\n" + Sender + ": " + Message; 
         SendMessage(ReceiveBox, EM_SETSEL, -1, -1); 
         SendMessage(ReceiveBox, EM_REPLACESEL, 0, reinterpret_cast<LPARAM>(Message.c_str())); 
        } 
       } 
       break; 

       case FD_CLOSE: 
       { 
        sock->Close(); 
       } 
       break; 

       default: 
        break; 
      } 
      break; 
     } 

     case WM_DESTROY: 
     { 
      sock->Close(); 
      delete sock; 
      WSACleanup(); 
      PostQuitMessage(0); 
     } 
     return 0; 

     default: 
      return DefWindowProc(Hwnd, Msg, wParam, lParam); 
    } 

    return 0; 
} 

int main() 
{ 
    Window().Create("Client", "Client", 500, 500, WindowProcedure, {0}); 
} 
+0

我在這裏不理解:Server-> Accepts Client-> Read PacketID。連接成功後,我無法從OnButton2.Server-> Accepts Client workes讀取任何消息,否: - > Read PacketID。 – user2849744

+0

在連接建立之前,我如何能夠從客戶端接收數據和ID數據? – user2849744

+0

我編輯了我的答案。希望你明白這一點。我不確定我能否簡化它。我試圖通過答案添加更多評論最多隻限於30000個字符。 – Brandon