#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <string>
class Website
{
int status, sock;
struct addrinfo hints;
struct addrinfo *servinfo;
public:
int init(std::string url){
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if((status = getaddrinfo(url.c_str(), "80", &hints, &servinfo)) != 0){
return status;
}
if((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1){
return -1;
}
return 0;
}
int connectToSite(){
return connect(sock, servinfo->ai_addr, servinfo->ai_addrlen);
}
int sendToSite(std::string request){
return send(sock, request.c_str(), strlen(request.c_str()), 0);
}
int recvFromSite(char buf[], int maxsize){
return recv(sock, buf, maxsize, 0);
}
void closeSocket(){
close(sock);
freeaddrinfo(servinfo);
}
};我有一个网络类,它在一个简单的类中实现低级别的c++函数。我希望使我的代码更高效。
发布于 2020-10-08 00:05:02
我不喜欢这个设计。
您正在使用两阶段初始化。构造对象(因此是构造函数),然后调用init()函数。两阶段初始化是个坏主意,因为在使用对象之前,您不能判断(或必须验证)对象的状态。
更好的方法是简单地使用构造函数来初始化对象。如果构造函数失败,那么抛出使对象永远不存在,并且不能被错误地使用。
您也有一个显式关闭(closeSocket()),这在某种程度上是可以的,但是在使用对象时,您强迫人们记住调用这个函数。这就是毁灭者的目的。
你需要查一下RAII的概念。
{
Website site("Stuff"); // All resources allocated
}
// All resources for `site` are now released.connectToSite()的用途是什么,这不是init吗?你能在调用这个之前先打电话给读写吗?如果没有,那么读/写在错误的对象上,连接应该返回具有读/写接口的适当对象。
这就是我希望使用一个名为"WebSite“的对象的方式
{
// An abstract object that represents the site.
// No actual underlying connection as connections are
// usually to resources on the site not the actual site.
Website site("thorsanvil.com", 80);
// Create a connection to a specific resource on the site.
Connection c = site.open("Path");
// Object was created successfully so we have
// a valid connection to a server we can not communicate with.
c.writeData("This is a message");
std::string resp = c.read();
}从你的界面。我希望将您的类重命名为"Connection“,这样对于如何使用对象的期望可能更像。
{
// Create the socket and connect to the site.
Connection c("https://thorsanvil.com/MyResource");
// Object was created successfully so we have
// a valid connection to a server we can not communicate with.
c.writeData("This is a message");
std::string response = c.readData();
}您不想使用C++字符串吗?
#include <string.h>URL?
int init(std::string url)当然,但是这个班的名字是“网站”。url不仅仅是一个网站。有一个schema/host(hostname/port/user/password)/path/query/fragment.你不只是传递一个主机和连接,也许是一个端口。
或者你可以传递整个网址,但是每一个新的连接都需要一个新的url,而不仅仅是网站。
您将在这里返回状态代码(错误):
if((status = getaddrinfo(url.c_str(), "80", &hints, &servinfo)) != 0){
return status;
}但在这里,你要重新调整-1
if((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1){
return -1;
}确定这些错误范围不重叠吗?
我认为一个更好的界面是在出了问题时抛出一个异常。
对于一般的URL,这是行不通的。
status = getaddrinfo(url.c_str(), "80" ....示例:
https://thorsanvil.com:1222/Work
Schema: https
Site: thorsanvil.com
Port: 1222 overrides default 443 for https
Path: /Work
Query: Empty
Fragment: Empty实际上,上面的代码应该是:
status = getaddrinfo(getHost(url), getPort(url), ....connect()失败会发生什么?您期望用户检查错误代码吗?
int connectToSite(){
return connect(sock, servinfo->ai_addr, servinfo->ai_addrlen);
}这是非常类似于C的界面,并且非常容易使用错误。我们知道API的用户并不总是检查错误代码。别泄露这个秘密。在异常情况下使其显式化。
这不是您使用send()和recv()的方式,返回值表示消息发送/恢复了多少。您需要反复调用此函数,直到消息被发送或出现错误为止。
int sendToSite(std::string request){
return send(sock, request.c_str(), strlen(request.c_str()), 0);
}
int recvFromSite(char buf[], int maxsize){
return recv(sock, buf, maxsize, 0);
}看看这门课:
https://github.com/Loki-Astari/ThorsSocket/blob/master/src/ThorsSocket/Socket.h
https://codereview.stackexchange.com/questions/250330
复制相似问题