简写网络库(2)--封装socket类

一、socket 类(socket.hpp)

1. 类设计思想

这是一个 RAII(资源获取即初始化)风格的 socket 封装类,管理 socket 文件描述符的生命周期。

2. 核心特性

构造与析构
复制代码
socket(int c=-1):m_sockfd(c){}  // 构造时传入文件描述符,默认-1表示无效
~socket(){
    if(m_sockfd!=-1)
        close(m_sockfd);  // 析构时自动关闭socket
}
禁用拷贝(重要设计)
复制代码
socket& operator=(const socket& other)=delete;
socket(const socket& other)=delete;
  • 禁用拷贝构造和拷贝赋值,因为 socket 文件描述符不能被两个对象共享

  • 防止资源重复释放问题

支持移动语义
复制代码
socket(socket&& other):m_sockfd(other.m_sockfd){
    other.m_sockfd=-1;  // 转移所有权后置为无效
}
  • 允许资源所有权的转移

  • 支持在容器中移动(如 vector、map)

常用方法
复制代码
int get_fd()const{ return m_sockfd; }      // 获取底层文件描述符
bool is_valid()const{ return m_sockfd!=-1; } // 检查是否有效
void close_socket(){ ... }                 // 手动关闭

二、socket 工具函数(socket_utils.hpp)

1. 创建 socket

复制代码
int create_sockfd() {
    return socket(AF_INET, SOCK_STREAM, 0);  // 创建TCP socket
}

int create_nonblocking_sockfd() {
    // 一次性设置非阻塞和close-on-exec标志
    return socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
}
  • SOCK_NONBLOCK: 非阻塞模式

  • SOCK_CLOEXEC: exec时自动关闭,防止文件描述符泄漏

2. socket 选项设置

非阻塞模式
复制代码
void set_socket_nonblocking(int sockfd, bool nonblock) {
    int flags = fcntl(sockfd, F_GETFL, 0);
    if(nonblock)
        flags |= O_NONBLOCK;
    else
        flags &= ~O_NONBLOCK;
    fcntl(sockfd, F_SETFL, flags);
}
地址重用(重要)
复制代码
void set_socket_reuseaddr(int sockfd, bool reuse) {
    int optval = reuse ? 1 : 0;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
}
  • SO_REUSEADDR: 允许立即重用处于 TIME_WAIT 状态的端口

  • 避免重启服务器时"Address already in use"错误

Linger 选项
复制代码
void set_socket_linger(int sockfd, bool onoff, int linger_time) {
    struct linger so_linger;
    so_linger.l_onoff = onoff ? 1 : 0;
    so_linger.l_linger = linger_time;  // 延迟关闭时间(秒)
    setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
}
  • 控制关闭 socket 时的行为

  • l_onoff=1: 启用延迟关闭,等待数据发送完成或超时

端口重用
复制代码
void set_socket_reuseport(int sockfd, bool reuse) {
    int optval = reuse ? 1 : 0;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
}
  • Linux 3.9+ 特性

  • 允许多个进程绑定相同端口,用于负载均衡

3. 地址操作

复制代码
// 获取本地地址信息
struct sockaddr_in& get_local_addr(int sockfd) {
    getsockname(sockfd, (struct sockaddr*)&local_addr, &addrlen);
    return local_addr;
}

// 获取对端地址信息
struct sockaddr_in& get_peer_addr(int sockfd) {
    getpeername(sockfd, (struct sockaddr*)&peer_addr, &addrlen);
    return peer_addr;
}

4. 错误处理

复制代码
std::string get_socket_error(int sockfd) {
    int optval;
    getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen);
    return std::string(strerror(optval));  // 将错误码转为字符串
}

三、使用示例

复制代码
// 创建 socket
socket s(wangt_socket_untils::create_nonblocking_sockfd());

// 设置选项
wangt_socket_untils::set_socket_reuseaddr(s.get_fd(), true);
wangt_socket_untils::set_socket_linger(s.get_fd(), true, 5);

// 绑定地址
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

wangt_socket_untils::bind_socket(s.get_fd(), 
    (struct sockaddr*)&addr, sizeof(addr));

// 监听
wangt_socket_untils::listen_socket(s.get_fd(), 128);

// 移动语义使用
socket s2 = std::move(s);  // s 转移给 s2,s 变为无效

整体代码

socket核心函数

复制代码
#ifndef SOCKET_HPP
#define SOCKET_HPP

#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<fcntl.h>
#include<string>
class socket{
    int m_sockfd;
    public:
    socket(int c=-1):m_sockfd(c){}
    ~socket(){
        if(m_sockfd!=-1)
            close(m_sockfd);
    }
    socket& operator=(const socket& other)=delete;
    socket(const socket& other)=delete;
    socket(socket&& other):m_sockfd(other.m_sockfd){
        other.m_sockfd=-1;
    }
    socket& operator=(socket&& other){
        if(this!=&other){
            close(m_sockfd);
            m_sockfd=other.m_sockfd;
            other.m_sockfd=-1;
        }
        return *this;
    }
    int get_fd()const{
        return m_sockfd;
    }
    bool is_valid()const{
        return m_sockfd!=-1;
    }
   void close_socket(){
        if(m_sockfd!=-1){
            close(m_sockfd);
            m_sockfd=-1;
        }
    }
};
#endif 

socket工具函数

复制代码
#include "socket_utils.hpp"
#include <cstring>
namespace wangt_socket_untils{
int create_sockfd()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    return sockfd;
}
int create_nonblocking_sockfd()
{
    int sockfd=socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC,0);
    return sockfd;
}
void set_socket_nonblocking(int sockfd,bool nonblock)
{
    int flags=fcntl(sockfd,F_GETFL,0);
    if(nonblock)
        flags|=O_NONBLOCK;
    else
        flags&=~O_NONBLOCK;
    fcntl(sockfd,F_SETFL,flags);
}
void set_socket_reuseaddr(int sockfd,bool reuse)
{
    int optval=reuse?1:0;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval));
}
void set_socket_linger(int sockfd,bool onoff,int linger_time)
{
    struct linger so_linger;
    so_linger.l_onoff=onoff?1:0;
    so_linger.l_linger=linger_time;
    setsockopt(sockfd,SOL_SOCKET,SO_LINGER,&so_linger,sizeof(so_linger));
}
void set_socket_reuseport(int sockfd,bool reuse)
{
    int optval=reuse?1:0;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
}
void bind_socket(int sockfd,const struct sockaddr* addr,socklen_t addrlen)
{
    bind(sockfd,addr,addrlen);
}
void listen_socket(int sockfd,int backlog)
{
    listen(sockfd,backlog);
}
void close_socket(int sockfd)
{
    close(sockfd);
}
struct sockaddr_in& get_local_addr(int sockfd)
{
    static struct sockaddr_in local_addr;
    socklen_t addrlen=sizeof(local_addr);
    getsockname(sockfd,(struct sockaddr*)&local_addr,&addrlen);
    return local_addr;
}
struct sockaddr_in& get_peer_addr(int sockfd)
{
    static struct sockaddr_in peer_addr;
    socklen_t addrlen=sizeof(peer_addr);
    getpeername(sockfd,(struct sockaddr*)&peer_addr,&addrlen);
    return peer_addr;
}
std::string get_socket_error(int sockfd)
{
    int optval;
    socklen_t optlen=sizeof(optval);
    if(getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&optval,&optlen)<0)
    {
        return std::string("getsockopt error");
    }
    if(optval!=0)
    {
        return std::string(strerror(optval));
    }
    return std::string();
}
}
相关推荐
小辰记事本8 小时前
从零读懂RoCEv2数据包构造:从WQE到线缆上的完整旅程
服务器·网络·网络协议·rdma
小鹏linux9 小时前
Ubuntu 22.04 部署开源免费具有精美现代web页面的Casdoor账号管理系统
linux·前端·ubuntu·开源·堡垒机
北京耐用通信10 小时前
全域适配工业场景耐达讯自动化Modbus TCP 转 PROFIBUS 网关轻松实现以太网与现场总线互通
网络·人工智能·网络协议·自动化·信息与通信
在角落发呆10 小时前
Linux转发配置:解锁网络互联的核心密码
linux·运维·网络
齐潇宇10 小时前
Zabbix 7 概述与配置
linux·zabbix·监控告警
江公望12 小时前
Ubuntu htop命令,10分钟讲清楚
linux·服务器
哎呦,帅小伙哦12 小时前
Linux 时间:从原子钟到 clock_gettime 的每一面
linux·运维·服务器
张小姐的猫12 小时前
【Linux】多线程 —— 线程互斥
linux·运维·服务器·c++
YMWM_13 小时前
UDP协议详解:从原理到Python实践
网络·网络协议·udp
pengyi87101513 小时前
共享 IP 与独享 IP 怎么选?被封后升级方案避坑
网络·网络协议·tcp/ip