网络
HTTP协议相关问题
1、GET 、POST方法的区别
1、GET参数放在url中,POST参数放在消息体中,GET参数直接暴露在url中,所以GET相比POST不安全,不能传递敏感信息
2、GET 可以被缓存,POST不能被缓存
3、GET可以被存为书签,POST不能被存为书签
4、GET的url最大有2048个字符,POST无长度限制
5、后退刷新按钮,GET无害,POST会被重新提交
6、GET的数据类型只能是ASCII码,而POST没有限制,可以是二进制
2、优雅关闭连接
我理解的就是按照TCP四次挥手的流程正常关闭连接,调用close(int sockfd) 会同时关闭读和写两个方向,但是默认情况下会将TCP发送缓冲区的残余数据继续发送给对方,所以直接使用close关闭问题也不大。
设置SO_LINGER 选项可以控制close()系统调用的行为,设置SO_LINGER选项时需要传入一个结构体linger
struct linger
{
int l_onoff;
int l_linger;
};
struct linger tmp = {0, 1};//第一个参数等于0,默认行为,close(sockfd)直接返回,TCP将发送缓冲区的残余数据发送给对方
setsockopt(connfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
设置linger的第一个成员为0时,SO_LINGER不起作用,close()是默认行为;
设置结构体linger{1,0}时,调用close()关闭连接时,会丢弃发送缓冲区的残余数据,同时给对方发送一个复位报文,RST,相当于直接终止了连接。这种情况就是直接关闭连接。
shutdown 可以关闭单个方向上的通道,比如shutdown(connfd,1)关闭发送通道,但是还可以读;shutdown(connfd,0)关闭读通道。但是我实际使用时,调用shutdown()关闭发送通道后,连接一直处于半关闭状态,主动关闭方一直处于FIN_WAIT_2,被动关闭方一直处于close_wait()状态,
3、TIME_WAIT状态的处理
TIME_WAIT只存在于主动关闭连接的一方。
TIME_WAIT存在的意义:
- 保证TCP正常的关闭连接,当最后一次确认丢失时,对方会重发FIN报文,TIME_WAIT存在的目的就是为了能收到对方重发的FIN 消息,保证连接有序的释放。如果没有TIME_WAIT状态,最后一次确认报文丢失,对方重发FIN消息,主动关闭方收到FIN消息,会发送一个复位报文,RST,被动关闭方会将这个当作一个错误处理。
- 是为了处理网络上还未到达的包,对方之前发送的包可能会在网络上逗留较长时间,慢慢悠悠才到达,如果没有TIME_WAIT状态,然后这个端口又立即被其他程序占用,则上一次连接的数据包可能会被当作这次连接的数据,显然这不是我们希望的。
解决大量TIME_WAIT:
time_wait过多,占用大量端口,浪费资源。
- 首先可以设置time_wait的上限,tcp_max_tw_buckets,当出现的time_wait数超过上限时,后续的连接释放时会跳过time_wait状态,直接关闭;
- 对于主动发起连接请求的,打开 tcp_tw_reuse 参数,可以立即复用time_wait状态的端口,但是这个只是对于主动发起tcp连接的一方才起作用,而服务器很少主动发起连接…
- 通过设置SO_LINGER选项,直接关闭连接,跳过了四次挥手,但是这时会放弃缓冲区中残留的数据,不建议。
二、socket编程
1、通信套接字要设置为非阻塞模式,否则读写操作recv、send可能会阻塞。
epoll注册通信套接字读事件为ET触发模式时,套接字默认为阻塞,通过recv函数读取套接字数据时,需要加上while(true)循环,当套接字当中的数据读完之后再次调用recv时(如果没有后续事件)会一直阻塞下去,比如下面的代码中,第一次调用recv读完了接收缓冲区中的数据,第二次调用recv会一直阻塞下去,直到关闭这次tcp连接,recv才返回。
while(true)
{
cout<<"recv:"<<endl;
int ret = recv(fd,buf+bytes_read,read_buf_size-1,0);
cout<<"recv next"<<endl;
if(ret==-1){
if(errno==EAGAIN || errno==EWOULDBLOCK){
cout<<"数据读取完毕"<<endl;
break;
}
cout<<errno<<endl;
close(fd);
break;
}
else if(ret==0){
cout<<"对方关闭连接"<<endl;
close(fd);
}
bytes_read +=ret;
cout<<"reading..."<<endl;
}
线程池相关
- 如果同时1000个客户端进行访问请求,线程数不多,怎么能及时响应处理每一个呢?
目前的并发模型中主线程通过一个epoll_wait()监听所有的文件描述符,一个线程同时只能处理一个客户端请求,如果同时请求的客户端较多,请求队列会堆积很多任务。
可以改变并发模型,在工作线程中增加工作线程自己的IO复用,主线程中收到新的连接就将通信套接字分发给某一个工作线程,此后这个套接字的所有请求都由该线程来处理,每个工作线程通过自己的epoll_wait()可以监听不同的通信套接字,每个工作线程都可以处理多个客户连接。