网络基础 send/recv
来源:互联网 发布:未来软件客服电话 编辑:程序博客网 时间:2024/05/21 18:11
以下为服务器端非阻塞的例子:
#include <unistd.h>#include <fcntl.h> sockfd = socket(AF_INET,SOCK_STREAM,0);fcntl(sockfd,F_SETFL,O_NONBLOCK);while(1){sockConn = accept(socketSer,(SOCKADDR*)&addrClient,&len);if(INVALID_SOCKET == sockConn){int err = WSAGetLastError();if(WSAEWOULDBLOCK == err){Sleep(100);continue;}char buf[100];recv(sockConn,buf,100,0);}}
而调用select()会有效的解决这个问题,它允许你把进程本身挂起,而通知使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任务被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已经准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费CPU开销(关于异步I/O今后将重点介绍)。
socket将数据传输分为 流(TCP)和数据报(UDP)。两者有啥区别呢?
TCP作为传输控制协议,发送的是流,发的包不是整包达到的,而是连续不断的到达的,而组包的工作就需要接收端去完成。而UDP传输的是数据报文,它一定是一整包到达,但是整包数据不宜过长。适用于一次性传输少量数据,对可靠性不高的应用环境。
网络中究竟一次性能传输多大的数据量呢?是不是我们一次塞给网络多大的数据,网络就能传输多大的呢?网络中究竟一次性能传输多大的数据量呢?是不是我们一次塞给网络多大的数据,网络就能传输多大的呢?
发包的长度是我们自己决定的,这需要我们考虑业务的具体需求与当前网络的状况。对于TCP而言,发送的长度可以比较大,但是socket内核默认的收发缓冲区大小为几KB (用户可以通过调用SetSockOpt来该改变这个值)。对于UDP,设置的值不宜过大,因为UDP要是其中有一个分片丢失,那么接受方网络层将把整个发送包丢弃,这就形成丢包,如果一个UDP包很大,被切分的自然片数很多,容易丢失的概率也就越大,但是包也不能太小,太小了会影响业务效率。
1. send( )函数返回了实际发送的长度,只要网络不断,函数绝对不会返回发送失败(对于阻塞的socket,无论一包数据多大,都能够发送,但一次传输不宜过大,否则容易造成socket缓冲的阻塞)。对于TCP,可以写一个消息循环发送;而UDP最好一次性一整包到达。
2. recv( )函数,对于TCP,接收方先收这个包头信息,然后再收包数据。一次收齐整个包也可以,可要对结果是否收齐进行验证。这也就完成了组包过程。UDP,那你只能整包接收了。要是你提供的接收Buffer过小,TCP将返回实际接收的长度,余下的还可以收,而UDP不同的是,余下的数据被丢弃并返回WSAEMSGSIZE错误。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多个发包,你必须分离它们,还有就是当Buffer太小,而一次收不完Socket内部的数据,那么Socket接收事件(OnReceive),可能不会再触发,使用事件方式进行接收时,密切注意这点。这些特性就是体现了流和数据包的区别。
发包的长度是我们自己决定的,这需要我们考虑业务的具体需求与当前网络的状况。对于TCP而言,发送的长度可以比较大,但是socket内核默认的收发缓冲区大小为几KB (用户可以通过调用SetSockOpt来该改变这个值)。对于UDP,设置的值不宜过大,因为UDP要是其中有一个分片丢失,那么接受方网络层将把整个发送包丢弃,这就形成丢包,如果一个UDP包很大,被切分的自然片数很多,容易丢失的概率也就越大,但是包也不能太小,太小了会影响业务效率。
三. send( )和recv( )工作原理
Send( )函数作用就是将buffer中的数据拷贝到socket的发送缓冲区,只要拷贝成功,send( )函数就返回了,但是此时这些数据并不一定马上通过网络传输。对于非阻塞socket的send函数调用,需要先比较发送数据的长度bLen和套接字的发送缓冲区的长度sLen:如果bLen长度大于sLen的长度,该函数将会返回SOCKET_ERROR。对于阻塞socket的send调用,则会一直等待直到全部拷贝到socket发送缓冲区才返回。
recv( )函数先检查socket的接收缓冲区,如果接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕(这里对于阻塞套接字而言)。recv函数然后把socket中接收缓冲区中的数据拷贝到buffer中。
对方如果中途调用close( )正常的关闭socket,这并不影响另外一端recv的正常接收数据;如果协议缓冲区内没有数据,recv返回0,指示对方关闭;如果协议缓冲区有数据,则返回对应数据(可能需要多次recv),在最后一次recv时,返回0,指示对方关闭。
1. recv( )的返回值:
无论阻塞还是非阻塞,recv的返回值都没有区别:
- 小于 0 出错
- 等于0 连接关闭
- 大于0 接收到数据大小
- EAGAIN:套接字已标记为非阻塞,而接收操作被阻塞或者接收超时(本机的接收缓冲区中无数据)
- EBADF:sock不是有效的描述词
- ECONNREFUSE:(104) 远程主机阻绝网络连接,远程主机没有关闭socket就直接退出了
- EFAULT:内存空间访问出错
- EINTR:操作被信号中断
- EINVAL:参数无效
- ENOMEM:内存不足
- ENOTCONN:与面向连接关联的套接字尚未被连接上
- ENOTSOCK:sock索引的不是套接字
以下代码是服务器端调用libevent的读事件回调(关于libevent,以后将讲解),调用该回调时,已经保证了有数据到来:
void main_Read(evutil_socket_t fd, short events, void *arg){ TFdState *state = arg; int iUsedIdx = 0; int result = 0; memset(state->read_buf, 0 ,MAX_BUF_LEN); while (1) { result = recv(fd, state->read_buf+iUsedIdx, MAX_BUF_LEN, 0); if (result <= 0) break; printf("result :%d\n", result); iUsedIdx = result; } printf("while out result: %d errno: %d\n", result, errno); if(result == 0) //接收套接字关闭 { free_fd_state(state); printf("recv finished!\n"); } if(result < 0) { if(errno == EAGAIN) { printf("errno == EAGAIN\n"); return ; } else { printf("else"); free_fd_state(state); } }}
(1) 如果客户端发送完数据(数据长度为3930个字节)以后就调用close() 关闭 套接字;且服务器的监听套接字(listen socket,设置非阻塞模式) 则结果如下:
(3)如果客户端发送数据以后,服务器的监听套接字为阻塞套接字,则结果如下:
- 网络基础 send/recv
- 网络send/recv函数解析
- 网络编程send和recv
- 网络编程send、recv函数详解
- 网络编程之recv send研究
- linux网络编程--深入浅出send和recv
- linux网络编程--深入浅出send和recv
- 网络socket编程指南 4 listen accept send recv 函数
- 网络编程中read/write和send/recv
- UNIX网络编程——send与recv函数详解
- 再次深入理解TCP网络编程中的send和recv
- linux网络编程三:recv, send 的调用
- UNIX网络编程——send与recv函数详解
- 网络编程学习笔记(recv和send函数)
- 转:理解TCP网络编程中的send和recv
- 网络编程send()和recv()详解
- linux下网络编程send,recv,read,write的区别
- Linux send recv网络中断的处理策略
- obj-c编程01:第一个类和对象的范例
- 电商实训三:网店经营
- iOS_Objective-C测试
- sprintf,sprintf_s,_snprintf和_snprintf_s之间的区别
- jQuery
- 网络基础 send/recv
- href 中路径的问题
- 如何使用Holmos处理模态窗口(ModalDialog)
- 希尔排序和快速排序
- 【Unity3D】【NGUI】Atlas的动态创建
- myibatis插入后返回主键
- 妙语速记3000英文单词
- misc设备驱动模型及实例解析
- java核心技术I 第三章 输入测试盒大数测试