0

リストにある人だけをチャットに追加するアプリケーションを作成することにしました。サーバー側とクライアント側には構造 User.I があります。クライアントからサーバーにデータを送信した後、サーバー側にデータがなく、機能しないため、あなたの助けが必要です...それはクールではありません。

サーバ:

    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);
}

サーバーに採用されているデータ構造を作成するにはどうすればよいですか? これで接続が確立され、データ ログインとパスワードが失われます。

4

1 に答える 1

3

あなたは私が推測したことをすることができます。最初にソケット通信用のクラスを作成し、それが機能することを確認します..

少し前に以下に書いた実用的なコードがいくつかあります。以下でそれがどのように行われるかを見ることができます.. サーバーとクライアントの両方のソースもアップロードしました (コードブロックと gcc/g++ 4.8.1 を使用してコンパイルします): http://www.mediafire.com/download/6j84bedkp3s3sq5/ Socket+Chat.zip

ソケット.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);
            }
        }
    }
}

プロトコル.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;
    }

上記のヘッダーとソース ファイルはすべて、クライアントとサーバーの両方で使用されます。プロトコルは、クライアントとサーバー間のリンクです。パケットとその読み取り/書き込み方法について説明しました。これは、サーバーとクライアントがやり取りする方法です!


私がしたサーバーの場合:

開始時: 上記のように、サーバーが最初に実行されている必要があります。ローカルホストでリッスンし、ポート 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});
}
于 2013-10-05T19:49:23.530 に答える