
目录摘要注释3.1 回顾3.2 新知识3.3 注意3.4 反思正文4.1 生命周期与返回值约定4.2 服务端路径4.3 收发与阻塞4.4 开发中遇到的问题总结常见问题标签摘要Socket.hpp 是 header-only 的单句柄 TCP 封装唯一成员为int _sockfd默认构造置 -1析构close后复位。对外提供Create / Bind / Listen / Accept / Connect / Recv / Send / Close另有SetNonBlock、SetNonBlockRecv / SetNonBlockSend与CreateServer / CreateClient两个组合入口。价值在于把系统调用返回值判定、sockaddr_in填充与字节序转换收在一处。注释段自述本实现保证 HTTP 请求的服务端而非客户端。代码仓库https://gitee.com/btshadow/Ubuntu_learning/blob/master/October/10-17/muduo/src/server.hpp注释3.1 回顾htons/ntohsTCP/IP 规定用大端序即网络字节序本机算出要发到网络用htons/htonl从网络收到转本机用ntohs/ntohl。Bind与Connect里的htons(port)属于服务端自身绑定、要让客户端看到该端口的那一侧。accept 二、三参数第二参数是输出参数存放对端地址第三参数是值-结果参数传入时为 addr 缓冲区大小返回时为实际写入长度见 bug3。ET 模式epoll 的边缘触发只有状态变化从无到有/从有到多时通知一次对比 LT。本文件未含 epoll仅记录。3.2 新知识noexcept(true)等价于noexcept是不抛异常的显式声明内部真抛了异常则直接std::terminate()。承诺的是不抛 C 异常不是调用一定成功。价值在于移动构造/移动赋值标 noexcept 后std::vector等容器扩容走移动而非拷贝。只在确实不抛处用移动构造、swap、析构、getter可能分配内存或抛业务异常的不标。本文件中带 noexcept 的只有注释抄录的listen原型。3.3 注意sockaddr_in在 bind 与 connect 前有必要清空Bind、Connect各有memsetaccept 时没必要因为 accept 内是覆盖式写入Accept未 memset。EAGAIN与EINTR作为读/写异常EAGAIN 关联非阻塞MSG_DONTWAITEINTR 关联信号与非阻塞关系小Recv、Send的 errno 判定。客户端无需 bind客户端间各自独立且难以手动管理内核已代做故CreateClient只有 Create Connect。3.4 反思测试时太大意了主要是不想测试、想快点结束结果代码本身没有 bug却因测试方式以为有 bug。正文4.1 生命周期与返回值约定Socket()置_sockfd -1Socket(int)接管外部 fdFd()取值析构close后置 -1。Close()只close不复位_sockfd故析构时会对同一 fd 再close一次。每个系统调用失败路径都走ERR_LOG成功的组合入口用INF_LOG。4.2 服务端路径CreateServer(port, ip 0.0.0.0)依次Create→Bind→Listen成功后INF_LOG并调SetReuseAddrPort。Create用socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)Bind先memset再填sin_family、htons(port)地址由inet_pton写入sin_addr旧的inet_addr写法留在注释中Listen缺省MAX_LISTEN 1024。SetReuseAddrPort对SO_REUSEADDR与SO_REUSEPORT各setsockopt一次位置在 bind/listen 之后失败只记录。4.3 收发与阻塞Recv/Send直接转发系统调用n 0时若 errno 为EAGAIN或EINTR返回 0否则ERR_LOG后返回 -1Send另有n 0的独立日志分支。SetNonBlockRecv/SetNonBlockSend以MSG_DONTWAIT调用。SetNonBlock用F_GETFL取标志再F_SETFL | O_NONBLOCK。客户端方向CreateClient只有 Create 与 Connect不含 BindConnect同样 memset、htons、inet_pton。4.4 开发中遇到的问题bug1INADDR_ANY是 0 不是字符串极易误会注释记 [30min]。原写法const std::string ip INADDR_ANY已注释掉现默认值为0.0.0.0仍交给inet_pton转换。bug2测试时注意监听套接字不能用于通信。标注位于Send()内。bug3accept 的第三参数同时作为输入输出参数调用前须给出缓冲区大小// Accept()// 原socklen_t len;socklen_t lensizeof(client_addr);总结封装边界是套接字生命周期加系统调用的返回判定三条注意分别落在Bind/Connect的 memset、Recv/Send的 errno 分支、CreateClient不 Bindbug3 正是值-结果参数未传初值。bug1 属类型不匹配bug2 属套接字角色混用。常见问题为何Bind、Connect要 memset 而Accept不用前两者是值传入Accept内覆盖式写入client_addr无需清空。为何INADDR_ANY不能当std::string默认参数它是 0 而非字符串bug1现用0.0.0.0经inet_pton转换。Recv/Send中EAGAIN与EINTR为何都返回 0二者都不视为致命错误交由上层下轮再试。bug3 中 len 不初始化会怎样accept把它当输入缓冲区大小未给值即未定义故须socklen_t len sizeof(client_addr)。标签C、TCP Socket 封装、inet_pton、EAGAIN/EINTR、header-only【统计全文约 1180 字 / 原代码 256 行 / 比例约 4.6注释字段小节 4 个 / 成文 bug 3 处】