首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >c++ linux网络库

c++ linux网络库
EN

Code Review用户
提问于 2020-10-07 16:37:01
回答 1查看 107关注 0票数 1
代码语言:javascript
复制
#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++函数。我希望使我的代码更高效。

EN

回答 1

Code Review用户

回答已采纳

发布于 2020-10-08 00:05:02

观察:

我不喜欢这个设计。

您正在使用两阶段初始化。构造对象(因此是构造函数),然后调用init()函数。两阶段初始化是个坏主意,因为在使用对象之前,您不能判断(或必须验证)对象的状态。

更好的方法是简单地使用构造函数来初始化对象。如果构造函数失败,那么抛出使对象永远不存在,并且不能被错误地使用。

您也有一个显式关闭(closeSocket()),这在某种程度上是可以的,但是在使用对象时,您强迫人们记住调用这个函数。这就是毁灭者的目的。

你需要查一下RAII的概念。

代码语言:javascript
复制
{
    Website    site("Stuff"); // All resources allocated
}
// All resources for `site` are now released.

设计

connectToSite()的用途是什么,这不是init吗?你能在调用这个之前先打电话给读写吗?如果没有,那么读/写在错误的对象上,连接应该返回具有读/写接口的适当对象。

这就是我希望使用一个名为"WebSite“的对象的方式

代码语言:javascript
复制
{
    // 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“,这样对于如何使用对象的期望可能更像。

代码语言:javascript
复制
 {
    // 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++字符串吗?

代码语言:javascript
复制
#include <string.h>

URL?

代码语言:javascript
复制
  int init(std::string url)

当然,但是这个班的名字是“网站”。url不仅仅是一个网站。有一个schema/host(hostname/port/user/password)/path/query/fragment.你不只是传递一个主机和连接,也许是一个端口。

或者你可以传递整个网址,但是每一个新的连接都需要一个新的url,而不仅仅是网站。

您将在这里返回状态代码(错误):

代码语言:javascript
复制
  if((status = getaddrinfo(url.c_str(), "80", &hints, &servinfo)) != 0){
          return status;
      }

但在这里,你要重新调整-1

代码语言:javascript
复制
      if((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1){
          return -1;
      }

确定这些错误范围不重叠吗?

我认为一个更好的界面是在出了问题时抛出一个异常。

对于一般的URL,这是行不通的。

代码语言:javascript
复制
  status = getaddrinfo(url.c_str(), "80" ....
  1. 这个港口并不总是80个。
  2. 默认端口由架构"http/https .“定义。
  3. 默认设置可以由url本身重写。

示例:

代码语言:javascript
复制
  https://thorsanvil.com:1222/Work

  Schema:   https
  Site:     thorsanvil.com
  Port:     1222             overrides default 443 for https
  Path:     /Work
  Query:    Empty
  Fragment: Empty

实际上,上面的代码应该是:

代码语言:javascript
复制
  status = getaddrinfo(getHost(url), getPort(url), ....

connect()失败会发生什么?您期望用户检查错误代码吗?

代码语言:javascript
复制
 int connectToSite(){
    return connect(sock, servinfo->ai_addr, servinfo->ai_addrlen);
 }

这是非常类似于C的界面,并且非常容易使用错误。我们知道API的用户并不总是检查错误代码。别泄露这个秘密。在异常情况下使其显式化。

这不是您使用send()recv()的方式,返回值表示消息发送/恢复了多少。您需要反复调用此函数,直到消息被发送或出现错误为止。

代码语言:javascript
复制
 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);
 }

Research

看看这门课:

https://github.com/Loki-Astari/ThorsSocket/blob/master/src/ThorsSocket/Socket.h

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/250330

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档