文章目录
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模型:
- 阻塞式I/O
- 非阻塞式I/O
- I/O服用(select、poll)
- 信号驱动式I/O(SIGIO)
- 异步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