0

我决定制作一个应用程序,仅将列表中的人添加到聊天中。服务器和客户端有一个结构用户。我需要你的帮助,因为从客户端发送数据到服务器后,我在服务器端没有它,它不起作用......这不酷。

服务器:

    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

你可以做我猜的。首先创建一个用于套接字通信的类并确保它可以工作..

我有一些我不久前在下面写的工作代码。您可以在下面看到它是如何完成的。我还上传了服务器和客户端的源代码(使用 Codeblocks 和 gcc/g++ 4.8.1 编译它):http ://www.mediafire.com/download/6j84bedkp3s3sq5/套接字+聊天.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

套接字.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 函数创建了另一个包装器,以使创建窗口变得容易!

窗口.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

窗口.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;
    }

上述所有头文件和源文件都将在客户端和服务器中使用。协议是客户端和服务器之间的链接。它描述了数据包以及如何读/写它。这是服务器和客户端来回通信的方式!


对于我做的服务器:

启动 时:如上所示,服务器必须首先运行。它将在 localhost 上侦听,并将在端口 27015 上侦听。

在客户端连接上: 客户端连接后,客户端将获得一个唯一 ID 并添加到服务器上的列表中。您可以在 FD_ACCEPT 中看到这种情况。

On Message Received: 接下来,如果客户端发送消息,则触发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});
}

对于我做的客户:

On Start: 创建窗口时,它会尝试连接到服务器。

On Connect: 连接到服务器后,收到 FD_WRITE 消息。在 FD_WRITE 开关案例中,我们简单地构建一个基本数据包并将其发送到服务器,让他们知道我们的姓名、我们的信息等。服务器将接收一个 UPDATE 数据包并更新我们的信息,并通知所有其他客户端我们存在。

On Receive: 收到消息后,触发 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 回答