Unix网络编程

本文深入解析TCP/IP协议的工作原理,包括三次握手、四次挥手、TIME_WAIT状态的必要性,以及UDP与TCP的区别。涵盖了网络编程中的关键概念,如套接字编程、I/O多路复用(select, poll, epoll),并介绍了常见的网络编程命令,如ping、telnet、nc、tcpdump、lsof、netstat和ifconfig。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


sprintf -> snprintf
gets -> fgets
strcat -> strncat -> strlcat
strcpy -> strncpy -> strlcpy

UDP是一个简单的、不可靠的数据报协议。应用于traceroute、DHCP()、NTP(时间协议)、DNS、NFS、RIP(路由协议)等。
TCP是一个复杂的、可靠的字节流协议。100%可靠提供的是数据的可靠传递或故障的可靠通知。应用于SMTP、Telnet、SSH、FTP、HTTP、BGP等。

SYN、FIN都各占据一个字节的序列号空间,ACK不占。

为什么需要三次握手,而不是两次?

因为TCP通信双方需要维护一个序列号并得到对方的确认。

为什么需要四次挥手?

因为A方发送FIN后,B方先回复ACK,此时B方可能还有数据需要发送,等自己发完才发送FIN。

TIME_WAIT

在这里插入图片描述
为什么需要TIME_WAIT状态?

  • 可靠地实现TCP全双工连接的终止:正确地处理连接终止序列的4个分节中任何一个分节丢失的情况。
  • 允许老的重复分节在网络中消逝

为什么是主动关闭的一端存在TIME_WAIT状态?

因为可能不得不重传最终那个ACK的就是那一端。
因为这边可能要等着给对方重传ACK。如果这边发完ACK不等就直接关闭的话,如果ACK丢失,那么对方重传FIN后就会得到一个RST,对方会认为除了错误。

TCP连接状态转换图:
在这里插入图片描述

MSL:最长分节生命期, 30s–2min。
是任何IP数据报能够在Internet中存活的最长时间。
TIME_WAIT的持续时间是2MSL。

MTU:不对称

DF:该字段可用于路径MTU发现,不过现在问题在于很多防火墙丢弃ICMP消息

tcpdump直接与数据链路层进行通信。

套接字编程

地址和端口号用网络字节序存储。 大端字节序。
通用套接字地址结构:struct sockaddr和新的struct sockaddr_storage(可容纳系统所支持的任何套接字地质结构)。
因为套接字地址结构有的长度不定(如Unix域结构、数据链路结构),因此需要传递长度。
IPv4是16字节、地址族是AF_INET,IPv6是28字节、地址族是AF_INET6。长度和地址族各一个字节。
大小端字节序:表示多个字节值的哪一端存放在起始地址处(低内存地址)

字节序转换函数

#include <netinet/in.h>
// host net short long  
uint16t htons(uint16_t host16bitvalue);
uint32t htonl(uint32_t host32bitvalue);  //返回网络字节序
uint16t ntohs(uint16_t net16bitvalue);
uint32t ntohs(uint32_t net32bitvalue);   //返回主机字节序

地址转换函数(ASCII字符串和网络字节序二进制的转换)

#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr); //yes->1, wrong->0, no->-1
const char* inet_ntop(int family, const void *addrptr, char *strptr, size_t len); //yes->ptr, no->NULL

字节流套接字上的read、write函数所表现的行为不用于通常文件的IO,因为套接字的缓冲区可能满了,以致于读或写的字节少于请求的,此时需要反复调用这些函数直到读完。

connect函数可能返回的错误类型;

  • ETIMEOUT:发送SYN后没有回应,超时
  • ECONNREFUSED:对方回应RST
  • EHOSTUNREACH或ENETUNREACH:不可达

bind函数将一个本地协议地址赋给一个套接字。 P80

listen函数将一个主动套接字转为一个被动套接字(监听套接字)
第二个参数制定内核应该为相应套接字排队的最大连接数,因实现不同各异。
内核为任何一个监听套接字维护两个队列

  • 未完成连接队列:收到SYN就将这个连接放进去
  • 已完成连接队列:收到去人的ACK后再将这个连接从未完成队列转移过来。
    后续调用accept时从已完成连接队列中取出一个连接,如果队列为空就阻塞。

TCP连接终止的几种情况分析

P102

  • 正常终止:客户端发FIN,进入TIME_WWAIT,然后等待正常结束。
    accept返回前连接终止:客户端发送RST
  • 服务器进程终止:客户端收到FIN回应ACK,阻塞在fgets上,然后向服务端wirte,服务端返回RST,客户端readline返回EOF(因为之前收到了FIN)
  • 写一个已经接收了RST的套接字会引起错误,broken pipe
  • 服务器崩溃,客户端发的消息收不到ACK,引起ETIMEOUT,如果中间的路由器判定不可达,引起ICMP报告destination unreachable,错误则是ENETUNREACH或EHOSTUNREACH
  • 服务器主机崩溃后重启:客户端发送消息,服务端回应RST,ECONNRESET
  • 服务器关机:同服务器进程终止

信号

  • 信号就是告知某个进程发生了某个事件的通知,信号通常是异步发生的。
  • 可以由一个进程发给另一个进程、也可以由内核发给进程。
  • Unix信号默认不排队,如果一个信号在阻塞期间产生了一次或者多次,当该信号的阻塞解除之后通常只递交一次。

I/O多路复用

Unix下的物种IO模型:

  1. 阻塞式I/O
  2. 非阻塞式I/O
  3. I/O服用(select、poll)
  4. 信号驱动式I/O(SIGIO)
  5. 异步I/O

其中前四种都是同步I/O,导致请求进程阻塞,直到I/O操作完成。
第五种不导致请求进城阻塞。
具体区别见下图:
在这里插入图片描述

select

  • 每次检查可用fd复杂度O(N)
  • 每次循环都要重置状态
  • 几乎所有的类Unix系统都支持
#include <sys/select.h>
#include <sys/time.h>
int select(int maxdfpl,  \\制定待测试的描述符个数。是待测试的最大描述符加1,最大1024,加这个参数是为了效率(不必每次都遍历全部)
           fd_set *readset, 
           fd_set *weiteset,
           fd_set *exceptset, 
           const struct timeval *timeout \\等待时间,具体又分三种情况:NULL永远等下去,等一段时间,不等直接返回
           );

struct timeval{ 
    long tv_sec;  //seconds
    long tv_usec; //microseconds
}

如果三个fd_set都为空,那么就可以实现更精确的定时器,以微妙为单位。sleep是以秒为单位。

select不知道内部的缓冲,可能造成一些问题。

pselect

//TODO

#include <sys/select.h>
#include <signal.h>
#include <time.h>
int select(int maxdfpl,
           fd_set *readset, 
           fd_set *weiteset,
           fd_set *exceptset, 
           const struct timespec *timeout,
           const sigset_t *sugmask  //TODO
           );
struct timeval{ 
    long tv_sec;  //seconds
    long tv_usec; //nanoseconds
}

poll

  • 不需要自己计算最大描述符+1
  • 也要遍历,不过只用遍历socket,而不是select那样要遍历到最大的fd+1
  • 对复用数量没有限制(select是1024=32*32)
  • 有些系统不支持
#include <poll.h>
int poll (struct pollfd *fds,
          unsigned int nfds, //数组长度 
          int timeout);  //超时;INFTIM永远等待、0立即返回、>0等待毫秒数(实际与select一样受限于系统实现的时钟分辨率,通常10ms)

struct pollfd {
      int fd; //需要检查的描述符
      short events;   //等待事件(感兴趣事件) 
      short revents;  //发生的事件, 可选事件参考P144
};

epoll

  • select和poll都是应用程序轮询是否可读、可写,epoll是操作系统负责通知,不用轮询,因此最高效

Epoll vs Select/Poll

  • We can add and remove file descriptor while waiting
  • epoll_wait returns only the objects with ready file descriptors
  • epoll has better performance – O(1) instead of O(n)
  • epoll can behave as level triggered or edge triggered (see man page)
  • epoll is Linux specific so non portable

参考:https://devarea.com/linux-io-multiplexing-select-vs-poll-vs-epoll/#.XLF88bzE83l

shutdown

close()是同时关闭了套接字的读和写,有时候我们已经发完了数据,不过对方还有数据要发过来,我们还需要等待接收数据,这时就需要shutdown().
而且close()只有在引用计数为0时才会关闭该套接字描述符,shutdown()则不考虑引用计数直接关闭。

#include <sys/socket.h>
int shutdown(int sockfd, int howto); 
\\howto 
\\ 0 SHUT_RD  关闭读,套接字缓冲区的数据也丢弃,不过会回发确认
\\ 1 SHUT_WR  关闭写,不能再向套接字缓冲区写,不过缓冲区的数据仍会发出
\\ 2 SHUT_RDWR 
#include <unistid.h>
unsigned int sleep(unisigned int secs); \\要休眠的秒数,或者被信号中断过早返回剩下的秒数
int pause(void); //等待信号  

Linux网络编程常用命令

ping

ping [ip地址]:判断网络是否通

telnet

telnet [ip地址] [端口号]:判断指定ip的某端口是否开放

nc

据说是网络工具中的瑞士军刀。
nc -l [端口号]:作为服务器
nc [id地址] [端口号]:作为客户端

文件传输(如果传输目录就先压缩一下):

  • nc -l [端口号] < FILE.NAME
  • nc -n [IP地址] [端口号] > FILE.NAME

tcpdump

抓包工具。
参考:https://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html
tcpdump:监视第一个网络接口上所有流过的数据包
tcpdump -i [网路接口]:监视指定接口
tcpdump tcp port 23 and host 123.234.234.123:截获特定主机端口的tcp包
-w file.cap:保存成cap文件,便于后续分析

  • -c:指定抓多少个包
  • -t:不显示时间戳

lsof

参考:https://www.cnblogs.com/peida/archive/2013/02/26/2932972.html
list opened files.

  • -c [进程名]:列出指定进程打开的文件
  • -i [条件]:列出符合条件的进程
  • -p [进程号]:指定进程号的文件
  • -a:列出打开文件所在的进程

netstat

检验本机各端口的网络连接情况。

发现网络细节的基本命令  
netstat
-i: interface
-r: route table
-n: number, not name

https://www.cnblogs.com/peida/archive/2013/03/08/2949194.html

  • LISTEN:侦听来自远方的TCP端口的连接请求
  • SYN-SENT:再发送连接请求后等待匹配的连接请求(如果有大量这样的状态包,检查是否中招了)
  • SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认(如有大量此状态,估计被flood攻击了)
  • ESTABLISHED:代表一个打开的连接
  • FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认
  • FIN-WAIT-2:从远程TCP等待连接中断请求
  • CLOSE-WAIT:等待从本地用户发来的连接中断请求
  • CLOSING:等待远程TCP对连接中断的确认
  • LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认(不是什么好东西,此项出现,检查是否被攻击)
  • TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认
  • CLOSED:没有任何连接状态

ifconfig

发现网络细节的基本命令  
ifconfig interface_name
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值