《TCP/IP网络编程》(第九章)套接字的多种选项

2024-05-24 10:52

本文主要是介绍《TCP/IP网络编程》(第九章)套接字的多种选项,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.套接字的多种可选项

在这里插入图片描述
SOL_SOCKET的选项是套接字相关的选项
IPPROTO_IP的可选项是IP协议相关的选项
IPPROTO_TCP的可选项是TCP协议相关的选项

接下来会简单介绍几个选项和用法

2.对选项信息进行读取和设置

使用getsockopt()setsockopt()进行读取和设置,语法如下:

Linux系统
getsockopt()读取套接字选项

int getsockopt(
int sockfd,           //sockfd:要查看的套接字的文件描述符。
int level,            //level:要查看的协议层,如SOL_SOCKET。
int optname,          //要查询的选项的名称。
void *optval,         //指向一个缓冲区,该缓冲区将接收查询的选项值。
socklen_t *optlen     //向optval缓冲区传递的缓冲大小。调用成功后,它会被设置为选项值的实际长度。
);  

setsockopt()设置套接字选项

int setsockopt(
int sockfd,            //sockfd:要设置的套接字文件描述符。
int level,             //level:要设置的协议层,如SOL_SOCKET。
int optname,           //optname:要设置的选项的名称。
const void *optval,    //optval:指向包含要设置的选项值的缓冲区。
socklen_t optlen       //optval缓冲区的长度。
);

Windows系统
getsockopt()读取套接字选项

int getsockopt(SOCKET s,int level,int optname,char *optval,int *optlen
);

setsockopt()设置套接字选项

int getsockopt(SOCKET s,int level,int optname,char *optval,int *optlen
);

3.SOL_SOCKET协议层中的SO_SNDBUFSO_RCVBUF

前者是输入缓冲大小的相关选项,后者是输出缓冲大小的相关选项
Linux系统

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(const char *message);int main(int argc, char *argv[]){int sock;int result;// 用于存储函数调用结果socklen_t optlen;// 用于存储选项长度int recvBufSize, sendBufSize;// 用于存储接收和发送缓冲区大小int buffer_value = 1024; // 我们想要设置的缓冲区大小// 创建套接字(示例中使用IPv4 TCP套接字)sock = socket(AF_INET, SOCK_STREAM, 0);if (!sock) {error_handling(" sock buliding error");return 1;}// 获取默认的接收缓冲区大小optlen = sizeof(recvBufSize);result = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&recvBufSize, &optlen);if (result) {error_handling("getsockopt SO_RCVBUF error");} else {printf("Default receive buffer size: %d\n", recvBufSize);}// 获取默认的发送缓冲区大小result = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufSize, &optlen);if (result) {error_handling("getsockopt SO_SNDBUF error");} else {printf("Default send buffer size: %d\n", sendBufSize);}// 设置接收缓冲区大小为1024result = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char*)&buffer_value, sizeof(buffer_value));if (result) {error_handling("setsockopt SO_RCVBUF error");} else {// 验证接收缓冲区大小设置result = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&recvBufSize, &optlen);if (result) {error_handling("get new SO_RCVBUF error");} else {printf("New receive buffer size: %d\n", recvBufSize);}}// 设置发送缓冲区大小为1024result = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&buffer_value, sizeof(buffer_value));if (result) {error_handling("setsockopt SO_SNDBUF error");} else {// 验证发送缓冲区大小设置result = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufSize, &optlen);if (result) {error_handling("get new SO_SNDBUF error");} else {printf("New send buffer size: %d\n", sendBufSize);}}return 0;}void error_handling(const char *msg){fputs(msg, stderr);fputc('\n', stderr);exit(1);
}

在这里插入图片描述

输出的结果和我们预设的不同,受限于系统,可能无法按照我们的要求100%实现,操作系统可能有自己的缓冲区大小限制,这些限制可能高于或低于您尝试设置的值。如果设置的值超出了系统允许的范围,系统会自动调整到允许的最大或最小值。
Windows系统

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>int main() {WSADATA wsaData;SOCKET sock = INVALID_SOCKET;int result;// 用于存储函数调用结果socklen_t optlen;// 用于存储选项长度int recvBufSize, sendBufSize;// 用于存储接收和发送缓冲区大小int buffer_value = 1024; // 我们想要设置的缓冲区大小// 初始化WinSockresult = WSAStartup(MAKEWORD(2,2), &wsaData);if (result != 0) {printf("WSAStartup failed: %d\n", result);return 1;}// 创建套接字(示例中使用IPv4 TCP套接字)sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sock == INVALID_SOCKET) {printf("Could not create socket: %ld\n", WSAGetLastError());WSACleanup();return 1;}// 获取默认的接收缓冲区大小optlen = sizeof(recvBufSize);result = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&recvBufSize, &optlen);if (result == SOCKET_ERROR) {printf("getsockopt failed for SO_RCVBUF: %d\n", WSAGetLastError());} else {printf("Default receive buffer size: %d\n", recvBufSize);}// 获取默认的发送缓冲区大小result = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufSize, &optlen);if (result == SOCKET_ERROR) {printf("getsockopt failed for SO_SNDBUF: %d\n", WSAGetLastError());} else {printf("Default send buffer size: %d\n", sendBufSize);}// 设置接收缓冲区大小为1024result = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char*)&buffer_value, sizeof(buffer_value));if (result == SOCKET_ERROR) {printf("setsockopt failed for SO_RCVBUF: %d\n", WSAGetLastError());} else {// 验证接收缓冲区大小设置result = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&recvBufSize, &optlen);if (result == SOCKET_ERROR) {printf("getsockopt failed for SO_RCVBUF: %d\n", WSAGetLastError());} else {printf("New receive buffer size: %d\n", recvBufSize);}}// 设置发送缓冲区大小为1024result = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&buffer_value, sizeof(buffer_value));if (result == SOCKET_ERROR) {printf("setsockopt failed for SO_SNDBUF: %d\n", WSAGetLastError());} else {// 验证发送缓冲区大小设置result = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufSize, &optlen);if (result == SOCKET_ERROR) {printf("getsockopt failed for SO_SNDBUF: %d\n", WSAGetLastError());} else {printf("New send buffer size: %d\n", sendBufSize);}}// 清理资源closesocket(sock);WSACleanup();system("pause");return 0;
}

在这里插入图片描述

4.Time-wait状态

在基于TCP的服务器端/客户端(2)中讲述“四次挥手的时,第四次挥手中,主动关闭方发送一个ACK作为回应,便会进入TIME_WAIT状态。
在这里插入图片描述
若没有TIME_WAIT状态,如果最后这条ACK消息在传输途中丢失了,未能传给主机B,则主机B就会重传FIN消息,而不是关闭连接,但此时主机A已经关闭连接无法收到消息,那么主机B将无法按照程序关闭连接。
但因为有TIME_WAIT状态,主机A就会收到重传的FIN消息,然后重新发送ACK消息给主机B,让主机B正常关闭连接。

但是TIME_WAIT状态也有缺点,如果需要尽快重启服务器,但因为网络不好,便会一直处于TIME_WAIT状态。
在这里插入图片描述
解决方法就是使用SO_REUSEADDR

5.SOL_SOCKET协议层中的SO_REUSEADDR

SO_REUSEADDR 是一个 socket 选项,允许一个 socket 绑定到一个已经在使用中的地址和端口上。当应用程序关闭处于 TIME_WAIT 状态的连接时,如果启用了 SO_REUSEADDR,新的 socket 可以立即重用该地址和端口,即使之前的连接仍在 TIME_WAIT 状态。
使用语法

int optval = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&optval, sizeof(optval));

当服务器端添加了这个代码后,即让服务器处于SO_REUSEADDR状态,即使由于网络问题,导致服务器无法正常关闭,也可以立刻重新运行服务器程序,让服务器重新绑定在之前的地址和端口上。

6.Nagle算法

TCP套接字默认使用Nagle算法,下图是极端例子下使用和不使用Nagle算法:
在这里插入图片描述
左图是使用Nagle算法,右图是不使用Nagle算法,这里再次强调,即使是不使用Nagle算法,数据传输也不是逐字传输,这里只是方便理解,极端化处理。

Nagle算法优点:

  • 减少网络拥塞: 通过减少小包的数量,可以降低网络拥塞,特别是在高延迟网络中。
  • 提高带宽利用率: 通过发送更大的数据包,可以更有效地利用可用带宽。

Nagle算法缺点:

  • 增加交互式应用的延迟: 对于需要快速响应的交互式应用(如远程登录或在线游戏),Nagle算法可能会导致感知上的延迟,因为它延迟了小数据包的发送。
  • 与某些应用协议不兼容: 一些应用协议依赖于小数据包的快速传输,Nagle算法可能会干扰这些协议的正常工作。

为了解决这些问题,TCP提供了一个称为TCP_NODELAY的选项,允许禁用Nagle算法。如果应用程序需要发送小的、需要快速确认的数据包,可以设置这个选项来禁用Nagle算法,从而允许立即发送数据包,不进行合并。

7.IPPROTO_TCP协议层中的TCP_NODELAY

TCP_NODELAY 是一个用于控制 TCP 连接中 Nagle 算法行为的套接字选项。默认情况下,Nagle 算法通过合并小的数据包来减少网络拥塞和提高带宽利用率,但这可能会导致交互式应用的延迟增加。

当启用 TCP_NODELAY 选项时,它会禁用 Nagle 算法的合并过程,允许立即发送小的数据包,而不需要等待更多的数据来填满一个最大报文段(MSS)。这可以减少延迟,特别是在需要快速响应的应用中,如远程登录(SSH)、在线游戏或任何需要实时通信的场景。

启动TCP_NODELAY

int optval = 1;// 启用 TCP_NODELAY ,禁用 Nagle 算法
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&optval, sizeof(optval)) 

查看状态

int opt_val;
socklent_ opt_len;
opt_len=sizeof(opt_val);
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, &opt_len)
//使用状态opt_val会保存为0,禁用状态则保存为1

这篇关于《TCP/IP网络编程》(第九章)套接字的多种选项的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/998200

相关文章

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

AOP编程的基本概念与idea编辑器的配合体验过程

《AOP编程的基本概念与idea编辑器的配合体验过程》文章简要介绍了AOP基础概念,包括Before/Around通知、PointCut切入点、Advice通知体、JoinPoint连接点等,说明它们... 目录BeforeAroundAdvise — 通知PointCut — 切入点Acpect — 切面

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全

Python开发简易网络服务器的示例详解(新手入门)

《Python开发简易网络服务器的示例详解(新手入门)》网络服务器是互联网基础设施的核心组件,它本质上是一个持续运行的程序,负责监听特定端口,本文将使用Python开发一个简单的网络服务器,感兴趣的小... 目录网络服务器基础概念python内置服务器模块1. HTTP服务器模块2. Socket服务器模块

Linux查询服务器系统版本号的多种方法

《Linux查询服务器系统版本号的多种方法》在Linux系统管理和维护工作中,了解当前操作系统的版本信息是最基础也是最重要的操作之一,系统版本不仅关系到软件兼容性、安全更新策略,还直接影响到故障排查和... 目录一、引言:系统版本查询的重要性二、基础命令解析:cat /etc/Centos-release详

C#异步编程ConfigureAwait的使用小结

《C#异步编程ConfigureAwait的使用小结》本文介绍了异步编程在GUI和服务器端应用的优势,详细的介绍了async和await的关键作用,通过实例解析了在UI线程正确使用await.Conf... 异步编程是并发的一种形式,它有两大好处:对于面向终端用户的GUI程序,提高了响应能力对于服务器端应

Go语言网络故障诊断与调试技巧

《Go语言网络故障诊断与调试技巧》在分布式系统和微服务架构的浪潮中,网络编程成为系统性能和可靠性的核心支柱,从高并发的API服务到实时通信应用,网络的稳定性直接影响用户体验,本文面向熟悉Go基本语法和... 目录1. 引言2. Go 语言网络编程的优势与特色2.1 简洁高效的标准库2.2 强大的并发模型2.

Linux之UDP和TCP报头管理方式

《Linux之UDP和TCP报头管理方式》文章系统讲解了传输层协议UDP与TCP的核心区别:UDP无连接、不可靠,适合实时传输(如视频),通过端口号标识应用;TCP有连接、可靠,通过确认应答、序号、窗... 目录一、关于端口号1.1 端口号的理解1.2 端口号范围的划分1.3 认识知名端口号1.4 一个进程