2013-10-05 2 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

당신은 당신의 특정 문제, 예상 입력과 출력을 지정해야합니다. _ '일하지 않아 ... 괜찮지 않아 .'_ 약간의 예비 정보가 있니?' –

+0

텍스트 버퍼의 데이터 유형을 표시하십시오. –

+0

IP, 로그인, 암호의 세 가지 필드가 있습니다. 입력란을 채운 후에는 단 한 번의 클릭으로 채팅에 클라이언트를 추가하려면 어떻게합니까? ScottMcP-MVP 구조 사용자에게 : {char ip [30]; char login [16]; char password [16]} – user2849744

답변

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 
당신은 그것을 아래 수행하는 방법을 볼 수 있습니다 .. 또한 서버와 클라이언트 모두에 대한 소스를 (Codeblocks 및 GCC/G ++ 4.8.1를 사용하여 컴파일) 업로드

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 기능을 둘러싼 또 다른 래퍼를 만들어 쉽게 Windows를 만들 수 있습니다!

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 클라이언트와 서버. 프로토콜은 클라이언트와 서버 간의 링크입니다. 그것은 패킷과 그것을 읽고 쓰는 방법을 설명했다. 그것은 서버와 클라이언트가 앞뒤로 통신하는 방법입니다! 시작에

: 위에서 볼 수 있듯이, 서버가 먼저 실행해야합니다 내가했던 서버를 들어


. 그것은 localhost를 수신하고 그것을 클라이언트 연결에 포트 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 스위치의 경우 기본 패킷을 구성하고 서버에 보내서 이름, 정보 등을 알립니다. 서버는 UPDATE 패킷을 받고 정보를 업데이트 할뿐만 아니라 다른 모든 클라이언트에게 있다.

수신 수신 : 메시지를 받으면 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 clients workes에서 메시지를 읽을 수 없습니다. -> Read PacketID. – user2849744

+0

연결을 설정하기 전에 클라이언트 데이터에서 수락 및 ID 데이터를 보내려면 어떻게해야합니까? – user2849744

+0

내 대답을 편집했습니다.바라기를 당신은 이해합니다. 나는 그것보다 더 단순화 할 수 있을지 잘 모르겠다. 나는 답을 더 많이 덧붙이려고했는데, 최대 30000 자로 제한되어있다. – Brandon

관련 문제