简写网络库(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();
}
}
相关推荐
冷的方程式2 小时前
安装在虚拟机中的kali设置网络联接
网络
JiMoKuangXiangQu2 小时前
Linux 内存案例:DDR 访问出错?
linux·内存·ddr ecc
安卓程序员_谢伟光2 小时前
如何监听System.exit(0)的调用栈
java·服务器·前端
Xの哲學2 小时前
从硬中断到 softirq:Linux 软中断机制的全景解剖
linux·服务器·网络·算法·边缘计算
lsp84ch803 小时前
MacBookPro运行飞牛Nas,解决合盖亮屏
linux·网络·macbook·nas·飞牛
wdfk_prog3 小时前
[Linux]学习笔记系列 -- [fs]mnt_idmapping
linux·笔记·学习
optimistic_chen3 小时前
【Redis 系列】常用数据结构---Hash类型
linux·数据结构·redis·分布式·哈希算法
我就是你毛毛哥3 小时前
Linux 定时备份 MySQL 并推送 Gitee
linux·mysql
儒道易行3 小时前
平凡的2025年终总结
网络·安全·web安全