Linux/Unix系统编程手册--SOCKET章节读书笔记

SOCKET章节读书笔记

强烈推荐Linux/Unix系统编程手册,号称超越APUE的神书。

backlog含义

#include 
int listen(int socketfd, int backlog)

backlog参数限制未决连接(未accept)的数量,在这个数量之内,connect会立刻成功
Linux上上限为128,定义在

udp已连接socket

udp socket也是可以调用connect()的,这种叫已连接socket,内核会记录这个socket的对等socket的地址。
当一个udp socket已连接之后:

1.数据包发送可使用write或send,会自动发送到对等socket上,与sendto一样,每个write会发送一个独立的数据包
2.在这个socket上只能读取对等socket发送的数据**

零时端口

未调用bind(),tcp/udp会分配一个零时端口(tcp服务器也可以不调用bind)
可通过/proc/sys/net/ipv4/ip_local_port_range来修改范围,一般做高并发测试时,测试机可能需要修改以支持多并发。

UDP避免IP分段

一般来说,UDP会采用保守的方法来避免IP分段,即确保传输的IP数据包的大小小于IPv4组缓冲区576字节。8自己udp头部,至少需要20字节存放IP头,剩下548字节存放UDP数据包。实践中,一般会选择512来存放数据包。

shutdown与close区别

SHUT_WR被称为半关闭套接字,通过文件结尾来通知对端本地的写端已经关闭了。
shoutdown与close一个重要区别:无论该套接字上是否还关联其它的文件描述符,shotdown都会关闭套接字通道。
如sockfd指向一个已连接的流式套接字,执行以下调用,连接依然保持打开,仍然可以通过文件描述符fd2在该连接上做i/o操作。

fd2=dup(sockfd);
close(sockfd);

但如果执行以下调用,那么连接的双向通道都会关闭,通过fd2无法执行i/o操作.

fd2=dup(sockfd);
shutdown(sockfd,SHUT_RDWR);

如果套接字文件描述符fork()被复制,如果fork()后,一个进程在描述符副本上执行SHUT_RDWR操作,那么其它进程都无法在这个文件描述符上执行i/o操作了。

需要注意的是,shutdown并不会关闭文件描述符,要关闭文件描述符,还需调用clsoe。

TCP_CORK套接字选项

HTTP中使用sendfile()发送文件,为提供带宽利用率,可使用TCP_CORK将HTTP首部给缓冲,与数据报文合并为一个报文。

int optval =1
setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval);
write(sockfd, ...); //write http headers
sendfile(sockfd,...); // send data
optval =0
setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));

TIME_WAIT状态

TIME_WAIT状态值出现在执行主动关闭的一端,迁移到CLOSED状态需要2MSL(报文最大生存时间)。MSL是IP报文在吃过TTL限制前可在网络中生存的最大估计时间。
Linux中TIME_WAIT将持续60s。
目的:

1.实现可靠的连接终止

如果最后发送的ACK丢失了,等待2MSL可以重新发送最后ACK;而如果主动关闭的一方不存在了,TCP协议将发送RST报文(RST会被解释为错误)

2.让老的重复的报文段在网络中过期失效,这样建立新的连接时将不再接受它们

当有TCP节点处于TIME_WAIT状态时是无法通过同样的IP和端口号重新建立新的连接的。SO_REUSEADDR选项可用来避免会遇到EADDRINUSE错误,同时仍然允许TIME_WAIT状态提供可靠性保证。

带外数据

send()和recv()需要制定MSG_OOB标志,当套接字接受到带外数据通知时,内核为套接字属主(通常为使用该套接字的进程)生成SIGURG信号。

带外数据是TCP套接字的一种特性,允许发送端将传送的数据标记为高优先级。TCP通过URG标志位来标示有紧急(带外)数据,并将紧急指针指向紧急数据,但TCP没有执行紧急数据长度,因此认为紧急数据只由一个字节组成。telnet,ftp等,都利用该特性来终止之前传送的命令。

s.send("hello",socket.MSG_OOB)

c.recv(100,socket.MSG_OOB) // "o"
c.recv(100) // "hell"

分散聚合IO

readv和writev可以一次系统调用读写多个非连续缓冲区

#include 
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt)
struct iovec{
void *iov_base; //buffer
size_t iov_len; //size of buffer
}

I/O多路复用,信号驱动,epoll

水平触发通知:

如果文件描述法上可以非阻塞的执行I/O系统调用,此时认为它已经就绪。所以每次不需要读取很多数据,多个描述符读取平衡。

边缘触发通知:

如果文件描述符自上次状态检查以来有了新的I/O活动,此时触发通知。由于只有新状态才通知,所以一次需要读取所有数据,不然会导致数据丢失,会导致其他描述符处于饥饿状态。

I/O模式 水平触发 边缘触发
select,poll yes
信号驱动I/O yes
epoll yes yes

select

#include 
#incldue 
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timevel *timeout)
 返回就绪(3个集合)的描述符数量,0 超时,-1 错误

exceptfds一般只会在下面两种情况发生:

1.连接到信包模式下的伪终端主设备上的从设备状态发生改变
2.tcp套接字接收到了带外数据

nfds是集合中最大文件描述符+1,目的是让select变得更有效率,内核不用检查大于这个值的描述符是否属于这个集合。

eopll

max_user_watches:每个用户可以注册到epoll实例上的文件描述符总数(/proc/sys/fs/epoll/max_user_watches,默认的上学值是根据系统可用内存计算)

可以研究研究https://github.com/cloudwu/socket-server,以及tornado源码,这两个都是水平触发的,边缘触发的可以研究nginx或golang,当然nginx更适合。

典型异步I/O读写:

int sz = write(fd, buffer, sz);
if (sz < 0) {
    switch(errno) {
    case EINTR:
        continue;
    case EAGAIN:
        return -1;
}

int n = read(fd, buffer, sz);
if (n<0) {
    switch(errno) {
    case EINTR:
        break;
    case EAGAIN:
        fprintf(stderr, "socket-server: EAGAIN capture.\n");
        break;
    default:
        // close when error
        return SOCKET_ERROR;
    }
    return -1;
}

配置参数

  1. 系统限制 /etc/sysctl.conf
    来自gopush群的分享
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
kernel.msgmnb = 1048576
kernel.msgmax = 1048576
kernel.shmmax = 68719476736
kernek.shmall = 4294967296
fs.file-max = 1048576
kernel.pid_max = 1048576
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_timestsmps = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_wmem = 4096 4096 1677216
net.ipv4.tcp_rmen = 4096 4096 1677216
net.ipv4.tcp_mem = 94500000 91500000 92700000
net.ipv4.tcp_max_orphans = 3276800
net.core.netdev_max_backlog = 32768
net.core.somaxconn = 32768
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
vm.overcommit_memory = 1
  1. 登陆用户限制 /etc/security/limits.conf
    soft是警告设置,hard是阀值(鸟哥私房菜)
* soft nofile 150000
* hard nofile 150000

如果不管可直接用ulimit -n 10000设置

你可能感兴趣的:(python,c++/c)