网络编程中的细节问题

(一)socket阻塞与非阻塞问题

在创建socket函数中的第二个参数types用来指定服务类型,在Linux2.6.17版本起,除了之前的SOCK_STREAM(流式服务)、SOCK_UGRAM(数据报服务)以外,又增加了SOCK_NONBLOCK和SOCK_CLOEXEC,分别表示将创建的socket设置为非阻塞的和用fork创建的子进程在子进程中关闭该socket;

第一个问题:如何将socket设置为非阻塞的?

第一种,fcntl系统调用函数是用来控制文件描述符常用的属性和行为,通过此函数可以修改socket属性;

#include <fcntl.h>
int fcntl(int fd,int cmd,...)

fd是要被操作的文件描述符,cmd参数指定执行何种类型操作,常见的cmd有以下几种:

获取文件描述符标志  ----> F_GETFD   设置文件名描述符标志----->F_SETFD 

获取文件描述符状态标志--->F_GETFL 设置文件名描述符状态标志----->F_SETFL

修改socket为非阻塞socket

int flags = fcntl(sockfd,F_GETFL,0);

fcntl(sockfd,F_SETFL,flags | SOCK_NONOBLOCK);

第二种:直接在创建socket时设置

int sockfd= socket(AF_INET,SOCK_STREAM | SOCK_NONOBLOCK,0);

注意accept返回的socket也是阻塞的,应用accept4函数将最后一个参数设置为SOCK_NONOBLOCK

int accept4(sockfd,(struct sockaddr*)&cli,&len,SOCK_NONBLOCK);

第二个问题:在完成三次握手之后,网络异常(掉线)或者客户端正常退出,对accept是否会有影响?

netstat -nt | grep hostport 查看此端口的TCP连接状态

掉线以及客户端退出都不会影响accept调用返回,由于accept只是从监听队列中取出连接,而不论连接状态处于何种状态,更不关心网络状态.不会因为监听队列中socket状态的变化而决定accept是否能调用成功。

第三个问题:关于socket选项的知识

#include <sys/socket.h>

int getsockopt(int sockfd,int level,int option_name,void *option_value,socklen_t *restrict option_len);

int setsockopt(int sockfd,int level,int option_name,const void *option_value,socklen_t  option_len);

level指定要操作那个协议的选项,通用socket选项的level是SOL_SOCKET,option_name 是指定选项的名字

常用的通用socket选项有:SO_REUSEADDR 重用本地址 、SO_RCVBUF --->TCP接受缓冲区大小、SO_SNDBUF --->TCP发送缓冲区大小、SO_LINGER 若有数据待发送则延缓关闭、TCP_MAXSEG  ---> TCP最大报文段大小 上述这几个必须在listen调用之前设置才有效。

socket选项一:SO_REUSEADDR

通过设置socket此选项可以强制使用处于time_wait状态的连接占用的socket地址,也可以通过修改内核参数/proc/sys/net/ipv4/tcp_tw_recycle来快速回收被关闭的连接socket,从而使得TCP连接根本不进入time_wait状态。进而允许本次程序重用socket地址

socket选项二:SO_RCVBUF SO_SNDBUF

SO_RCVBUF:TCP接受缓冲区的大小最小值为256字节 ,如果我们修改此大小小于256字节时,系统会忽略此次设置,最小值还是256字节

SO_SNDBUF:TCP发送缓冲区大小最小值是2048字节,是为了保证一个TCP连接有足够的空闲缓冲区处理拥塞,如果我们修改此大小,系统会将我们设置的值翻倍

也可以修改内核参数:/proc/sys/net/ipv4/tcp_rmem、/proc/sys/net/ipv4/tcp_wmem来强制TCP接受和发送缓冲区的大小没有最小值限制。

选项三:SO_LINGER

SO_LINGER用于控制close系统调用在关闭TCP连接时的行为,默认情况下,当我们使用close系统调用关闭socket时会立即返回,TCP模块将该socket对应的发送缓冲区的残留数据发送给对方

在设置此选项时,需要向函数传递Linger结构体:

struct linger

{

  int l_onoff;//0未开启,1开启

  int l_linger;//滞留时间 

}

根据linger结构体中两个变量的值不同,close调用会有不同的方式

情况一:l_onoff==0;此时SO_LINGER不起作用,close使用默认行为关闭socket;

情况二:l_onoff != 0 && l_linger == 0;此时close调用立即返回,将该socket对应的发送缓冲区的数据发给对方,同时给对方发送一个复位报文段。这种情况给服务器提供了一个异常终止连接的方法;

情况三:l_onoff != 0 && l_linger >0;此时close的行为取决于两个条件:1.该socket对应的发送缓冲区是否还有残留数据;2.该socket是阻塞的还是非阻塞的;

对于阻塞的socket,close将等待一段长为l_linger的时间等TCP模块将发送缓冲区的数据发给对方并得到对方确认,返回-1并设置EWOULDBLOCK(不需要重新读写);

对于非阻塞的socket,close立即返回,此时需要根据返回值和error来判断残留数据是否发送完毕;如果tcp缓冲区还有数据需要发送,数据能够正确的发送到对端;在发送的最后一个包会加FIN标志;如果另一端也要关闭发FIN时,本端就会发RST,因为本端的SOCKET已经关了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值