Linux socket 编程中存在的五个隐患

Linux:线程安全 Linux:线程安全从抢票说起二级目录三级目录 从抢票说起 我们先写一个程序来模拟一下抢票:创建4个线程(代表4个人),票数为100(全局变量int g_tickets = 100;),每个执行流执行之后g_tickets减一,代表抢到了票,后面打印出抢到了第几张票。 #include <stdio.h> #include <pthr 阅读详情
Socket API 是网络应用程序开发中实际应用的标准 API。尽管该 API 简单,但是
  
  开发新手可能会经历一些常见的问题。本文识别一些最常见的隐患并向您显示如何避免它们。 相关文档:《linux socket 编程》
  在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系统的标准特性。事实上,很难找到一种不支持 Sockets API 的现代语言。该 API 相当简单,但新的开发人员仍然会遇到一些常见的隐患。
  本文识别那些隐患并向您显示如何避开它们。
  隐患 1.忽略返回状态
  第一个隐患很明显,但它是开发新手最容易犯的一个错误。如果您忽略函数的返回状态,当它们失败或部分成功的时候,您也许会迷失。反过来,这可能传播错误,使定位问题的源头变得困难。
  捕获并检查每一个返回状态,而不是忽略它们。考虑清单 1 显示的例子,一个套接字 send 函数。
  清单 1. 忽略 API 函数返回状态
  
   int status, sock, mode;
  
   /* Create a new stream (TCP) socket */
   sock = socket( AF_INET, SOCK_STREAM, 0 );
   ...status = send( sock, buffer, buflen, MSG_DONTWAIT );
   if (status == -1)
   { /* send failed */
   printf( "send failed: %s/n",?
   strerror(errno) );
   }
   else
   { /* send succeeded -- or did it? */}
   
  清单 1 探究一个函数片断,它完成套接字 send 操作(通过套接字发送数据)。函数的错误状态被捕获并测试,但这个例子忽略了 send 在无阻塞模式(由 MSG_DONTWAIT 标志启用)下的一个特性。
  send API 函数有三类可能的返回值:
   如果数据成功地排到传输队列,则返回 0。
   如果排队失败,则返回 -1(通过使用 errno 变量可以了解失败的原因)。
   如果不是所有的字符都能够在函数调用时排队,则最终的返回值是发送的字符数。
  由于 send 的 MSG_DONTWAIT 变量的无阻塞性质,函数调用在发送完所有的数据、一些数据或没有发送任何数据后返回。在这里忽略返回状态将导致不完全的发送和随后的数据丢失。
  
  隐患 2.对等套接字闭包
  UNIX 有趣的一面是您几乎可以把任何东西看成是一个文件。文件本身、目录、管道、设备和套接字都被当作文件。这是新颖的抽象,意味着一整套的 API 可以用在广泛的设备类型上。
  考虑 read API 函数,它从文件读取一定数量的字节。read 函数返回读取的字节数(最高为您指定的最大值);或者 -1,表示错误;或者 0,如果已经到达文件末尾。
  如果在一个套接字上完成一个 read 操作并得到一个为 0 的返回值,这表明远程套接字端的对等层调用了 close API 方法。该指示与文件读取相同 —— 没有多余的数据可以通过描述符读取(参见 清单 2)。
  清单 2.适当处理 read API 函数的返回值
  
   int sock, status;
   sock = socket( AF_INET, SOCK_STREAM, 0 );
   ...status = read( sock, buffer, buflen );
   if (status > 0)
   { /* Data read from the socket */}
   else if (status == -1)
   {
   /* Error, check errno, take action... */
   }
   else if (status == 0)
   {
   /* Peer closed the socket, finish the close */
   close( sock );
   /* Further processing... */
   }
  
  
  
  同样,可以用 write API 函数来探测对等套接字的闭包。在这种情况下,接收 SIGPIPE 信号,或如果该信号阻塞,write 函数将返回 -1 并设置 errno 为 EPIPE。
  
  隐患 3.地址使用错误(EADDRINUSE)
  您可以使用 bind API 函数来绑定一个地址(一个接口和一个端口)到一个套接字端点。可以在服务器设置中使用这个函数,以便限制可能有连接到来的接口。也可以在客户端设置中使用这个函数,以便限制应当供出去的连接所使用的接口。bind 最常见的用法是关联端口号和服务器,并使用通配符地址(INADDR_ANY),它允许任何接口为到来的连接所使用。
  bind 普遍遭遇的问题是试图绑定一个已经在使用的端口。该陷阱是也许没有活动的套接字存在,但仍然禁止绑定端口(bind 返回 EADDRINUSE),它由 TCP 套接字状态 TIME_WAIT 引起。该状态在套接字关闭后约保留 2 到 4 分钟。在 TIME_WAIT 状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。
  等待 TIME_WAIT 结束可能是令人恼火的一件事,特别是如果您正在开发一个套接字服务器,就需要停止服务器来做一些改动,然后重启。幸运的是,有方法可以避开 TIME_WAIT 状态。可以给套接字应用 SO_REUSEADDR 套接字选项,以便端口可以马上重用。
  考虑清单 3 的例子。在绑定地址之前,我以 SO_REUSEADDR 选项调用 setsockopt。为了允许地址重用,我设置整型参数(on)为 1 (不然,可以设为 0 来禁止地址重用)。
  清单 3.使用 SO_REUSEADDR 套接字选项避免地址使用错误
  
   int sock, ret, on;struct sockaddr_in servaddr;
   /* Create a new stream (TCP) socket */
   sock = socket( AF_INET, SOCK_STREAM, 0 ):
   /* Enable address reuse */
   on = 1;
   ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR,
   &on, sizeof(on) );
   /* Allow connections to port 8080 from any available interface
   */
   memset( &servaddr, 0, sizeof(servaddr) );
   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
   servaddr.sin_port = htons( 45000 );
   /* Bind to the address (interface/port) */
   ret = bind( sock, (struct sockaddr *)&servaddr, sizeof(servaddr) );
 
  在应用了 SO_REUSEADDR 选项之后,bind API 函数将允许地址的立即重用。
  
  隐患 4.发送结构化数据
  套接字是发送无结构二进制字节流或 ASCII 数据流(比如 HTTP 上的 HTTP 页面,或 SMTP 上的电子邮件)的完美工具。但是如果试图在一个套接字上发送二进制数据,事情将会变得更加复杂。
  比如说,您想要发送一个整数:您可以肯定,接收者将使用同样的方式来解释该整数吗?运行在同一架构上的应用程序可以依赖它们共同的平台来对该类型的数据做出相同的解释。但是,如果一个运行在高位优先的 IBM PowerPC 上的客户端发送一个 32 位的整数到一个低位优先的 Intel x86,那将会发生什么呢?字节排列将引起不正确的解释。
  通过套接字发送一个 C 结构会怎么样呢?这里,也会遇到麻烦,因为不是所有的编译器都以相同的方式排列一个结构的元素。结构也可能被压缩以便使浪费的空间最少,这进一步使结构中的元素错位。
  幸好,有解决这个问题的方案,能够保证两端数据的一致解释。过去,远程过程调用(Remote Procedure Call,RPC)套装工具提供所谓的外部数据表示(External Data Representation,XDR)。XDR 为数据定义一个标准的表示来支持异构网络应用程序通信的开发。
  现在,有两个新的协议提供相似的功能。可扩展标记语言/远程过程调用(XML/RPC)以 XML 格式安排 HTTP 上的过程调用。数据和元数据用 XML 进行编码并作为字符串传输,并通过主机架构把值和它们的物理表示分开。SOAP 跟随 XML-RPC,以更好的特性和功能扩展了它的思想。参见参考资料小节,获取更多关于每个协议的信息。
  
  隐患 5.TCP 中的帧同步假定
  TCP 不提供帧同步,这使得它对于面向字节流的协议是完美的。这是 TCP 与 UDP(User Datagram Protocol,用户数据报协议)的一个重要区别。UDP 是面向消息的协议,它保留发送者和接收者之间的消息边界。TCP 是一个面向流的协议,它假定正在通信的数据是无结构的,如图 1 所示。
  图 1.UDP 的帧同步能力和缺乏帧同步的 TCP
  图 1 的上部说明一个 UDP 客户端和服务器。左边的对等层完成两个套接字的写操作,每个 100 字节。协议栈的 UDP 层追踪写的数量,并确保当右边的接收者通过套接字获取数据时,它以同样数量的字节到达。换句话说,为读者保留了写者提供的消息边界。
  现在,看图 1 的底部.它为 TCP 层演示了相同粒度的写操作。两个独立的写操作(每个 100 字节)写入流套接字。但在本例中,流套接字的读者得到的是 200 字节。协议栈的 TCP 层聚合了两次写操作。这种聚合可以发生在 TCP/IP 协议栈的发送者或接收者中任何一方。重要的是,要注意到聚合也许不会发生 —— TCP 只保证数据的有序发送。
  对大多数开发人员来说,该陷阱会引起困惑。您想要获得 TCP 的可靠性和 UDP 的帧同步。除非改用其他的传输协议,比如流传输控制协议(STCP),否则就要求应用层开发人员来实现缓冲和分段功能。
  调试套接字应用程序的工具
  GNU/Linux 提供几个工具,它们可以帮助您发现套接字应用程序中的一些问题。此外,使用这些工具还有教育意义,而且能够帮助解释应用程序和 TCP/IP 协议栈的行为。在这里,您将看到对几个工具的概述。查阅下面的 参考资料 了解更多的信息。
  查看网络子系统的细节
  netstat 工具提供查看 GNU/Linux 网络子系统的能力。使用 netstat,可以查看当前活动的连接(按单个协议进行查看),查看特定状态的连接(比如处于监听状态的服务器套接字)和许多其他的信息。清单 4 显示了 netstat 提供的一些选项和它们启用的特性。
  清单 4.netstat 实用程序的用法模式 
  
   View all TCP sockets currently active$ netstat
   --tcpView all UDP sockets$ netstat
   --udpView all TCP sockets in the listening state$ netstat
   --listeningView the multicast group membership information$ netstat
   --groupsDisplay the list of masqueraded connections$ netstat
   --masqueradeView statistics for each protocol$ netstat
   --statistics 
  
  尽管存在许多其他的实用程序,但 netstat 的功能很全面,它覆盖了 route、ifconfig 和其他标准 GNU/Linux 工具的功能。
  监视流量
  可以使用 GNU/Linux 的几个工具来检查网络上的低层流量。tcpdump 工具是一个比较老的工具,它从网上“嗅探”网络数据包,打印到 stdout 或记录在一个文件中。该功能允许查看应用程序产生的流量和 TCP 生成的低层流控制机制。一个叫做 tcpflow 的新工具与 tcpdump 相辅相成,它提供协议流分析和适当地重构数据流的方法,而不管数据包的顺序或重发。清单 5 显示 tcpdump 的两个用法模式。
  清单 5.tcpdump 工具的用法模式 
  
   Display all traffic on the eth0 interface for
   the local host$ tcpdump -l -i eth0Show all traffic
   on the network coming from or going
   to host plato$ tcpdump host platoShow all HTTP traffic
   for host camus$ tcpdump host camus and (port http)View
   traffic coming from or going
   to TCP port 45000 on the local host$ tcpdump tcp port 45000
 
  tcpdump 和 tcpflow 工具有大量的选项,包括创建复杂过滤表达式的能力。查阅下面的参考资料 获取更多关于这些工具的信息。
  tcpdump 和 tcpflow 都是基于文本的命令行工具。如果您更喜欢图形用户界面(GUI),有一个开放源码工具 Ethereal 也许适合您的需要。Ethereal 是一个专业的协议分析软件,它可以帮助调试应用层协议。它的插入式架构(plug-in architecture)可以分解协议,比如 HTTP 和您能想到的任何协议(写本文的时候共有 637 个协议)。
【Qt】QLocalSocket与QLocalServer问题:接收不到数据、只能收到第一条、数据不完整解决方案【2023.05.24】 Qt很强大,但是Qt的帮助文档、API属实是让我们走不少弯路。QLocalSocket一个很简单的东西,我仅想用来实现一个简单的本地进程通信,就遇到了:客户端循环发送数据,服务端只能接收到一条、接收到数据不完整等奇奇怪怪的现象。 阅读详情

相关推荐

Docker+vLLM内网离线部署Qwen3 流程

本文介绍了在CentOS 7系统下进行VLLM容器化部署Qwen3-32B大模型的内网离线方案。首先需准备Nvidia显卡驱动、CUDA 12.4和Docker环境。通过联网机器拉取vllm/vllm-openai镜像并导入内网服务器,同时从魔塔社区下载模型文件。部署时使用docker run命令启动容器,配置了GPU资源、内存共享、端口映射等参数,特别指定了模型路径、并行计算、内存利用率等关键参数。该方案实现了大模型在内网环境的安全高效部署,适用于需要离线运行的AI推理场景。

qq_42881308的博客 1003

Linux进程间通信(九):数据报套接字 socket()、bind()、sendto()、recvfrom()、close()...

前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用。 一、简单回顾——什么是数据报套接字 socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计...

weixin_34324081的博客 399

莆田市C++专项选拔第一轮题4

现在小明同学想要从两堆硬币中各选择一枚硬币,使得两枚硬币的面值之和 不超过 k。不同的硬币组合,就算面值相同,也视为不同的方案。现在他把这些硬币分成了两堆,已知两堆硬币分别有 n 和 m 枚硬币。可选方案有: (1,2), (1,1), (1,1), (5,2), (5,1), (5,1),共 6 种。第一行三个整数 n, m, k,分别表示第一堆硬币的数量,第二堆硬币的数量,以及面值之和的上限。对于30%的数据,1 ≤ n , m ≤ 1000 , 1 ≤ b , c ≤ 1000。

lpstudio的博客 899

关于TCP/UDP 是否多线程安全_2019.12.27

对于 UDP,多线程读写同一个 socket 不用加锁,不过更好的做法是每个线程有自己的 socket,避免 contention,可以用 SO_REUSEPORT 来实现这一点。 对于 TCP,通常多线程读写同一个 socket 是错误的设计,因为有 short write 的可能。假如你加锁,而又发生 short write,你是不是要一直等到整条消息发送完才解锁(无论阻塞IO还是非阻塞IO)?如果这样,你的临界区长度由对方什么时候接收数据来决定,一个慢的 peer 就把你的程序搞死了。 总结:对于

虫字的博客 1312

linux下tcp多线程send函数引发的命案

”原来tcp的send函数不是线程安全的“。这句话就是我对这两周的总结。 首先我来说明下情况,本人在做一个分布式并行计算框架,之前系统运作正常,可是当我将任务包的大小调整到MB级别以后,问题就来了,老是出现master节点收到的部分结果包接收不正常。举例来说,master假...

chuyike4671的博客 1265

sendto linux非阻塞,假设socket.sendto是非阻塞操作是否安全?

不,你不能指望 socket.sendto 是无阻塞的 .将数据字节发送到addr给出的远程对等体(传输 - >依赖目标地址) . 如果addr为None,则将数据发送到创建传输时给定的目标地址 . 这种方法不会阻止;它缓冲数据并安排它以异步方式发送出去 .transport, protocol = await loop.create_datagram_endpoint(factory, s...

weixin_42162216的博客 435

linux的write是线程安全的吗,socket的write/send还是是否是线程安全?

在多线程的网络服务器程序中, 对同一个客户端多线程同时发送数据是经常可能发生的事情, 也就是有可能会多线程的对一个fd调用send/write, 那么这种操作是否需要加锁?并发写套接字是否导致系统缓冲区数据混乱呢? 网上搜了下,有人说可以写,有人说不能,linux man page也没有说明。 看来需要写程序测试。 写了个server的代码进行测试。10个线程同时对一个fd进行write, 看看客...

weixin_39616880的博客 730

Linux系统绑定sock失败解决,LinuxSocket编程中遇到的问题及解决办法

概述:在学习linuxsocket编程中,我遇到了一些问题和自己感觉比较重要的一些知识点,这边做一个总结,当作是学习笔记,也算是一个记录,以便以后翻阅吧。问题及要点:(1)bind error : Address already in use .地址绑定错误问题。(2)大端小端字节序,网络字节序。(3)URL(域名)转化问题。(4)读写函数read(),write()返回值问题。(5)非阻塞下c...

weixin_31708613的博客 1617

SUID & SGID LINUX 权限安全设置

于用户在UNIX下经常会遇到SUID、SGID的概念,而且SUID和SGID涉及到系统安全,所以用户也比较关心这个问题。关于SUID、 SGID的问题也经常有人提问,但回答的人一般答得不够详细,加上曾经回答过两个网友的问题,还查了一些资料,决定整理成本文,以供大家参考。限于本人的水平问题,文章中如果有不当之处,请广大网友指正。 一、UNIX下关于文件权限的表示方法和解析 SUI

大鹏 1661

Linux 套接字编程中的 5 个隐患

M. Tim Jones , 资深软件工程师, Emulex2005 年 10 月 08 日在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系统的标准特性。事实上,很难找到一种不支持 Sockets API 的现代语言。该 API 相当简单,但新的开发人员仍然会遇到一些常见的隐患。本文识别那些隐患并向您显示如何避开它们。隐患 1.忽略返回状态第一个缺陷很明

baggio785的专栏 1959

工作小记: sendto失败 errno 22 / SO_REUSEADDR SO_REUSEPROT

sentto

饭真好吃的博客 2927

基于MSP430单片机的线阵LED图文显示系统设计.pdf

基于MSP430单片机的线阵LED图文显示系统设计.pdf

上一篇: 浅析gethostbyname函数
下一篇: 网络socket编程指南
hello_wyq
博客等级 码龄26年 130粉丝 162原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值