Administrator
发布于 2023-12-04 / 21 阅读 / 0 评论 / 0 点赞

网络编程(二)

第二部分 第一个网络通讯程序_哔哩哔哩_bilibili

 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

位图

  1. 删除FD_CLR

  2. 是否存在FD_ISSET

  3. 加入位图FD_SET

  4. 把位图清零FD_ZERO

通过比较readsets和tmpset,得知3,6有事件

一般服务器多关注读事件,不关心写事件。

水平触发

本来一句话,每次处理5字节,所有,在没读完前会不断通知该事件直到事件被读完

select的缺点

  1. 轮询慢

  2. 拷贝2次

  3. bitmap1024,可以修改效率更低

poll

  1. fd

  2. fd的要监听的事件

  3. fd需要返回的事件

结构体数组的2种表示

第一种,容易书写

第二中,空间利用率更低

epoll

epoll_event.data 是一个union,填fd,就不能填ptr

阻塞IO和非阻塞IO

connect()

accept()

send()、recv()

事件循环
while(true){
	//todo
}

我们一般把这种代码称为事件循环

一般事件循环最好不要在某个地方阻塞,这是优化的关键!

recv()因为是接到通知才执行,一般不会阻塞住

send()可能因为对方被填满,要等待地方缓冲区处理完,很可能阻塞!!!

注意:

得到读通知时,第一次recv()一般不会阻塞住,但后面还有recv()就会阻塞

非阻塞的connect()

connect会阻塞,有3次握手,

如果是阻塞connect,如果握手失败会等会再尝试,尝试几次后才结束

如果是非阻塞connect,不管能否连接成功立即返回失败

如果错误代码不是EAGAIN才是真的失败

水平触发和边缘触发

水平触发,发现数据没有读完,会一直通知

边沿触发,当有数据来时,会通知一次,不管有没有处理都不会再通知了

边沿触发可能出现的问题

  1. 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。


评论