linux网卡堵塞,linux c++ 网络测试-- 阻塞测试

首先说下问题的疑惑点在哪里?

1.客户端用户不发数据,服务器阻塞udp socket ,recvfrom函数是否会一直阻塞?

来,我们来写一端代码,方便我们测试,— 一定要多写代码,再简单都要写,理解和你认为你理解了,这之间的鸿沟,看似简单,实则非常深,而且不自己写一遍,心里真的是很没底的.

#include #include #include #include #include #include #include #define bind_local_port 9996

int main()

{

int sfd = socket(AF_INET,SOCK_DGRAM,0);

//bind port -- struct sockaddr_in 在头文件里

struct sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(bind_local_port);

addr.sin_addr.s_addr = INADDR_ANY;

if ( bind( sfd , (struct sockaddr*)&addr, sizeof(addr) ) < 0 )

{

perror("bind fail");

exit(1);

}

struct sockaddr_in client;

int len;

char recvbuf [2000];

int recvlen = recvfrom(sfd,recvbuf ,sizeof(recvbuf) , 0 ,(struct sockaddr*)&client , (socklen_t*)&len) ;

if(recvlen < 0)

{

switch(errno)

{

case EAGAIN:

{

printf("EAGIN event\n");

}

break;

// case EWOULDBLOCK:

// {

// printf("EWOULDBLOCK event\n");

// }

// break;

case ECONNREFUSED:

{

printf("ECONNREFUSED event\n");

}

break;

case EFAULT://内核内存错误---

{

printf("EFAULT event\n");

}

break;

case EINTR://被信号打断

{

printf("EINTR event\n");

}

break;

case ENOMEM:

{

printf("ENOMEM event\n");

}

break;

default:

{

printf("unknow error event:%d \n" , errno);

}

break;

}

}

//现在我没有用超时的来判断,我就是一个阻塞的udp socket

//测试:

//1.如果我一直阻塞,那么这里是否在一直等待.

//2.如果我在阻塞函数里设置了超时,是否回直接返回,我怎么判断返回是超时触发的?

//3.超时触发后,如果客户端直接中断连接了,那么服务器端怎么检测出来?-- 或者说超时之后,服务器是否还能够检测出来?

if(recvlen < 0)

{

//判断errno

}

printf("recv len: %d \n" , recvlen);

close(sfd);

return 0;

}

测试结论:

阻塞函数,如果不设置超时,那么是真的会一直阻塞,直到有数据过来!!!

2.如果这个时候我们设置了超时时间(注意此阶段,客户端依然不发送任何数据),那么阻塞函数recvfrom的反馈是怎样的? 非正常返回后,我们如何判断是超时导致的错误呢?

到了这一部,我们需要给我们的sock属性设置超时时间。

好我们来设置超时时间为2秒.

//设置超时属性

struct timeval rto;

rto.tv_sec = 2;

rto.tv_usec = 0;

setsockopt(sfd,SOL_SOCKET,SO_RCVTIMEO,(void*)&rto,sizeof(rto));

在recv的时候,我们监控时间属性:

//开始时间

struct timeval tb;

gettimeofday(&tb,NULL);

int recvlen = recvfrom(sfd,recvbuf ,sizeof(recvbuf) , 0 ,(struct sockaddr*)&client , (socklen_t*)&len) ;

//recv time out结束属性

struct timeval te;

gettimeofday(&te,NULL);

printf("second:%d usec:%d \n" , te.tv_sec - tb.tv_sec , te.tv_usec - tb.tv_usec);

结论:

489538e030b2179ee416ef7756d4e36c.png

从结果来看,很明显了,返回-1, errno为EAGIN,系统回告诉你,再次去读取数据.

3.在recv的过程中,如果客户端中断了连接,那么recv函数返回的情况是怎样的?

这里我们直接换用TCP连接进行测试,效果更加直接.

直接说结果:

如果说tcp stream,如果返回0,那么代表流已经终止了,返回-1代表发生了异常.

但是在udp里面,因为udp是无连接的,所以udp其实是存在接收0个长度的数据的可能性的.

这里我们一定要看官方的文档api说明.

RETURN VALUE

These calls return the number of bytes received, or -1 if an error

occurred. In the event of an error, errno is set to indicate the

error.

When a stream socket peer has performed an orderly shutdown, the return

value will be 0 (the traditional "end-of-file" return).

Datagram sockets in various domains (e.g., the UNIX and Internet

domains) permit zero-length datagrams. When such a datagram is

received, the return value is 0.

如果tcp stream 的 recv 超时呢,返回的是-1,并且errno = EAGIN,告诉我们继续去读取数据,这里和udp上面是或的是一致的.

总结下,就是就算recv失败,除非你自己调用close, 否则server端的管道是不会关闭的,但是server可以知道连接上的管道是否close掉了.

Server

gcc -o block_write block_write.c -lpthread

#include #include #include #include #include #include #include #include //write阻塞测试

#define SERVER_PORT 9995

#define MAX_SOCKETS 256

void* user_coming( void *args)

{

int cfd = *((int*)(args));

//内环缓冲区,默认的的缓冲区大小配置在: 系统默认在 cat /proc/sys/net/ipv4/tcp_wmem

int default_recv_buf_size ;

int len = 0;

getsockopt(cfd,SOL_SOCKET,SO_RCVBUF,&default_recv_buf_size,(socklen_t*)&len);

printf("sock recv default size:%d \n" , default_recv_buf_size);

//上面获取的用户进程的写缓冲区 。

uint8_t pkt [1024];

int size_writed = 0;

int c ;

//文档说明:https://linux.die.net/man/3/write

while(1)

{

fprintf(stderr, "start write , size writed:%d \n", size_writed);

c = write(cfd,pkt,sizeof(pkt));

if ( c == -1)

{

switch (errno)

{

case EAGAIN:

{

fprintf(stderr," EAGAIN \n");

}

break;

case EBADF:

{

fprintf(stderr, "EBADF \n");

}

break;

case EFBIG:

{

fprintf(stderr, "EFBIG \n");

}

break;

case EINTR:

{

fprintf(stderr, "EINTR \n");

}

break;

case EIO:

{

fprintf(stderr, "EIO \n");

}

break;

case ENOSPC:

{

fprintf(stderr, "ENOSPC \n");

}

break;

case EPIPE:

{

fprintf(stderr, "EPIPE \n");

}

break;

case ERANGE:

{

fprintf(stderr, "ERANGE \n");

}

break;

default:

fprintf(stderr, "Errno unknow: %d \n" , errno);

break;

}

break;

}

//注意事项:

//1.write函数的说明,它的返回值是怎么回事?

size_writed += c;

fprintf(stderr, "end write , size writed:%d \n", size_writed);

}

fprintf(stderr, "exit client \n");

close(cfd);

}

int main( int argc ,char *argv[])

{

int sfd = 0;

int ret = 0;

sfd = socket(AF_INET,SOCK_STREAM,0);

struct sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = INADDR_ANY;

addr.sin_port = htons(SERVER_PORT);

ret = bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

if( ret < 0 )

{

fprintf(stderr,"Error bind(). \n");

goto close;

}

ret = listen(sfd,MAX_SOCKETS);

if (ret < 0)

{

fprintf(stderr, "Error listen(). \n");

goto close;

}

while(1)

{

struct sockaddr_in clientAddr;

int len;

int cfd = accept(sfd,(struct sockaddr_in *)&clientAddr, (socklen_t*)&len );

if(cfd < 0)

{

break;

}

//创建线程

pthread_t cthr_id;

pthread_create(&cthr_id,NULL,user_coming,&cfd);

}

close:

close(sfd);

return 0;

}

结论:

如果客户端不接收,服务器会一直发送,一直到把内核的缓冲区塞满,塞满之后,此时再次write就会阻塞住,直到内核缓冲区中有空间可以继续写数据了.

如果客户端关闭了和服务器的连接,那么write就会返回-1,errno返回ECONNRESET (=104),

ECONNRESET

A write was attempted on a socket that is not connected.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值