c ++ winsock irc客户问题

时间:2017-04-04 20:15:27

标签: c++ irc winsock2

我正在尝试使用C ++中的Winsock创建一个IRC聊天机器人。我是编程新手,并且不熟悉使用套接字编程。

我正在尝试连接到我的Twitch频道。我可以成功建立连接,并传递几个缓冲区(即我的机器人的密码或oauth令牌,用户名,以及我想加入的通道)。

但是,当我拨打recv()时,没有从Twitch服务器发送数据。

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define DEFAULT_BUFLEN 512

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <cstdlib>
#include <iostream>

#pragma comment(lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


using namespace std;

int main()
{
    string Buffer;
    char  buffers[1024 * 8] = { "0" };
    string oauth = "oauthtoken";
    string nick = "text_adventure_bot";
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;
    int iResult;
    string hostname = "irc.chat.twitch.tv";
    struct addrinfo *result = NULL,
        *ptr = NULL,
        hints;


    WSABUF DataBuf;
    WSADATA  wsadata;
    WORD wVersionRequested;

    WORD DllVersion = MAKEWORD(2, 1);
    iResult = WSAStartup(MAKEWORD(2, 2), &wsadata);
    if(iResult != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    SOCKADDR_IN addr; //the ip
    int sizeofaddr = sizeof(addr);
    addr.sin_addr.s_addr = inet_addr("52.25.27.117");
    addr.sin_port = htons(6667);
    addr.sin_family = AF_INET;

    SOCKET sock = socket(AF_INET, SOCK_STREAM, NULL);

    if (connect(sock, (SOCKADDR*)&addr, sizeofaddr) != 0)
    {
        cout << "Connection error" << endl;
    }

    cout << "connected" << endl;

    Buffer = "PASS " + oauth;

    send(sock, Buffer.c_str(), (int)strlen(Buffer.c_str()), 0); 
    recv(sock, buffers, 1024 * 8, 0);
    cout << Buffer.c_str() << endl << buffers << endl << endl;

    Buffer + "NICK " + nick;

    send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
    recv(sock, buffers, 1024 * 8, 0);
    cout << Buffer.c_str() << endl << buffers << endl << endl;

    while (true) {
        recv(sock, buffers, 1024 * 8, 0);
        cout << buffers << endl << endl;
        if (buffers[0] == 'PING') {
            Buffer = "PONG :" + hostname + "\r\n";
            send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
            cout << Buffer.c_str() << endl << buffers << endl << endl;
        }
    }
    return 0;
}

当我运行它时,我看到的只是传递的变量,然后是无限量的零。

2 个答案:

答案 0 :(得分:3)

您的代码存在许多问题。

  1. 您没有检查socket()的错误返回值(我假设您 之前正在调用WSAStartup(),对吧?)。

    < / LI>
  2. 您未在PASSNICK命令的末尾发送任何换行符。 IRC是一种基于行的协议。这就是为什么你没有从服务器获取任何数据 - 它等着你先完成你的命令。

  3. 必须转发IRC中的各种保留字符。

  4. 您正在发送PASS命令两次,因为在设置+命令时使用=运算符而不是NICK运算符。< / p>

  5. 您没有发送任何USERJOIN命令。

  6. 您不应该使用strlen()来计算std::string的长度。它有自己的length()size()方法用于此目的。

  7. 您忽略了send()recv()的返回值。 TCP是一个字节流,但您没有考虑send()recv()返回的字节数少于请求的字节数。您需要在循环中调用它们,直到您发送/接收所需的所有字节为止。

  8. 尝试更像这样的东西:

    #include <windows.h>
    #include <winsock.h>
    
    #include <iostream>
    #include <string>
    #include <algorithm>
    
    void replaceStr(std::string &str, const std::string &oldStr, const std::string &newStr)
    {
        std::string::size_type index = 0;
        do
        {
            index = str.find(oldStr, index);
            if (index == std::string::npos)
                return;
    
            str.replace(index, oldStr.length(), newStr);
            index += newStr.length();
        }
        while (true);
    }
    
    std::string quote(const std::string &s)
    {
        std::string result = s;
        replaceStr(result, "\x10", "\x10""\x10");
        replaceStr(result, "\0", "\x10""0");
        replaceStr(result, "\n", "\x10""n");
        replaceStr(result, "\r", "\x10""r");
        return result;
    }
    
    std::string unquote(const std::string &s)
    {
        std::string result = s;
        std::string::size_type len = result.length();
        std::string::size_type index = 0;
        while (index < len)
        {
            index = result.find("\x10", index);
            if (index = std::string::npos)
                break;
    
            result.erase(index, 1);
            --len;
    
            if (index >= len)
                break;
    
            switch (result[index])
            {
                case '0':
                    result[index] = '\0';
                    break;
                case 'n':
                    result[index] := '\n';
                    break;
                case 'r':
                    result[index] = '\r';
                    break;
            }
    
            ++index;
        }
    
        return result;
    }
    
    std::string fetch(std::string &s, const std::string &delim)
    {
        std::string result;
        std::string::size_type pos = s.find(delim);
        if (pos == std::string::npos)
        {
            result = s;
            s = "";
        }
        else
        {
            result = s.substr(0, pos);
            s.erase(0, pos+delim.length());
        }
        return result;
    }
    
    bool sendStr(SOCKET sock, const std::string &s)
    {
        const char *ptr = s.c_str();
        int len = s.length();
    
        while (len > 0)
        {
            int ret = send(sock, ptr, len, 0);
            if (ret == SOCKET_ERROR)
            {
                std::cout << "send() error: " << WSAGetLastError() << std::endl;
                return false;
            }
            ptr += ret;
            len -= ret;
        }
    
        return true;
    }
    
    bool sendCmd(SOCKET sock, const std::string &cmd)
    {
        std::cout << "Sending: " << cmd << std::endl;
        return sendStr(sock, quote(cmd)) && sendStr(sock, "\r\n");
    }
    
    int main()
    {
        int exitCode = -1;
    
        WSADATA wsa;
        int ret = WSAStartup(MAKEWORD(2, 0), &wsa);
        if (ret != 0)
        {
            std::cout << "Winsock init error: " << ret << std::endl;
            goto done;
        }
    
        SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (sock == INVALID_SOCKET)
        {
            std::cout << "socket() error: " << WSAGetLastError() << std::endl;
            goto done;
        }
    
        SOCKADDR_IN addr = {0};
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr("52.25.27.117"); //the ip
        addr.sin_port = htons(6667);
    
        if (connect(sock, (SOCKADDR*)&addr, sizeof(addr)) != 0)
        {
            std::cout << "connect() error: " << WSAGetLastError() << std::endl;
            goto cleanup:
        }
    
        std::cout << "connected" << std::endl;
    
        std::string oauth = ...;
        std::string nick = ...;
        std::string user = ...;
        std::string channel = ...;
    
        sendCmd("PASS " + oauth);
        sendCmd("NICK " + nick);
        sendCmd("USER " + user);
        sendCmd("JOIN " + channel);
    
        char buf[1024];
        std::string LineBuffer;
        std::string::size_type StartIdx = 0;
    
        do
        {
            int ret = recv(sock, buf, sizeof(buf), 0);
            if (ret == SOCKET_ERROR)
            {
                std::cout << "recv() error: " << WSAGetLastError() << std::endl;
                goto cleanup;
            }
    
            if (ret == 0)
            {
                std::cout << "Server disconnected" << std::endl;
                break;
            }
    
            LineBuffer.append(buf, ret);
    
            do
            {
                std::string::size_type pos = LineBuffer.find('\n', StartIdx);
                if (pos == std::string::npos)
                    break;
    
                std::string::size_type len = pos;
                if ((pos > 0) && (LineBuffer[pos-1] == '\r'))
                    --len;
    
                std::string msg = unquote(LineBuffer.substr(0, len));
                LineBuffer.erase(0, pos+1);
                StartIdx = 0;
    
                std::string senderNick;
                std::string senderHost;
    
                if (!msg.empty() && (msg[0] == ':'))
                {
                    std::string tmp = fetch(msg, " ");
                    tmp.erase(0, 1); // remove ':'
                    senderNick = fetch(tmp, "!");
                    senderHost = tmp;
                }
    
                std::cout << "Received: " << msg << std::endl;
    
                if (msg == "PING")
                    sendCmd("PONG :" + hostname);
            }
            while(true);
        }
        while (true);
    
        exitCode = 0;
    
    cleanup:
        closesocket(sock);
    
    done:
        return exitCode;
    }
    

答案 1 :(得分:0)

首先,您忽略recv的返回值,因此您不知道收到了多少字节。

其次,您实际上没有实现IRC消息协议(请参阅RFC1459的2.3节)。因此,您没有理由假设缓冲区的第一个字节将包含IRC协议消息的第一个字节。只有IRC消息协议的实际实现才能产生一个缓冲区,其第一个字节是IRC消息的第一个字节。

同样,你不能这样做:

    cout << buffers << endl << endl;

流的operator<<(const char *)需要指向C风格字符串的指针。在解析从TCP连接收到的数据并从中生成C风格的字符串之前,不得将其视为C风格的字符串。

此外:

    if (buffers[0] == 'PING') {

你真的认为PING是一个多字节字符常量吗?据我所知,没有名为PING的多字节字符。并且,无论如何,IRC服务器发送文字四字符串“PING”,而不是单个“PING”字符。