C++ winsock irc 客户端问题
c++ winsock irc client questions
我正在尝试使用 C++ 中的 Winsock 创建一个 IRC 聊天机器人。我是编程的新手,也是套接字编程的新手。
我正在尝试连接到我的 Twitch 频道。我可以成功建立连接,并传递几个缓冲区(即我的 bot 的密码或 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;
}
当我 运行 这样做时,我所看到的只是我的变量被传递,然后是无限量的零。
首先,您忽略了 recv
的 return 值,因此您不知道收到了多少字节。
其次,您实际上没有在任何地方实现 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' 字符。
你的代码有很多问题。
您没有检查 socket()
的 return 值是否有错误(我假设您 正在 调用 WSAStartup()
事先,对吧?)。
您没有在 PASS
和 NICK
命令末尾发送任何换行符。 IRC 是一种基于线路的协议。这就是为什么您没有从服务器获取任何数据的原因 - 它正在等待您先完成命令。
IRC中各种保留字符必须转义
您发送了两次 PASS
命令,因为您在设置 [=14= 时使用 +
运算符而不是 =
运算符]命令。
您没有发送任何 USER
和 JOIN
命令。
您不应该使用 strlen()
来计算 std::string
的长度。为此,它有自己的 length()
和 size()
方法。
您忽略了 send()
和 recv()
的 return 值。 TCP 是字节流,但您没有考虑到 send()
和 recv()
可以 return 比请求的字节少。您需要循环调用它们,直到您拥有 sent/receive 您期望的所有字节。
尝试更像这样的东西:
#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, "[=10=]", "\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] = '[=10=]';
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;
}
我正在尝试使用 C++ 中的 Winsock 创建一个 IRC 聊天机器人。我是编程的新手,也是套接字编程的新手。
我正在尝试连接到我的 Twitch 频道。我可以成功建立连接,并传递几个缓冲区(即我的 bot 的密码或 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;
}
当我 运行 这样做时,我所看到的只是我的变量被传递,然后是无限量的零。
首先,您忽略了 recv
的 return 值,因此您不知道收到了多少字节。
其次,您实际上没有在任何地方实现 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' 字符。
你的代码有很多问题。
您没有检查
socket()
的 return 值是否有错误(我假设您 正在 调用WSAStartup()
事先,对吧?)。您没有在
PASS
和NICK
命令末尾发送任何换行符。 IRC 是一种基于线路的协议。这就是为什么您没有从服务器获取任何数据的原因 - 它正在等待您先完成命令。IRC中各种保留字符必须转义
您发送了两次
PASS
命令,因为您在设置 [=14= 时使用+
运算符而不是=
运算符]命令。您没有发送任何
USER
和JOIN
命令。您不应该使用
strlen()
来计算std::string
的长度。为此,它有自己的length()
和size()
方法。您忽略了
send()
和recv()
的 return 值。 TCP 是字节流,但您没有考虑到send()
和recv()
可以 return 比请求的字节少。您需要循环调用它们,直到您拥有 sent/receive 您期望的所有字节。
尝试更像这样的东西:
#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, "[=10=]", "\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] = '[=10=]';
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;
}