Socket 编程是网络编程的基础,它允许不同计算机或同一台计算机上的不同进程之间进行通信。理解和正确使用套接字(socket)的各种参数对于开发高效、可靠的网络应用至关重要。本文将详细讲解套接字的各种参数及其使用方法,包括协议族(如 PF_INET
)、套接字类型(如 SOCK_RAW
)等。
目录
1. 什么是套接字(Socket)
套接字(Socket)是应用程序与网络通信之间的中介,通过套接字,应用程序可以发送和接收数据。套接字抽象了网络通信的复杂性,提供了一组标准的接口,允许不同的进程或不同的计算机之间进行通信。
套接字通常用于实现以下协议:
- TCP(传输控制协议):面向连接,可靠的数据传输。
- UDP(用户数据报协议):无连接,不保证可靠性,适用于实时应用。
2. 套接字的主要参数
创建套接字时,需要指定几个关键参数,这些参数决定了套接字的行为和功能。主要参数包括协议族(Protocol Family)、套接字类型(Socket Type)和协议(Protocol)。
2.1 协议族(Protocol Family)
协议族(有时称为地址族)定义了套接字所使用的协议和地址类型。常见的协议族包括:
- PF_INET:用于 IPv4 网络通信。
- PF_INET6:用于 IPv6 网络通信。
- PF_UNIX 或 PF_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. 创建套接字
创建套接字的基本步骤如下:
- 调用
socket()
函数:创建一个新的套接字。 - 配置套接字选项(可选):使用
setsockopt()
设置套接字参数。 - 绑定套接字(服务器端):使用
bind()
将套接字绑定到特定的地址和端口。 - 监听连接(服务器端):使用
listen()
开始监听传入的连接。 - 接受连接(服务器端):使用
accept()
接受传入的连接。 - 连接套接字(客户端):使用
connect()
连接到服务器。 - 发送和接收数据:使用
send()
,recv()
,sendto()
,recvfrom()
等函数进行数据传输。 - 关闭套接字:使用
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 客户端套接字流程
- 创建套接字:调用
socket()
函数。 - 配置服务器地址:填充
struct sockaddr_in
结构体。 - 连接服务器:调用
connect()
函数。 - 发送和接收数据:使用
send()
,recv()
等函数。 - 关闭套接字:调用
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 服务器端套接字流程
- 创建套接字:调用
socket()
函数。 - 绑定套接字:调用
bind()
函数,将套接字绑定到特定的地址和端口。 - 监听连接:调用
listen()
函数,开始监听传入的连接。 - 接受连接:调用
accept()
函数,接受传入的连接。 - 发送和接收数据:使用
send()
,recv()
等函数。 - 关闭套接字:调用
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
:发送或接收标志,如0
、MSG_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
)等,开发者可以构建各种类型的网络应用,从简单的客户端-服务器模型到复杂的自定义协议实现。
本文详细介绍了套接字的主要参数及其使用方法,并通过具体的编程示例展示了如何创建和使用不同类型的套接字。掌握这些基础知识,将为深入学习和开发网络应用打下坚实的基础。
希望本文能够帮助您全面理解套接字的各种参数与使用方法。如果有更多问题或需要进一步的示例,请随时提问!