gpt4 book ai didi

c++ - 如何实现 recv() 回调

转载 作者:搜寻专家 更新时间:2023-10-31 00:36:04 26 4
gpt4 key购买 nike

我正在努力提高我对 OOP 的了解,并决定创建一个简单的类来简化套接字编程。这是一个学习实验,所以我不想使用 boost 或其他库。

我想实现一个事件驱动 recv()。意思是,每次有新数据进来时,它都应该调用我的函数。

我想我需要创建一个线程来运行 recv() 循环,然后在每次有新数据时调用我的函数。还有其他方法可以使用线程吗?我希望我的代码是可移植的。

这是我的简单类和示例代码:

class.h:

#ifndef _SOCKETSCLASS_H
#define _SOCKETSCLASS_H

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#define W32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SOCKET int
#endif
#include <string>
#include<ctime>
#include <stdio.h>
#include <stdarg.h>
#include <varargs.h>
#include <tchar.h>

using namespace std;

#ifdef _DEBUG
#define DEBUG(msg) XTrace(msg)
#else
#define DEBUG(msg, params)
#endif

struct TCP_Client_opts
{
BOOL UseSCprotocol;
BOOL UseEncryption;
BOOL UseCompression;
int CompressionLevel;
void *Callback;
BOOL async;
};

struct TCP_Stats
{
unsigned long int upload; //bytes
unsigned long int download;//bytes
time_t uptime; //seconds
};

class TCP_Client
{
public:
TCP_Client();
TCP_Client(TCP_Client_opts opts_set);
~TCP_Client();
SOCKET GetSocket();
void SetOptions(TCP_Client_opts opts_set);
TCP_Client_opts GetOptions();
BOOL Connect(string server, int port);
int Send(string data);
int Recv(string *data);
BOOL IsConnected();
int Disconnect();
TCP_Stats GetStats();
private:
SOCKET s = SOCKET_ERROR;
TCP_Client_opts opts;
TCP_Stats stats;
BOOL connected = FALSE;
time_t starttime;
};
#endif

class.cpp:

#include "SocketsClass.h"

void XTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[512]; // get rid of this hard-coded buffer
nBuf = _vsnwprintf_s(szBuffer, 511, lpszFormat, args);
::OutputDebugString(szBuffer);
va_end(args);
}


TCP_Client::TCP_Client(TCP_Client_opts opts_set)
{
SetOptions(opts_set);
}

TCP_Client::~TCP_Client()
{
Disconnect();
}

TCP_Client::TCP_Client()
{
}

void TCP_Client::SetOptions(TCP_Client_opts opts_set)
{
opts = opts_set;
}

TCP_Client_opts TCP_Client::GetOptions()
{
return opts;
}

SOCKET TCP_Client::GetSocket()
{
return s;
}

BOOL TCP_Client::IsConnected()
{
return connected;
}

int TCP_Client::Disconnect()
{
connected = FALSE;
stats.uptime = time(0) - starttime;
return shutdown(s, 2);
}

BOOL TCP_Client::Connect(string server, int port)
{
struct sockaddr_in RemoteHost;

#ifdef W32
WSADATA wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
DEBUG(L"Failed to load Winsock!\n");
return FALSE;
}
#endif

//create socket if it is not already created
if (s == SOCKET_ERROR)
{
//Create socket
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == SOCKET_ERROR)
{
DEBUG(L"Could not create socket");
return FALSE;
}
}

//setup address structure
if (inet_addr(server.c_str()) == INADDR_NONE)
{
struct hostent *he;

//resolve the hostname, its not an ip address
if ((he = gethostbyname(server.c_str())) == NULL)
{
//gethostbyname failed
DEBUG(L"gethostbyname() - Failed to resolve hostname\n");
return FALSE;
}
}
else//plain ip address
{
RemoteHost.sin_addr.s_addr = inet_addr(server.c_str());
}

RemoteHost.sin_family = AF_INET;
RemoteHost.sin_port = htons(port);

//Connect to remote server
if (connect(s, (struct sockaddr *)&RemoteHost, sizeof(RemoteHost)) < 0)
{
DEBUG(L"connect() failed");
return FALSE;
}

connected = TRUE;
starttime = time(0);
stats.download = 0;
stats.upload = 0;
return TRUE;
}

TCP_Stats TCP_Client::GetStats()
{
if (connected==TRUE)
stats.uptime = time(0)-starttime;
return stats;
}

int TCP_Client::Send(string data)
{
stats.upload += data.length();
return send(s, data.c_str(), data.length(), 0);

}

int TCP_Client::Recv(string *data)
{
int ret = 0;
char buffer[512];

ret = recv(s, buffer, sizeof(buffer), 0);
data->assign(buffer);
data->resize(ret);
stats.download += data->length();

return ret;
}

ma​​in.cpp:

#include <stdio.h>
#include <string.h>
#include "SocketsClass.h"

using namespace std;


int main(int argc, char *argv)
{
TCP_Client tc;
tc.Connect("127.0.0.1", 9999);
tc.Send("HEllo");
string data;
tc.Recv(&data);
puts(data.c_str());
tc.Disconnect();
printf("\n\nDL: %i\nUP: %i\nUptime: %u\n", tc.GetStats().download, tc.GetStats().upload, tc.GetStats().uptime);
return 0;
}

一些额外的问题:

  1. 假设我正在发送一个文件。我的功能如何知道当前数据与上一条消息相关?
  2. 我的类(class)设计和实现情况如何?我应该改变什么吗?

谢谢

最佳答案

如果您所说的“便携”是指在 Windows 以外的其他平台上运行,那么工作线程中的 recv() 循环是您唯一的便携选项。特别是在 Windows 上,您还有一些其他选择:

  1. 分配一个隐藏窗口,然后使用WSAAsyncSelect() 接收FD_READ 通知。这需要一个消息循环,您可以将其放入工作线程。

  2. 使用 WSAEventSelect()FD_READ 通知注册一个等待事件,然后通过 WSAWaitForMultipleEvents() 等待这些事件一个线程。

  3. 使用带有 I/O 完成端口的 WSARecv()。在线程中通过 GetQueuedCompletionResult() 轮询 IOCP。

至于你关于消息的问题,TCP是一个字节流,它没有消息的概念。您必须自己构建消息。您可以:

  1. 为每条消息提供一个包含消息长度的固定 header 。首先读取 header ,然后读取它说的任意字节数,然后读取下一个 header ,依此类推。

  2. 使用消息数据中未出现的唯一分隔符分隔每条消息。阅读直到遇到该分隔符,然后阅读直到遇到下一个分隔符,依此类推。

关于c++ - 如何实现 recv() 回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22639775/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com