找工作小项目:day14-完善Connection类

day14-完善Connection类

在之前的编写中,服务器只能完成单一任务,今天我们将改写服务器为一个能够由用户自定义业务的通用网络库。
今天的任务主要在Connection中完成,并且今天我们要将项目整体展示出来(之前将业务工程化后,对于读者项目整体变得不清晰了)。

1、错误检测机制

//声明
void ErrorIf(bool condition, const char *msg);
//实现
void ErrorIf(bool condition, const char *errmsg) {
   
   
  if (condition) {
   
   
    perror(errmsg);
    exit(EXIT_FAILURE);
  }
}

2、Macros

定义了一些宏,用以控制类是否可移动复制等操作。
ASSERT(expr, message):如果expr的值为false,则打印信息message。
UNREACHABLE(message):表示某个程序路径是不可达的,即程序不应该执行到这个位置,如果达到了则抛出错误并打印信息message。

#define DISALLOW_COPY(cname)                             \
  cname(const cname &) = delete;            /* NOLINT */ \
  cname &operator=(const cname &) = delete; /* NOLINT */

#define DISALLOW_MOVE(cname)                        \
  cname(cname &&) = delete;            /* NOLINT */ \
  cname &operator=(cname &&) = delete; /* NOLINT */

#define DISALLOW_COPY_AND_MOVE(cname) \
  DISALLOW_COPY(cname);               \
  DISALLOW_MOVE(cname);

#define ASSERT(expr, message) assert((expr) && (message))

#define UNREACHABLE(message) throw std::logic_error(message)

3、Socket

在之前的工作中,我们将地址创建同Socket创建放到了一个文件中,通过声明查看发现本次多了一个Connect的重写以及IsNonBlocking方法。注意,这两个类都是禁止复制移动的,这样可以提高调用效率。

class InetAddress {
   
   
 public:
  InetAddress();
  InetAddress(const char *ip, uint16_t port);
  ~InetAddress() = default;

  DISALLOW_COPY_AND_MOVE(InetAddress);

  void SetAddr(sockaddr_in addr);
  sockaddr_in GetAddr();
  const char *GetIp();
  uint16_t GetPort();

 private:
  struct sockaddr_in addr_ {
   
   };
};

class Socket {
   
   
 private:
  int fd_{
   
   -1};

 public:
  Socket();
  explicit Socket(int fd);
  ~Socket();

  DISALLOW_COPY_AND_MOVE(Socket);

  void Bind(InetAddress *addr);
  void Listen();
  int Accept(InetAddress *addr);

  void Connect(InetAddress *addr);
  void Connect(const char *ip, uint16_t port);

  void SetNonBlocking();
  bool IsNonBlocking();
  int GetFd();
};

之后我们通过实现的代码来理解发生了哪些变化以及多了那些能力。首先在实现中看到的是IsNonBlocking方法,这个方法是用来检查Socket是否是非阻塞模式,是的话返回true;接下来是void Connect(const char *ip, uint16_t port)方法,从实现来看该方法将创建地址同创建Socket做了结合。

Socket::Socket() {
   
   
  fd_ = socket(AF_INET, SOCK_STREAM, 0);
  ErrorIf(fd_ == -1, "socket create error");
}
Socket::Socket(int fd) : fd_(fd) {
   
    ErrorIf(fd_ == -1, "socket create error"); }

Socket::~Socket() {
   
   
  if (fd_ != -1) {
   
   
    close(fd_);
    fd_ = -1;
  }
}

void Socket::Bind(InetAddress *addr) {
   
   
  struct sockaddr_in tmp_addr = addr->GetAddr();
  ErrorIf(bind(fd_, (sockaddr *)&tmp_addr, sizeof(tmp_addr)) == -1, "socket bind error");
}

void Socket::Listen() {
   
    ErrorIf(::listen(fd_, SOMAXCONN) == -1, "socket listen error"); }
void Socket::SetNonBlocking() {
   
    fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL) | O_NONBLOCK); }
bool Socket::IsNonBlocking() {
   
    return (fcntl(fd_, F_GETFL) & O_NONBLOCK) != 0; }
int Socket::Accept(InetAddress *addr) {
   
   
  // for server socket
  int clnt_sockfd = -1;
  struct sockaddr_in tmp_addr {
   
   };
  socklen_t addr_len = sizeof(tmp_addr);
  if (IsNonBlocking()) {
   
   
    while (true) {
   
   
      clnt_sockfd = accept(fd_, (sockaddr *)&tmp_addr, &addr_len);
      if (clnt_sockfd == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
   
   
        // printf("no connection yet\n");
        continue;
      }
      if (clnt_sockfd == -1) {
   
   
        ErrorIf(true, "socket accept error");
      } else {
   
   
        break;
      }
    }
  } else {
   
   
    clnt_sockfd = accept(fd_, (sockaddr *)&tmp_addr, &addr_len);
    ErrorIf(clnt_sockfd == -1, "socket accept error");
  }
  addr->SetAddr(tmp_addr);
  return clnt_sockfd;
}

void Socket::Connect(InetAddress *addr) {
   
   
  // for client socket
  struct sockaddr_in tmp_addr = addr->GetAddr();
  if (fcntl(fd_, F_GETFL) & O_NONBLOCK) {
   
   
    while (true) {
   
   
      int ret = connect(fd_, (sockaddr *)&tmp_addr, sizeof(tmp_addr));
      if (ret == 0) {
   
   
        break;
      }
      if (ret == -1 && (errno == EINPROGRESS)) {
   
   
        continue;
      }
      if (ret == -1) {
   
   
        ErrorIf(true, "socket connect error");
      }
    }
  } else {
   
   
    ErrorIf(connect(fd_, (sockaddr *)&tmp_addr, sizeof(tmp_addr)) == -1, "socket connect error");
  }
}

void Socket::Connect(const char *ip, uint16_t port) {
   
   
  InetAddress *addr = new InetAddress(ip, port);
  Connect(addr);
  delete addr;
}

int Socket::GetFd() {
   
    return fd_; }

InetAddress::InetAddress() = default;
InetAddress::InetAddress(const char *ip, uint16_t port) {
   
   
  memset(&addr_, 0, sizeof(addr_));
  addr_.sin_family = AF_INET;
  addr_.sin_addr.s_addr = inet_addr(ip);
  addr_.sin_port = htons(port);
}

void InetAddress::SetAddr(sockaddr_in addr) {
   
    addr_ = addr; }

sockaddr_in InetAddress::GetAddr() {
   
    return addr_; }

const char *InetAddress::GetIp() {
   
    return inet_ntoa(addr_.sin_addr); }

uint16_t InetAddress::
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值