一.网络编程配置:
修改UDP接收发送缓冲区大小:
1、修订单个socket的缓冲区大小:通过setsockopt使用SO_RCVBUF来设置接收缓冲区,该参数在设置的时候不会与rmem_max进行对比校验,但是如果设置的大小超过rmem_max的话,则超过rmem_max的部分不会生效;
发包缓冲区与收包缓冲区一样,只不过参数名称不一样:SO_SNDBUF、wmem_max。
2、修订linux系统udp缓冲区大小:通过rmem_max来设置系统中udp缓存的上限,该值可通过如下方式查看:
root@ubuntu:/mnt/hgfs/vm-shared/socket# cat /proc/sys/net/core/rmem_max
通过如下方式进行修订:
root@ubuntu:/mnt/hgfs/vm-shared/socket# vi /etc/sysctl.conf
在文件/etc/sysctl.conf中新增如下信息:
rmem_max=MAX
需要注意的这里设置的rmem_max参数是整个系统的大小,不是单个socket的大小。
修订后的大小可以通过如下命令查看:
root@ubuntu:/mnt/hgfs/vm-shared/socket# sysctl -a | grep rmem_max
net.core.rmem_max = 131071
修改udp缓冲区的大小
cat /proc/sys/net/core/rmem_default // 默认发送缓冲区
echo 65536 > /proc/sys/net/core/wmem_default // 默认接收缓冲区
总结
要修订linux udp收包缓冲大小,需要上述两个地方同时修改。
只改第1点,缓冲区大小会受到rmem_max的限制。
只改第2点,一个socket只会预留63个报文的接收缓冲。
修改TCP接收发送缓冲区大小:
修改tcp接收/发送缓冲区最大值
echo 65536 > /proc/sys/net/core/rmem_max
echo 256960 > /proc/sys/net/core/wmem_max
修改tcp接收/发送缓冲区,一般是最大值的一半
echo "4096 32768 65536" > /proc/sys/net/ipv4/tcp_rmem // 接收缓冲区
echo "4096 65536 256960" > /proc/sys/net/ipv4/tcp_wmem // 发送缓冲区
修改网络设备接收队列
echo 500 > /proc/sys/net/core/netdev_max_backlog
重传次数
echo 5 > /proc/sys/net/ipv4/tcp_retries2
通过函数设置
setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen);
tcp 或 udp 接收缓冲区最大可设置值的一半。
也就是说调用 setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen); 时rcv_size 如果超过 131071,那么
getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen); 去到的值就等于 131071 * 2 = 262142
tcp 或udp收发缓冲区最小值
tcp 或udp接收缓冲区的最小值为 256 bytes,由内核的宏决定;
tcp 或udp发送缓冲区的最小值为 2048 bytes,由内核的宏决
其他选项:
1. /proc/sys/net/ipv4/tcp_timestamps — 时间戳在TCP的包头增加12个字节
2. /proc/sys/net/ipv4/tcp_sack — 有选择的应答
3. /proc/sys/net/ipv4/tcp_window_scaling — 支持更大的TCP窗口.
二.Socket 函数:
其在linux和windows环境下的头文件主要是:#include<sys/socket.h>和#include<WinSock2.h>
1. socket
Int socket(int domain,int type, int protocol)
返回值:非负描述符 – 成功,-1 - 出错
其中:
Family指明了协议族/域
AF_INET(ipv4网络)
AF_INET6(ipv6网络)
AF_LOCAL(本地)等;
type是套接口类型
SOCK_STREAM (提供面向连接的稳定数据传输,即TCP协议)
OOB: 在所有数据传送前必须使用connect()来建立连接状态。
SOCK_DGRAM: 使用不连续不可靠的数据包连接。
SOCK_SEQPACKET: 提供连续可靠的数据包连接。
SOCK_RAW: 提供原始网络协议存取。
SOCK_RDM: 提供可靠的数据包连接。
SOCK_PACKET: 与网络驱动程序直接通信。
protocol一般取为0。成功时,返回一个小的非负整数值,与文件描述符类似。
对于windows环境下,在调用该函数之前需首先调用WSAStartup函数完成对Winsock服务的初始化,如
#include<WinSock2.h>
WSADATA wdata;
if ( WSAStartup(MAKEWORD(2,2), &wdata) !=0 ){
return INVALID_SOCKET;
}
后面即可调用socket函数,参数意义与linux环境一致。
2. bind
Int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen)
返回值:0 – 成功,-1 - 出错
当socket函数返回一个描述符时,只是存在于其协议族的空间中,并没有分配一个具体的协议地址(这里指IPv4/IPv6和端口号的组合),bind函数可以将一组固定的地址绑定到sockfd上。
其中:
sockfd是socket函数返回的描述符;
myaddr指定了想要绑定的IP和端口号,均要使用网络字节序-即大端模式;
addrlen是前面struct sockaddr(与sockaddr_in等价)的长度。
为了统一地址结构的表示方法,统一接口函数,使得不同的地址结构可以被bind()、connect()、recvfrom()、sendto()等函数调用。但一般的编程中并不直接对此数据结构进行操作,而使用另一个与之等价的数据结构sockaddr_in。
通常服务器在启动的时候都会绑定一个众所周知的协议地址,用于提供服务,客户就可以通过它来接连服务器;而客户端可以指定IP或端口也可以都不指定,未分配则系统自动分配。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
Windows下的版本:
Int bind( IN SOCKET s, IN const struct sockaddr FAR * name, IN int namelen);
3. listen
Int listen(int sockfd,int backlog)
返回值:0 – 成功,-1 - 出错
为了接受连接,先用socket()创建一个套接口的描述字,然后用listen()创建套接口并为申请进入的连接建立一个后备日志,然后便可用accept()接受连接了。listen()仅适用于支持连接的套接口,如SOCK_STREAM类型的。套接口s处于一种“变动”模式,申请进入的连接请求被确认,并排队等待被接受。这个函数特别适用于同时有多个连接请求的服务器;如果当一个连接请求到来时,队列已满,那么客户将收到一个WSAECONNREFUSED错误。
当没有可用的描述字时,listen()函数仍试图正常地工作。它仍接受请求直至队列变空。当有可用描述字时,后续的一次listen()或accept()调用会将队列按照当前或最近的“后备日志”重新填充,如有可能的话,将恢复监听申请进入的连接请求
内核维护两个队列 未完成链接队列(等待完成三次握手),已完成链接队列(链接已经建立); 两个队列之和数量不得超过backlog.
4. connect
Int connect(int sockfd,conststruct sockaddr *addr, socklen_t addrlen)
返回值:0 – 成功,-1 - 出错
通过此函数建立于TCP服务器的连接,实际是发起三次握手过程,仅在连接成功或失败后返回。参数sockfd是本地描述符,addr为服务器地址,addrlen是socket地址长度。
UDP的connect函数,结果与tcp调用不相同,没有三次握手过程。内核只是记录对方的ip和端口号,他们包含在传递给connect的套接口地址结构中,并立即返回给调用进程。
5. accept
Int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
返回值:非负描述符 – 成功,-1 - 出错
本函数从s的等待连接队列中抽取第一个连接,创建一个与s同类的新的套接口并返回句柄。如果队列中无等待连接,且套接口为阻塞方式,则accept()阻塞调用进程直至新的连接出现。如果套接口为非阻塞方式且队列中无等待连接,则accept()返回一错误代码。已接受连接的套接口不能用于接受新的连接,原套接口仍保持开放