前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++中的socket编程常用接口

C++中的socket编程常用接口

作者头像
薄荷冰
发布2024-07-19 08:23:54
900
发布2024-07-19 08:23:54
举报
文章被收录于专栏:后端学习之旅
一、socket

socket() 函数是进行网络编程的基础,它用于创建一个新的套接字(socket)。套接字是网络通信的端点,可以用于在不同计算机之间传输数据。下面是对 socket() 函数的详细解释:

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数

socket() 函数有三个参数:

  1. domain(协议域):指定套接字使用的协议族。常见的值包括:
  • AF_INET:IPv4协议
  • AF_INET6:IPv6协议
  • AF_UNIX(或 AF_LOCAL):本地通信(同一台机器上的进程间通信)
  1. type(套接字类型):指定套接字的类型。常见的值包括:
  • SOCK_STREAM:面向连接的流式套接字,使用TCP协议
  • SOCK_DGRAM:无连接的数据报套接字,使用UDP协议
  • SOCK_RAW:原始套接字,允许对底层协议直接访问
  1. protocol(协议):指定使用的协议。常用值包括:
  • 0:通常用于选择默认协议。例如,当 domainAF_INETtypeSOCK_STREAM 时,默认协议是TCP。
返回值

socket() 函数成功时返回一个套接字描述符(非负整数),失败时返回 -1 并设置 errno 来指示错误。

bind()

bind() 函数用于将套接字绑定到一个本地地址和端口。对于服务器端套接字,这是必需的步骤,因为它指定了服务器将在其上监听连接请求的地址和端口。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数

bind() 函数有三个参数:

  1. sockfd:由 socket() 函数返回的套接字描述符。
  2. addr:指向 sockaddr 结构的指针,包含了要绑定的地址和端口信息。
  3. addrlensockaddr 结构的长度。

其中sockaddr 结构

在 IPv4 中,sockaddr 结构通常是 sockaddr_in 结构,它定义如下:

代码语言:javascript
复制
struct sockaddr_in {
    sa_family_t sin_family;   // 地址族 (AF_INET)//IPv4
    in_port_t sin_port;       // 端口号 (使用 htons() 转换)
    struct in_addr sin_addr;  // IP 地址
};

struct in_addr {
    uint32_t s_addr;          // 地址 (使用 inet_addr() 或 INADDR_ANY(它的值是 0.0.0.0,表示所有的 IPv4 地址。))
};
返回值

bind() 函数成功时返回 0,失败时返回 -1 并设置 errno 来指示错误。

示例
代码语言:javascript
复制
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

#define PORT 8080

int main() {
    int sockfd;
    struct sockaddr_in address;

    // 创建一个 IPv4 的 TCP 套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        return -1;
    }

    // 初始化地址结构
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;  // 绑定到所有可用接口
    address.sin_port = htons(PORT);        // 将端口号转换为网络字节序

    // 绑定套接字到指定地址和端口
    if (bind(sockfd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        close(sockfd);
        return -1;
    }

    std::cout << "Bind successful" << std::endl;

    // 关闭套接字
    close(sockfd);

    return 0;
}

在上面的示例中,我们执行了以下步骤:

  1. 使用 socket() 函数创建一个套接字。
  2. 初始化 sockaddr_in 结构,将地址族设置为 AF_INET,IP 地址设置为 INADDR_ANY(这意味着绑定到所有可用的接口),端口号设置为 8080(使用 htons() 函数将端口号从主机字节序转换为网络字节序)。
  3. 使用 bind() 函数将套接字绑定到指定的地址和端口。
  4. 如果绑定成功,输出成功信息;否则,输出错误信息。
  5. 关闭套接字。

bind() 函数在服务器端使用较多,客户端通常不需要显式调用这个函数,因为操作系统会在 connect() 函数调用时自动选择一个合适的端口。

三、listen

listen() 函数用于将一个套接字设置为被动模式,即它将成为一个服务器套接字,可以接受来自客户端的连接请求。这个函数在服务器端使用,是建立一个TCP服务器的重要步骤之一。

代码语言:javascript
复制
#include <sys/socket.h>

int listen(int sockfd, int backlog);
参数

listen() 函数有两个参数:

  1. sockfd:由 socket() 函数返回的套接字描述符。
  2. backlog:定义了内核为此套接字排队的最大连接数。如果连接请求的数量超过此值,则新的连接请求可能会被拒绝。
返回值

listen() 函数成功时返回 0,失败时返回 -1 并设置 errno 来指示错误。

使用步骤

在服务器端,典型的步骤是:

  1. 创建套接字 (socket()).
  2. 绑定套接字到本地地址和端口 (bind()).
  3. 将套接字设置为监听模式 (listen()).
  4. 接受客户端连接 (accept()).
四、accept()

accept() 函数用于在服务器端接受一个客户端的连接请求。它从已完成连接队列中取出下一个连接,并为新的连接创建一个新的套接字。accept() 是阻塞调用,直到有新的连接进来。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数

accept() 函数有三个参数:

  1. sockfd:由 socket()bind() 函数创建并由 listen() 函数设置为监听模式的套接字描述符。
  2. addr:指向 sockaddr 结构体的指针,接受连接的客户端的地址信息。可以是 sockaddr_in(对于IPv4)或 sockaddr_in6(对于IPv6)结构体。
  3. addrlen:指向一个 socklen_t 类型的变量,它在调用时指定 addr 结构的大小,并在返回时被设置为客户端地址的实际大小。
返回值

accept() 函数成功时返回一个新的套接字描述符(非负整数),用于与客户端通信;失败时返回 -1 并设置 errno 来指示错误。

五、connect()

connect() 函数在客户端编程中起着关键作用。它用于将客户端的套接字连接到服务器的地址和端口。connect() 通过向服务器发送连接请求,并在服务器接受连接请求后,建立一个双向的通信通道。

connect() 的使用

connect() 函数通常在客户端使用,它将客户端的套接字连接到指定的服务器地址和端口。调用 connect() 时,客户端的套接字必须已经使用 socket() 函数创建。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd:通过 socket() 创建的套接字文件描述符。
  • addr:指向包含服务器地址信息的 sockaddr 结构体。
  • addrlen:地址结构体的长度。
返回值

成功时返回 0,失败时返回 -1 并设置 errno

六、recv

recv() 函数用于在连接建立后从套接字接收数据。它通常用于从服务器或客户端接收数据,可以在服务器端和客户端的通信中使用。

recv() 的使用

recv() 函数通常在已经建立连接的套接字上使用,用于从对端接收数据。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数
  • sockfd:已连接的套接字文件描述符。
  • buf:指向用于存储接收到的数据的缓冲区。
  • len:缓冲区的长度。
  • flags:接收操作的标志。常用标志包括 0(默认)和 MSG_DONTWAIT(非阻塞模式)。
返回值

成功时返回接收到的字节数,失败时返回 -1 并设置 errno

七、read
代码语言:javascript
复制
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
参数
  • fd:文件描述符,可以是套接字、文件、管道等。
  • buf:指向用于存储接收到的数据的缓冲区。
  • count:缓冲区的长度。
返回值

成功时返回读取的字节数,失败时返回 -1 并设置 errno

read与recv的区别

功能范围

  • recv() 专门用于套接字通信,并且可以指定额外的标志来控制接收行为。
  • read() 是一个通用的系统调用,可以用于任何文件描述符,包括套接字、文件、管道等。

标志选项

  • recv() 允许使用 flags 参数来指定额外的控制选项,例如 MSG_DONTWAITMSG_PEEK 等。
  • read() 没有 flags 参数,因此不提供额外的控制选项。

使用场景

  • 如果需要使用额外的控制选项或明确表示这是一个网络操作,通常使用 recv()
  • 如果只需要简单地从文件描述符读取数据且不需要额外控制选项,通常使用 read()
八、send

send() 函数用于向套接字发送数据。它与 recv() 对应,通常在服务器端和客户端的通信中使用。

send() 的使用

send() 函数通常在已建立连接的套接字上使用,用于向对端发送数据。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd:已连接的套接字文件描述符。
  • buf:指向包含要发送的数据的缓冲区。
  • len:缓冲区中要发送数据的长度。
  • flags:用于指定发送操作的标志。常用的标志包括 0(默认)和 MSG_DONTWAIT(非阻塞模式)。
返回值

成功时返回发送的字节数,失败时返回 -1 并设置 errno

九、close

close() 函数用于关闭一个打开的文件描述符,这里包括套接字。关闭一个套接字会释放它占用的所有资源。对于网络编程来说,close() 是一个重要的步骤,因为它会终止与该套接字相关的所有网络连接。

close() 的使用

close() 是一个非常简单的系统调用,用于关闭文件描述符。它的定义如下:

代码语言:javascript
复制
#include <unistd.h>
int close(int fd);
  • fd:要关闭的文件描述符。
返回值

成功时返回 0,失败时返回 -1 并设置 errno

close的关闭顺序

在网络编程中,正确关闭套接字对于释放资源和确保连接的正常终止非常重要。套接字关闭的顺序通常如下:

  1. 客户端关闭连接:客户端在完成所有数据发送和接收后,首先关闭自己的套接字。
  2. 服务器关闭连接:服务器在检测到客户端已经关闭连接之后,关闭相应的客户端套接字。
十、setsockopt()

setsockopt() 函数用于设置套接字选项。它可以控制套接字的行为,如允许端口复用、设置超时时间、控制数据包的发送和接收缓冲区大小等。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • sockfd:套接字文件描述符。
  • level:选项的级别。例如,SOL_SOCKET 表示通用套接字选项。常见的级别包括:
    • SOL_SOCKET:适用于通用套接字选项。
    • IPPROTO_TCP:适用于 TCP 特定选项。
    • IPPROTO_IP:适用于 IP 特定选项。
  • optname:需要设置的选项名称。
  • optval:指向包含选项值的缓冲区。
  • optlenoptval 缓冲区的大小。

返回值:成功时返回 0,失败时返回 -1 并设置 errno

常用选项

以下是一些常用的 setsockopt() 中optname选项:

  • SO_REUSEADDR:允许重用本地地址和端口。
  • SO_REUSEPORT:允许多个套接字绑定到同一个端口(在某些系统中可用)。
  • SO_RCVBUF:设置接收缓冲区的大小。
  • SO_SNDBUF:设置发送缓冲区的大小。
  • SO_KEEPALIVE:启用保活机制,以检测断开的连接。

这五个常用的选项,对应的optval都是int选项SO_RCVBUF SO_SNDBUF 对应的int是缓存区的大小,其他的是1(启用),0(禁用)。

十一、fcntl

fcntl 函数在 Unix 系统中用于对文件描述符进行各种控制操作,包括设置非阻塞模式、获取和设置文件描述符标志等。在网络编程中,它通常用于设置套接字的非阻塞模式。

代码语言:javascript
复制
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

fd:文件描述符,即要进行操作的套接字或文件的句柄。 cmd:操作命令,指定要执行的操作,可以是以下之一:

  • F_GETFL:获取文件状态标志。(此时第三个参数不是必需的,可以传递 0 或者 NULL。)
  • F_SETFL:设置文件状态标志。
  • 常见的有
  • 非阻塞模式 (O_NONBLOCK)
    • 作用:将套接字设置为非阻塞模式,使得读写操作不会阻塞进程,而是立即返回。适用于需要异步操作的场景,如同时处理多个连接或超时控制。
    • 使用方式fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

    异步输入输出模式 (O_ASYNC)

    • 作用:允许套接字接收到信号通知,表明有数据可读或写入完成。
    • 使用方式fcntl(sockfd, F_SETFL, flags | O_ASYNC);

    关闭非阻塞模式 (O_NONBLOCK 的反操作)

    • 作用:关闭套接字的非阻塞模式,使得读写操作会阻塞进程直到操作完成。
    • 使用方式fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK);
示例代码
代码语言:javascript
复制
// 设置套接字为非阻塞模式
int flags = fcntl(sockfd, F_GETFL, 0);
if (flags == -1) {
    perror("fcntl F_GETFL failed");
    close(sockfd);
    return -1;
}
if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
    perror("fcntl F_SETFL failed");
    close(sockfd);
    return -1;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-07-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、socket
    • 参数
      • 返回值
      • bind()
        • 参数
          • 返回值
            • 示例
            • 三、listen
              • 参数
                • 返回值
                  • 使用步骤
                  • 四、accept()
                    • 参数
                      • 返回值
                      • 五、connect()
                        • connect() 的使用
                          • 返回值
                          • 六、recv
                            • recv() 的使用
                              • 参数:
                                • 返回值
                                • 七、read
                                  • 参数:
                                    • 返回值
                                      • read与recv的区别
                                      • 八、send
                                        • send() 的使用
                                          • 返回值
                                          • 九、close
                                            • close() 的使用
                                              • 返回值
                                                • close的关闭顺序
                                                • 十、setsockopt()
                                                  • 常用选项
                                                  • 十一、fcntl
                                                    • 示例代码
                                                    领券
                                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档