Administrator
Administrator
发布于 2024-12-05 / 8 阅读
0
0

Socket套接字

Socket 编程是网络编程的基础,它允许不同计算机或同一台计算机上的不同进程之间进行通信。理解和正确使用套接字(socket)的各种参数对于开发高效、可靠的网络应用至关重要。本文将详细讲解套接字的各种参数及其使用方法,包括协议族(如 PF_INET)、套接字类型(如 SOCK_RAW)等。

目录

  1. 什么是套接字(Socket)
  2. 套接字的主要参数
  3. 创建套接字
  4. 常用套接字选项(Socket Options)
  5. 套接字的使用流程
  6. 套接字编程示例
  7. 常见问题与解决方案
  8. 总结
  9. 附录:常用套接字函数

1. 什么是套接字(Socket)

套接字(Socket)是应用程序与网络通信之间的中介,通过套接字,应用程序可以发送和接收数据。套接字抽象了网络通信的复杂性,提供了一组标准的接口,允许不同的进程或不同的计算机之间进行通信。

套接字通常用于实现以下协议:

  • TCP(传输控制协议):面向连接,可靠的数据传输。
  • UDP(用户数据报协议):无连接,不保证可靠性,适用于实时应用。

2. 套接字的主要参数

创建套接字时,需要指定几个关键参数,这些参数决定了套接字的行为和功能。主要参数包括协议族(Protocol Family)、套接字类型(Socket Type)和协议(Protocol)。

2.1 协议族(Protocol Family)

协议族(有时称为地址族)定义了套接字所使用的协议和地址类型。常见的协议族包括:

  • PF_INET:用于 IPv4 网络通信。
  • PF_INET6:用于 IPv6 网络通信。
  • PF_UNIXPF_LOCAL:用于本地(同一台计算机上的)进程间通信。
  • PF_PACKET:用于底层网络通信,允许访问网络层及以下层的数据。

常见协议族示例:

#include <sys/socket.h>

// IPv4
int sockfd_ipv4 = socket(PF_INET, SOCK_STREAM, 0);

// IPv6
int sockfd_ipv6 = socket(PF_INET6, SOCK_DGRAM, 0);

// UNIX 本地套接字
int sockfd_unix = socket(PF_UNIX, SOCK_STREAM, 0);

2.2 套接字类型(Socket Type)

套接字类型定义了数据传输的方式。主要的套接字类型包括:

  • SOCK_STREAM:提供面向连接的、可靠的数据传输,通常用于 TCP。
  • SOCK_DGRAM:提供无连接的数据传输,不保证可靠性,通常用于 UDP。
  • SOCK_RAW:允许访问底层协议,通常用于实现自定义协议或网络监控工具。
  • SOCK_SEQPACKET:提供面向连接的数据传输,保留消息边界。

常见套接字类型示例:

#include <sys/socket.h>

// 面向连接的套接字(TCP)
int sockfd_stream = socket(PF_INET, SOCK_STREAM, 0);

// 无连接的套接字(UDP)
int sockfd_dgram = socket(PF_INET, SOCK_DGRAM, 0);

// 原始套接字
int sockfd_raw = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);

2.3 协议(Protocol)

协议参数指定了要使用的具体协议。大多数情况下,可以将其设置为 0,由系统自动选择合适的协议。但在某些情况下,需要明确指定协议。

常见协议值:

  • IPPROTO_TCP:TCP 协议。
  • IPPROTO_UDP:UDP 协议。
  • IPPROTO_ICMP:ICMP 协议。
  • IPPROTO_RAW:原始 IP 协议。

协议参数示例:

#include <sys/socket.h>
#include <netinet/in.h>

// 明确指定 TCP 协议
int sockfd_tcp = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

// 明确指定 UDP 协议
int sockfd_udp = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

// 明确指定 ICMP 协议(需要 root 权限)
int sockfd_icmp = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);

3. 创建套接字

创建套接字的基本步骤如下:

  1. 调用 socket() 函数:创建一个新的套接字。
  2. 配置套接字选项(可选):使用 setsockopt() 设置套接字参数。
  3. 绑定套接字(服务器端):使用 bind() 将套接字绑定到特定的地址和端口。
  4. 监听连接(服务器端):使用 listen() 开始监听传入的连接。
  5. 接受连接(服务器端):使用 accept() 接受传入的连接。
  6. 连接套接字(客户端):使用 connect() 连接到服务器。
  7. 发送和接收数据:使用 send(), recv(), sendto(), recvfrom() 等函数进行数据传输。
  8. 关闭套接字:使用 close() 关闭套接字。

示例:创建一个 TCP 套接字

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    // 创建套接字
    int sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 配置服务器地址
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080); // 端口号
    server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有接口

    // 绑定套接字
    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(sockfd, 5) < 0) {
        perror("listen");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("服务器正在监听端口 8080...\n");

    // 接受连接
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
    if (connfd < 0) {
        perror("accept");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("接受到一个连接。\n");

    // 发送数据
    char *msg = "Hello, Client!";
    send(connfd, msg, strlen(msg), 0);

    // 关闭连接
    close(connfd);
    close(sockfd);

    return 0;
}

4. 常用套接字选项(Socket Options)

套接字选项允许开发者配置套接字的行为,以满足特定的需求。可以使用 setsockopt()getsockopt() 函数来设置和获取套接字选项。

4.1 SO_REUSEADDR

描述:允许套接字重新绑定到一个已经处于 TIME_WAIT 状态的地址。这在服务器重启时非常有用,避免出现“地址已被使用”的错误。

使用方法

#include <sys/socket.h>
#include <netinet/in.h>

int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

示例

在服务器端绑定之前设置 SO_REUSEADDR 选项:

int sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

4.2 SO_KEEPALIVE

描述:启用 TCP 连接的保活机制,以检测死掉的连接。

使用方法

#include <sys/socket.h>
#include <netinet/in.h>

int keepalive = 1;
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));

示例

int sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
int keepalive = 1;
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));

4.3 SO_LINGER

描述:控制套接字关闭时的数据传输行为。可以设置套接字在关闭时等待未发送的数据完成传输。

使用方法

#include <sys/socket.h>
#include <netinet/in.h>

struct linger sl;
sl.l_onoff = 1;    // 启用 linger
sl.l_linger = 10;  // 在关闭时等待 10 秒
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));

示例

struct linger sl;
sl.l_onoff = 1;
sl.l_linger = 5;
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));

4.4 SO_RCVBUF 和 SO_SNDBUF

描述:设置接收缓冲区和发送缓冲区的大小。可以根据应用需求调整缓冲区大小以优化性能。

使用方法

#include <sys/socket.h>
#include <netinet/in.h>

int rcvbuf_size = 65535;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size));

int sndbuf_size = 65535;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size));

示例

int sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
int rcvbuf_size = 131072; // 128KB
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size));

int sndbuf_size = 131072; // 128KB
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size));

5. 套接字的使用流程

套接字的使用流程因客户端和服务器端有所不同。以下分别介绍客户端和服务器端的基本流程。

5.1 客户端套接字流程

  1. 创建套接字:调用 socket() 函数。
  2. 配置服务器地址:填充 struct sockaddr_in 结构体。
  3. 连接服务器:调用 connect() 函数。
  4. 发送和接收数据:使用 send(), recv() 等函数。
  5. 关闭套接字:调用 close() 函数。

示例:TCP 客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char *message = "Hello, Server!";
    char buffer[1024];

    // 创建套接字
    sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080); // 服务器端口
    if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {
        perror("inet_pton");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 发送数据
    send(sockfd, message, strlen(message), 0);

    // 接收数据
    int len = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
    if (len > 0) {
        buffer[len] = '\0';
        printf("服务器回应:%s\n", buffer);
    }

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

    return 0;
}

5.2 服务器端套接字流程

  1. 创建套接字:调用 socket() 函数。
  2. 绑定套接字:调用 bind() 函数,将套接字绑定到特定的地址和端口。
  3. 监听连接:调用 listen() 函数,开始监听传入的连接。
  4. 接受连接:调用 accept() 函数,接受传入的连接。
  5. 发送和接收数据:使用 send(), recv() 等函数。
  6. 关闭套接字:调用 close() 函数。

示例:TCP 服务器端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    int listenfd, connfd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    char buffer[1024];
    char *response = "Hello, Client!";

    // 创建套接字
    listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listenfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080); // 监听端口
    server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有接口

    // 设置 SO_REUSEADDR 选项
    int reuse = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    // 绑定套接字
    if (bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(listenfd, 5) < 0) {
        perror("listen");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    printf("服务器正在监听端口 8080...\n");

    // 接受连接
    connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
    if (connfd < 0) {
        perror("accept");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    printf("接受到一个连接。\n");

    // 接收数据
    int len = recv(connfd, buffer, sizeof(buffer) - 1, 0);
    if (len > 0) {
        buffer[len] = '\0';
        printf("收到客户端消息:%s\n", buffer);
    }

    // 发送回应
    send(connfd, response, strlen(response), 0);

    // 关闭连接
    close(connfd);
    close(listenfd);

    return 0;
}

6. 套接字编程示例

通过具体的编程示例,可以更直观地理解套接字的参数与使用方法。以下提供了几个常见的套接字编程示例,包括使用 SOCK_RAW 的示例。

6.1 TCP 客户端示例

功能:连接到服务器,发送一条消息,并接收服务器的回应。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char *message = "Hello, Server!";
    char buffer[1024];

    // 创建套接字
    sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
        perror("inet_pton");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 发送消息
    send(sockfd, message, strlen(message), 0);

    // 接收回应
    int len = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
    if (len > 0) {
        buffer[len] = '\0';
        printf("服务器回应:%s\n", buffer);
    }

    // 关闭套接字
    close(sockfd);
    return 0;
}

6.2 TCP 服务器端示例

功能:监听特定端口,接受客户端连接,接收消息并发送回应。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SERVER_PORT 8080
#define BACKLOG 5

int main() {
    int listenfd, connfd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    char buffer[1024];
    char *response = "Hello, Client!";

    // 创建套接字
    listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listenfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    // 设置 SO_REUSEADDR 选项
    int reuse = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    // 绑定套接字
    if (bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(listenfd, BACKLOG) < 0) {
        perror("listen");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    printf("服务器正在监听端口 %d...\n", SERVER_PORT);

    // 接受连接
    connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
    if (connfd < 0) {
        perror("accept");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    printf("接受到一个连接。\n");

    // 接收消息
    int len = recv(connfd, buffer, sizeof(buffer) - 1, 0);
    if (len > 0) {
        buffer[len] = '\0';
        printf("收到客户端消息:%s\n", buffer);
    }

    // 发送回应
    send(connfd, response, strlen(response), 0);

    // 关闭连接
    close(connfd);
    close(listenfd);
    return 0;
}

6.3 使用 SOCK_RAW 的示例

注意:创建原始套接字通常需要超级用户权限(root)。原始套接字允许开发者访问和操作网络层及以下层的数据包,这在实现自定义协议、网络监控或防火墙等应用中非常有用。

功能:捕获网络上的所有 ICMP 数据包(例如,ping 请求和回应)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip.h>

// 计算校验和
unsigned short checksum(void *b, int len) {    
    unsigned short *buf = b;
    unsigned int sum = 0;
    unsigned short result;
    
    for(sum = 0; len > 1; len -= 2)
        sum += *buf++;
    if(len == 1)
        sum += *(unsigned char*)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    result = ~sum;
    return result;
}

int main() {
    int sockfd;
    char buffer[1024];
    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(addr);

    // 创建原始套接字
    sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    printf("捕获 ICMP 数据包...\n");

    while (1) {
        // 接收数据包
        int len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&addr, &addr_len);
        if (len < 0) {
            perror("recvfrom");
            close(sockfd);
            exit(EXIT_FAILURE);
        }

        // 解析 IP 头
        struct iphdr *ip = (struct iphdr*)buffer;
        struct sockaddr_in source, dest;
        source.sin_addr.s_addr = ip->saddr;
        dest.sin_addr.s_addr = ip->daddr;

        // 解析 ICMP 头
        struct icmphdr *icmp = (struct icmphdr*)(buffer + (ip->ihl * 4));

        // 打印信息
        printf("从 %s 到 %s\n", inet_ntoa(source.sin_addr), inet_ntoa(dest.sin_addr));
        printf("ICMP 类型:%d\n", icmp->type);
        printf("ICMP 代码:%d\n\n", icmp->code);
    }

    // 关闭套接字
    close(sockfd);
    return 0;
}

编译与运行

gcc -o icmp_sniffer icmp_sniffer.c
sudo ./icmp_sniffer

输出示例

捕获 ICMP 数据包...
从 192.168.1.2 到 192.168.1.1
ICMP 类型:8
ICMP 代码:0

从 192.168.1.1 到 192.168.1.2
ICMP 类型:0
ICMP 代码:0

7. 常见问题与解决方案

7.1 创建套接字失败

问题描述:调用 socket() 函数失败,返回 -1,并设置 errno

可能原因

  • 权限不足(例如,创建 SOCK_RAW 套接字需要超级用户权限)。
  • 系统资源耗尽。
  • 指定的协议族或套接字类型不支持。

解决方案

  • 确保以正确的权限运行程序(需要 root 权限时)。
  • 检查系统资源,如文件描述符限制。
  • 确认所使用的协议族和套接字类型是否正确且受支持。

7.2 连接被拒绝

问题描述:客户端调用 connect() 函数时失败,错误为 ECONNREFUSED

可能原因

  • 服务器未启动或未监听指定端口。
  • 防火墙阻止了连接。
  • 服务器地址或端口配置错误。

解决方案

  • 确认服务器端程序已启动并正在监听正确的端口。
  • 检查防火墙设置,确保相关端口已开放。
  • 验证客户端使用的服务器地址和端口是否正确。

7.3 数据传输中断

问题描述:在数据传输过程中,连接意外中断。

可能原因

  • 网络故障或不稳定。
  • 服务器端程序崩溃或关闭了连接。
  • 超时设置导致连接被关闭。

解决方案

  • 检查网络连接,确保网络稳定。
  • 查看服务器端日志,确认服务器是否正常运行。
  • 调整超时设置,确保连接不会过早关闭。

7.4 使用 SOCK_RAW 需要权限

问题描述:尝试创建 SOCK_RAW 套接字时,收到 Operation not permitted 错误。

解决方案

  • 以超级用户(root)权限运行程序。

  • 在需要时,赋予特定权限,例如使用 setcap 命令:

    sudo setcap cap_net_raw+ep ./icmp_sniffer
    

8. 总结

套接字编程是实现网络通信的核心技术,通过理解套接字的各种参数(如协议族 PF_INET、套接字类型 SOCK_RAW 等),开发者可以灵活地创建适用于不同场景的网络应用。本文详细介绍了套接字的主要参数及其使用方法,并通过具体的编程示例展示了如何创建和使用不同类型的套接字。

掌握套接字编程不仅有助于开发高效的网络应用,还为深入理解网络协议和实现自定义网络解决方案打下坚实的基础。随着网络技术的发展,套接字编程仍将是网络工程师和开发者不可或缺的技能。

9. 附录:常用套接字函数

9.1 socket()

描述:创建一个新的套接字。

原型

int socket(int domain, int type, int protocol);

参数

  • domain:协议族,如 PF_INET
  • type:套接字类型,如 SOCK_STREAM
  • protocol:具体协议,如 IPPROTO_TCP。通常设为 0 由系统自动选择。

返回值:成功时返回套接字描述符,失败时返回 -1 并设置 errno

9.2 bind()

描述:将套接字绑定到特定的地址和端口。

原型

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数

  • sockfd:套接字描述符。
  • addr:指向 struct sockaddr 结构体的指针,包含地址和端口信息。
  • addrlen:地址结构的长度。

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

9.3 listen()

描述:使套接字处于监听状态,等待传入连接。

原型

int listen(int sockfd, int backlog);

参数

  • sockfd:套接字描述符。
  • backlog:连接队列的最大长度。

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

9.4 accept()

描述:接受一个传入的连接请求。

原型

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

参数

  • sockfd:监听套接字描述符。
  • addr:指向 struct sockaddr 结构体的指针,用于存储客户端地址。
  • addrlen:指向地址长度的指针。

返回值:成功时返回新的连接套接字描述符,失败时返回 -1 并设置 errno

9.5 connect()

描述:连接到远程套接字。

原型

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数

  • sockfd:套接字描述符。
  • addr:指向 struct sockaddr 结构体的指针,包含服务器地址和端口。
  • addrlen:地址结构的长度。

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

9.6 send() 和 recv()

描述:发送和接收数据。

原型

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数

  • sockfd:套接字描述符。
  • buf:指向数据缓冲区的指针。
  • len:数据长度。
  • flags:发送或接收标志,如 0MSG_DONTWAIT 等。

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

9.7 sendto() 和 recvfrom()

描述:用于无连接套接字(如 SOCK_DGRAM),发送和接收数据,同时指定目标地址。

原型

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                struct sockaddr *src_addr, socklen_t *addrlen);

参数

  • sockfd:套接字描述符。
  • buf:指向数据缓冲区的指针。
  • len:数据长度。
  • flags:发送或接收标志。
  • dest_addr / src_addr:目标或源地址。
  • addrlen:地址结构的长度。

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

9.8 close()

描述:关闭套接字。

原型

int close(int fd);

参数

  • fd:文件描述符(套接字描述符)。

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

9.9 setsockopt() 和 getsockopt()

描述:设置和获取套接字选项。

原型

int setsockopt(int sockfd, int level, int optname,
               const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname,
               void *optval, socklen_t *optlen);

参数

  • sockfd:套接字描述符。
  • level:选项的协议层次,如 SOL_SOCKET
  • optname:选项名称,如 SO_REUSEADDR
  • optval:指向选项值的指针。
  • optlen:选项值的长度。

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

10. 总结

套接字编程为网络通信提供了强大而灵活的接口。通过理解和正确使用套接字的各种参数,如协议族(PF_INET)、套接字类型(SOCK_RAW)、协议(IPPROTO_TCP)等,开发者可以构建各种类型的网络应用,从简单的客户端-服务器模型到复杂的自定义协议实现。

本文详细介绍了套接字的主要参数及其使用方法,并通过具体的编程示例展示了如何创建和使用不同类型的套接字。掌握这些基础知识,将为深入学习和开发网络应用打下坚实的基础。


希望本文能够帮助您全面理解套接字的各种参数与使用方法。如果有更多问题或需要进一步的示例,请随时提问!


评论