ps -ef | grep op_file
cat /proc/7766/fd/3
默认的3给文件描述符
文件和socket是同一个东西
可以使用文件处理函数处理socket
socket
linux默认打开的最大文件数1024
sock也是文件,也受这个限制
字节序
c语言的4个处理字节序转换的函数
IP和端口
ip4字节,port2字节(0-65536)
处理大小端序
只有向sockaddr_in填充数据时才考虑字节序
地址结构体
2中设置ip
读和写
send()和recv()
write()和read()
send(const string &buffer)既支持c_stry又支持string
设置非阻塞
fcntl(sockfd, F_SETFL,fcntl(sockfd, F_GETFL)|O_NONBLOCK);
这行代码是用来将套接字设置为非阻塞模式的。下面是各个参数的解释:
sockfd
:这是你想要修改的套接字的文件描述符。F_SETFL
:这是fcntl
函数的命令参数,表示你想要设置文件状态标志。fcntl(sockfd, F_GETFL)
:这是一个嵌套的fcntl
调用,用于获取当前的文件状态标志。F_GETFL
命令表示你想要获取文件状态标志。O_NONBLOCK
:这是你想要设置的文件状态标志。将其与当前的文件状态标志进行位或运算,可以将O_NONBLOCK
添加到文件状态标志中,而不改变其他标志。
文件传输
多进程服务器
父进程只负责建立新连接,子进程只负责与客户端通信
三次握手和四次挥手
1024以内的端口只有root才能使用
ESTABLISHED 是listen() 但是还未accpt()的端口的状态
SYN_RECV是没有完成3次握手,也就是连接失败的端口(超出设置连接数)
服务端的主动断开的缺点
要等2分钟
解决方法,让你不用等待这么久
TCP缓存
查看socket缓存
send和recv会阻塞吗?
都会,如果发送的比接受的快太多,就会变成发一会停一会。
如果发送端在接受端睡眠时发送数据并关闭,接收端结束睡眠时,依然会收到这些数据(如果接受缓冲区未满时)
Nagle算法
优点:充分利用带宽
弊端:会让发送的包延迟40ms,对时效性很强的系统不好
IO多路复用
Select
位图
删除FD_CLR
是否存在FD_ISSET
加入位图FD_SET
把位图清零FD_ZERO
通过比较readsets和tmpset,得知3,6有事件
一般服务器多关注读事件,不关心写事件。
水平触发
本来一句话,每次处理5字节,所有,在没读完前会不断通知该事件直到事件被读完
select的缺点
轮询慢
拷贝2次
bitmap1024,可以修改效率更低
poll
fd
fd的要监听的事件
fd需要返回的事件
结构体数组的2种表示
第一种,容易书写
第二中,空间利用率更低
epoll
阻塞IO和非阻塞IO
connect()
accept()
send()、recv()
事件循环
while(true){
//todo
}
我们一般把这种代码称为事件循环
一般事件循环最好不要在某个地方阻塞,这是优化的关键!
recv()因为是接到通知才执行,一般不会阻塞住
send()可能因为对方被填满,要等待地方缓冲区处理完,很可能阻塞!!!
注意:
得到读通知时,第一次recv()一般不会阻塞住,但后面还有recv()就会阻塞
非阻塞的connect()
connect会阻塞,有3次握手,
如果是阻塞connect,如果握手失败会等会再尝试,尝试几次后才结束
如果是非阻塞connect,不管能否连接成功立即返回失败
如果错误代码不是EAGAIN才是真的失败
水平触发和边缘触发
水平触发,发现数据没有读完,会一直通知
边沿触发,当有数据来时,会通知一次,不管有没有处理都不会再通知了
边沿触发可能出现的问题
accept()时,同时有多个客户端连接,水平模式下,这次没处理的,下次还会触发,会继续处理,但是如果是边沿模式,每次循环只能建立一个新连接,其他的会被遗忘,下次循环也不会在通知(默认你已经处理完了)
所以accpt要加循环,并且,accpt要设为非阻塞,不然在循环中会被阻塞
边沿模式,非阻塞recv()
确保一次通知,全部读完,再处理
写也是
确保一次通知,全部写完
epoll的原理
没给资源。。。。
BUG
1. 关于string的buf,发送和接受同值不相等
原因:buf.的大小不一样
2. 关于recv()的readn==0的情况
readn=recv(sockfd, buf.data(), buf.size(),0);
可能原因,buf未初始化或刚刚被buf.clear();大小未知
此时buf.size()会出问题
解决方法
buf.clear();
buf.resize(1024);
readn=recv(sockfd, buf.data(), buf.size(),0);
3. 文件传输服务器,双方大小不一致问题
在运输末尾时,把整个缓冲区都写入了,实际上最后一行不一定满足4096,但写入却按4096写入了。
错误写法
while (true) {
memset(&buf, 0, sizeof(buf));
onread=(filesize-totalbytes)>4096?4096:filesize-totalbytes;
misjudgment(recv(sockfd, buf, sizeof(buf), 0),"EORROR: recv file_bytes failed");
fout.write(buf, sizeof(buf));
totalbytes+=onread;
if (totalbytes==filesize) {
break;
}
}
正确写法
while (true) {
memset(&buf, 0, sizeof(buf));
onread=(filesize-totalbytes)>4096?4096:filesize-totalbytes;
misjudgment(recv(sockfd, buf, onread, 0),"EORROR: recv file_bytes failed");
fout.write(buf, onread);
totalbytes+=onread;
if (totalbytes==filesize) {
break;
}
}
4. strlen()和sizeof()区别
strlen(buf)
返回的是buf
中第一个空字符前的字符数。也就是说,如果buf
是一个字符串,strlen(buf)
将返回字符串的长度(不包括结尾的空字符)。sizeof(buf)
返回的是buf
的总大小,以字节为单位。在你的代码中,buf
是一个包含1024个字符的数组,所以sizeof(buf)
将返回1024。