0x00 前言

文章中的文字可能存在语法错误以及标点错误,请谅解;

如果在文章中发现代码错误或其它问题请告知,感谢!

系统版本:Ubuntu 14.04_64

最后更新:2022-04-28

0x01 使用场景

对于TCP来说,一般使用send()recv()函数进行数据读写。
对于UDP来说,一般使用sendto()recvfrom()函数进行数据读写。

我们都知道,UDP是一个简单传输层协议,丢包不可重传。UDP提供的是无连接(connectionless)服务,UDP客户和服务器之间并不建立连接,所以双方之间每次发送数据都需要指定发送的目的地址以及端口信息1sendto()函数可以在参数中指定发送的目的地,可以让数据正确发送到对方。recvfrom()函数可以等待某个对方数据的到达并记录对方的地址及端口信息。

TCP则是传输控制协议,提供的是面向连接的可靠字节流,可以丢包重传。当使用TCP方式进行通信时,TCP客户首先会跟服务器建立一个连接(connection),即三次握手(three-way handshake)的过程。通过该连接,客户与服务器之间便可以进行数据交换2。所以,一旦连接建立之后,便意味着形成了一条通路,双方就可以不用在各自的发送过程中再指定目的地地址和端口信息或者在接收数据的过程中另外记录对方的地址和端口信息,直接进行数据读写即可。在TCP连接中,可以使用send()函数以及recv()函数进行数据读写,也可以使用sendto()函数以及recvfrom()函数进行读写,只不过将地址相关参数设置为NULL即可。

0x02 函数介绍

1 recvfrom和sendto

recvfrom:

#include <sys/types.h>
#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

sockfd:指定接收端套接字描述符;
buf:待接收数据的缓冲区;
len:待接收数据的缓冲区大小;
flags:一般设置为0,或者一个或多个为常值的逻辑或3

flags 说明 recvfrom send
MSG_DONTROUTE 绕过路由表查找 ·
MSG_DONTWAIT 仅本操作非阻塞 · ·
MSG_OOB 发送或接收带外数据 · ·
MSG_PEEK 窥看外来消息 ·
MSG_WAITAIL 等待所有数据 ·

src_addr:用来指定欲接收的网络地址
addrlen:用来指定欲接收的网络地址的长度

返回值:成功则返回实际传送出去的字符数, 失败返回-1, 错误原因存于errno 中.

错误代码4
1、EBADF 参数s 非法的socket 处理代码.
2、EFAULT 参数中有一指针指向无法存取的内存空间.
3、WNOTSOCK canshu s 为一文件描述词, 非socket.
4、EINTR 被信号所中断.
5、EAGAIN 此动作会令进程阻断, 但参数s 的soket 为补课阻断的.
6、ENOBUFS 系统的缓冲内存不足.
7、EINVAL 传给系统调用的参数不正确.

sendto:

#include <sys/types.h>
#include <sys/socket.h>ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd:指定发送端套接字描述符;
buf:待发送数据的缓冲区;
len:要发送的数据的字节数;
flags:一般设置为0,或者一个或多个为常值的逻辑或,具体见上

src_addr:用来指定欲发送的网络地址
addrlen:用来指定欲发送的网络地址的长度

返回值:成功则返回实际传送出去的字符数, 失败返回-1, 错误原因存于errno 中。

错误代码5
1、EBADF 参数s 非法的socket 处理代码。
2、EFAULT 参数中有一指针指向无法存取的内存空间。
3、WNOTSOCK canshu s 为一文件描述词, 非socket。
4、EINTR 被信号所中断。
5、EAGAIN 此动作会令进程阻断, 但参数s 的soket 为补课阻断的。
6、ENOBUFS 系统的缓冲内存不足。
7、EINVAL 传给系统调用的参数不正确。

2 recv和send

recv:

 #include <sys/types.h>#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd:指定接收端套接字描述符;
buf:待接收数据的缓冲区;
len:待接收数据的缓冲区大小;
flags:一般设置为0,或者一个或多个为常值的逻辑或,具体见上。

返回值:成功则返回接收到的字符数, 失败返回-1,错误原因存于errno 中。

错误代码6
EBADF 参数s 非合法的socket 处理代码。
EFAULT 参数中有一指针指向无法存取的内存空间。
ENOTSOCK 参数s 为一文件描述词, 非socket。
EINTR 被信号所中断。
EAGAIN 此动作会令进程阻断, 但参数s 的socket 为不可阻断。
ENOBUFS 系统的缓冲内存不足。
ENOMEM 核心内存不足。
EINVAL 传给系统调用的参数不正确。

send:

#include <sys/types.h>
#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);

sockfd:指定发送端套接字描述符;
buf:待发送数据的缓冲区;
len:要发送的数据的字节数;
flags:一般设置为0,或者一个或多个为常值的逻辑或,具体见上。

返回值:成功则返回实际传送出去的字符数, 失败返回-1. 错误原因存于errno。

错误代码7
EBADF 参数s 非合法的socket 处理代码。
EFAULT 参数中有一指针指向无法存取的内存空间。
ENOTSOCK 参数s 为一文件描述词, 非socket。
EINTR 被信号所中断。
EAGAIN 此操作会令进程阻断, 但参数s 的socket 为不可阻断。
ENOBUFS 系统的缓冲内存不足。
ENOMEM 核心内存不足。
EINVAL 传给系统调用的参数不正确。

0x03 代码举例

举几个例子可以更好的体会send、sendto以及recv、recvfrom。

1 UDP下的recvfrom和sendto

UDP server:

#include<stdio.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<string.h>int main()
{//创建套接字int fd = socket(AF_INET, SOCK_DGRAM, 0);//绑定struct sockaddr_in myaddr;myaddr.sin_family = AF_INET;myaddr.sin_port = htons(8000);myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//inet_addr只能用于IPV4,也可以用inet_pton IPV4 IPV6都可以int ret = bind(fd, (struct sockaddr*)&myaddr, sizeof(myaddr));if(ret < 0){perror("");return 0;}//读写char buf[1500] = "";struct sockaddr_in cliaddr;socklen_t len = sizeof(cliaddr);while(1){memset(buf, 0, sizeof(buf));int n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr, &len);if(n < 0){perror("");break;}else{printf("%s\n", buf);sendto(fd, buf, n, 0, (struct sockaddr*)&cliaddr, len);}}//关闭close(fd);return 0;
}

UDP client:

#include<stdio.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<string.h>int main()
{//创建套接字int fd = socket(AF_INET, SOCK_DGRAM, 0);//读写char buf[1500] = "";struct sockaddr_in dstaddr;dstaddr.sin_family = AF_INET;dstaddr.sin_port = htons(8000);dstaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//inet_addr只能用于IPV4,也可以用inet_pton IPV4 IPV6都可以int n = 0;while(1){read(STDIN_FILENO, buf, sizeof(buf));n = sendto(fd, buf, strlen(buf), 0, (struct sockaddr*)&dstaddr, sizeof(dstaddr));memset(buf, 0, sizeof(buf));n = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);//保不保存对方服务器IP无所谓,因为自己知道已经知道对方服务器IPif(n < 0){perror("");}else{printf("%s\n", buf);}}//关闭close(fd);return 0;
}

2 TCP下的recv和send

TCP server:

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>int main()
{//创建套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);//绑定struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8000);addr.sin_addr.s_addr = INADDR_ANY; //绑定通配地址//inet_pton(AF_INET, "192.168.21.37", addr.sin_addr.s_addr);int ret = bind(lfd, (struct socketaddr*)&addr, sizeof(addr));if(ret < 0){perror("");exit(0);}//监听listen(lfd,128);//提取(一个)struct sockaddr_in cliaddr;socklen_t len = sizeof(cliaddr);int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);char ip[16] = {0};printf("new client ip = %s port = %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, 16), ntohs(cliaddr.sin_port));//读写char buf[1024] = {0};while(1){//发送至客户端bzero(buf, sizeof(buf));int n = read(STDIN_FILENO, buf, sizeof(buf));send(cfd, buf, n, 0);//从客户端读取n = recv(cfd, buf, sizeof(buf), 0);if(0 == n){printf("client closed\n");break;}printf("%s\n", buf);}//关闭close(lfd);close(cfd);return 0;
}

TCP client:

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>int main(int argc, char* argv[])
{   //创建套接字int sock_fd;sock_fd = socket(AF_INET, SOCK_STREAM, 0);//连接服务器struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8000);inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr);connect(sock_fd,(struct sockaddr*)&addr, sizeof(addr));//读写数据char buf[1024] = {0};while(1){int n = read(STDIN_FILENO, buf, sizeof(buf));send(sock_fd, buf, n, 0);//发送数据至服务器n = recv(sock_fd, buf, sizeof(buf), 0);send(STDOUT_FILENO, buf, n, 0);}//关闭close(sock_fd);return 0;
}

以上。


  1. 《UNIX网络编程 卷1:套接字联网API》(第3版) ,p186 ↩︎

  2. 《UNIX网络编程 卷1:套接字联网API》(第3版) ,p30 ↩︎

  3. 《UNIX网络编程 卷1:套接字联网API》(第3版) ,p305 ↩︎

  4. http://c.biancheng.net/cpp/html/368.html ↩︎

  5. http://c.biancheng.net/cpp/html/372.html ↩︎

  6. http://c.biancheng.net/cpp/html/367.html ↩︎

  7. http://c.biancheng.net/cpp/html/370.html ↩︎

【高并发服务器】send、sendto以及recv、recvfrom区别小结相关推荐

  1. send/sendto和recv/recvfrom各自的区别

    一般情况下:    send(),recv()用于TCP,sendto()及recvfrom()用于UDP    但是send(),recv()也可以用于UDP,sendto()及recvfrom() ...

  2. send sendto ,recv recvfrom有什么区别

    一般情况下: send(),recv()用于TCP,sendto()及recvfrom()用于UDP 但是send(),recv()也可以用于UDP,sendto()及recvfrom()也可以用于T ...

  3. 从简单到高并发服务器(一)

    一个单线程的回声服务器 (Echo Server) 我们从一个简单的服务器开始说起. 它可以接受一个客户的连接,接收消息,然后把这个消息发送回去,关闭连接--完工.我们用 Linux 和 iOS / ...

  4. Linux 高并发服务器开发

    该文章是通过观看牛客网的视频整理所得,以及在实践过程中遇到的问题及解决方案的整理总结. Linux 高并发服务器开发 linux 系统编程 linux 环境的搭建 环境搭建需要的软件 虚拟机中安装 u ...

  5. Linux高并发服务器开发---笔记4(网络编程)

    0705 第4章 项目制作与技能提升 4.0 视频课链接 4.1 项目介绍与环境搭建 4.2 Linux系统编程1.4.3 Linux系统编程2 4.4 多进程 1-9 10.进程间通信☆☆☆ 4.5 ...

  6. 网络编程实战之高级篇, 彻底解决面试C10k问题, 高并发服务器, IO多路复用, 同时监视多个IO事件

    目录 一.前言 二.IO多路复用的理解 三.IO多路复用的发展 select poll epoll ​四.C10K服务端代码 五. 总结 一.前言 网络入门篇,从操作系统的层次推开网络大门 网络入门基 ...

  7. Linux高并发服务器开发---笔记2(多进程)

    0630 第4章 项目制作与技能提升 4.0 视频课链接 4.1 项目介绍与环境搭建 4.2 Linux系统编程1.4.3 Linux系统编程2 4.4 多进程 1-9 10.进程间通信☆☆☆ 进程间 ...

  8. Linux 高并发服务器实战 - 2 Linux多进程开发

    Linux 高并发服务器实战 - 2 Linux多进程开发 进程概述 概念1: 概念2: 微观而言,单CPU任意时刻只能运行一个程序 并发:两个队列交替使用一台咖啡机 并行:两个队列同时使用两台咖啡机 ...

  9. Linux高并发服务器解决方案

    Linux高并发服务器案例演示 在网络通信中,我们常常的服务器经常会受到成千上万的请求提示,而电脑会根据请求建立相对应的socket链接,但是接触过Linux网络编程的人都知道,Linux链接和客户端 ...

  10. 如何使用 Go 语言搭建企业级高并发服务器?

    每到节假日和过年,需要外出通行的人几乎都会遇到一个问题:抢火车票!当全国上亿人都在固定的时间段抢票,服务器动辄就要承受上百万级并发的情况时,你就会明白,一个支持高并发的服务器架构有多重要! 在后端程序 ...

最新文章

  1. 徒手撸出一个类Flask微框架(三)根据业务进行路由分组
  2. 综合布线五大技巧与综合布线规范详解
  3. 《面向对象程序设计》第07章在线测试
  4. 面向中文短文本的实体链指任务竞赛亚军DeepBlueAI团队技术分享
  5. Spring+CXF之集成
  6. ecmall类关系图(转)
  7. NS3网络仿真(6): 总线型网络
  8. 动态规划经典题目_「动态规划经典算法」本周总结(二)
  9. Mac的反编译工具一:otool (objdump工具的OSX对应工具)。
  10. 让 M1 芯片的 MacBook Pro 同时支持两个 4k 显示器
  11. matlab怎样编写延时函数,编写延时函数的简单方法
  12. 双向可控硅详细用法说明
  13. iosclient发现_世界杯送流量活动项目总结
  14. 厉害了!99行代码实现《冰雪奇缘》
  15. 逆变器阻抗扫描 扫频法 阻抗扫描 阻抗建模验证
  16. App Store Connect显示app已经上架(可供销售),但在App Store中没有实时更新
  17. Mdeditor编辑器
  18. python 计算灰度共生矩阵
  19. 六轴EtherCAT总线伺服涂布收卷机程序,采用六个伺服+变频器+编码器,动态测量频率
  20. 餐饮APP发放优惠券需求文档

热门文章

  1. 如何在vim中每隔几行添加一行?
  2. Mybatis-plus向oracle插入数据返回主键
  3. linux-- linux忘记密码(修改root密码)
  4. 升级android10后费电,荣耀V20升级更新安卓10的Magic UI 3.0后耗电速度变快了
  5. Games104 Lecture 8 游戏引擎的动画技术基础
  6. 文字的动态运动效果在Vegas中该如何实现?
  7. MFC—UpdateData()用法
  8. Jenkins+Dcoker+Gitee+Rancher实现SpringBoot项目的持续集成自动化部署以及管理
  9. 3325. 今天是个特殊的日子
  10. android用微信打开链接地址,外链h5浏览器跳转微信打开小程序跳转任意网站地址方法...