传统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()
read
和write
是通用的,是阻塞的send
和recv
是为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_in
是IPv4
专用结构(实际使用中强制转换)
服务器
开发时可以使用测试工具作为客户端/服务器,【下载地址】
流程
- 创建套接字 (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