Skip to content

传统Socket的简单使用

这里简单使用,其他如结合IO多路复用多线程使用见相关文章

相关Api

socket

c
int socket(int domain, int type, int protocol);
  • 参数:
    • domain: 协议族 (AF_INET/IPv4, AF_INET6/IPv6, AF_UNIX/本地套接字)
    • type: 套接字类型 (SOCK_STREAM/TCP, SOCK_DGRAM/UDP)
    • protocol: 通常为0,自动选择
  • 返回值:成功返回套接字描述符,失败返回-1

bind

c
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数:
    • sockfd: socket()返回的套接字描述符
    • addr: 指向要绑定的地址结构体指针
    • addrlen: 地址结构体长度
  • 返回值:成功返回0,失败返回-1

这里struct sockaddr是服务器使用的,单独为服务器声明一个

accept

c
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 参数:
    • sockfd: 处于监听状态的套接字
    • addr: 用于存储客户端地址信息
    • addrlen: 地址结构体长度的指针
  • 返回值:成功返回新套接字描述符,失败返回-1

这里struct sockaddr存储客户端的信息,应该为每次每个客户端单独声明,不需要客户端信息,可以为NULL

listen

c
int listen(int sockfd, int backlog);
  • 参数:
    • sockfd: 已绑定的套接字描述符
    • backlog: 连接请求队列的最大长度
  • 返回值:成功返回0,失败返回-1

connect

c
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数:
    • sockfd: socket()返回的套接字描述符
    • addr: 服务器地址信息
    • addrlen: 地址结构体长度
  • 返回值:成功返回0,失败返回-1

客户端连接时需要服务器的信息,这里struct sockaddr是服务器的信息,需要客户端自己声明

send()/write()

  • readwrite是通用的,是阻塞的
  • sendrecv是为socket设计的,可以设置为非阻塞,但是实际是轮询
c
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t write(int sockfd, const void *buf, size_t count);
  • 参数:
    • sockfd: 已连接的套接字描述符
    • buf: 发送数据缓冲区
    • len/count: 发送数据长度
    • flags: 发送标志(通常为0)
  • 返回值:实际发送数、写入数

recv()/read()

c
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t read(int sockfd, void *buf, size_t count);
  • 参数:
    • sockfd: 已连接的套接字描述符
    • buf: 接收数据缓冲区
    • len/count: 缓冲区长度
    • flags: 接收标志(通常为0)
  • 返回值:实际读取接收数

close

c
int close(int fd);

关闭套接字

相关头文件

c
#include <sys/socket.h>      // Socket 核心函数(socket(), bind(), listen(), accept() 等)
#include <netinet/in.h>      // IPv4/IPv6 地址结构(struct sockaddr_in, sockaddr_in6)
#include <netinet/tcp.h>     // TCP 选项(如 TCP_NODELAY)
#include <sys/types.h>       // 基本数据类型(如 size_t, ssize_t)
#include <unistd.h>          // 系统调用(close(), read(), write())
#include <arpa/inet.h>       // ip和字节序转换


#include <sys/stat.h>        // 文件状态(stat(), fstat() 获取文件大小、权限等)
#include <fcntl.h>           // 文件控制(open(), O_RDONLY, O_WRONLY 等)
#include <sys/sendfile.h>    // 高效文件传输(sendfile())

struct sockaddr说明

函数addr 方向填充方典型用途是否需要长期保存?
bind输入参数调用者初始化服务端绑定本地地址❌ 临时变量
connect输入参数调用者初始化客户端连接服务端地址❌ 临时变量
accept输出参数内核填充服务端获取客户端地址⚠️ 可选(如日志)
  • struct sockaddr 是通用地址结构(抽象基类)。
  • struct sockaddr_inIPv4 专用结构(实际使用中强制转换)

服务器

开发时可以使用测试工具作为客户端/服务器,【下载地址】

image-20250505130736607

流程

  • 创建套接字 (socket())
  • 绑定地址和端口 (bind())
  • 监听连接 (listen())
  • 接受客户端连接 (accept())
  • 与客户端通信 (recv()/send())
  • 关闭连接 (close())

需要持续等待客户端连接

实例

cpp
#ifndef SERVER_H
#define SERVER_H

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>

class Server
{
public:
    Server(int port);
    ~Server();
    void init();
    void handel();
    void echoMsg(int client_fd);
    std::string readMsg(int client_fd);
    void writeMsg(int client_fd, const std::string &msg);

private:
    int server_fd;
    // 不使用struct sockaddr,绑定时需要强转
    // 实际使用sockaddr_in或sockaddr_in6来区分ip类型
    struct sockaddr_in address;
};

#endif

客户端

流程

  • 创建套接字 (socket())
  • 连接服务器 (connect())
  • 发送数据 (send())
  • 接收响应 (recv())
  • 关闭连接 (close())

实例

cpp
#ifndef CLIENT_H
#define CLIENT_H

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>

class Client
{
public:
    Client(const int port, const std::string &ip);
    ~Client();
    void handel();
    void echoMsg();
    std::string readMsg();
    void writeMsg(const std::string &msg);

private:
    int client_fd;
    struct sockaddr_in server_addr;
};

#endif

测试

image-20250505154320255