网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

14丨UDP也可以是“已连接”?

盛延敏 2019-09-02
你好,我是盛延敏,这里是网络编程实战的第 14 讲,欢迎回来。
在前面的基础篇中,我们已经接触到了 UDP 数据报协议相关的知识,在我们的脑海里,已经深深印上了“UDP 等于无连接协议”的特性。那么看到这一讲的题目,你是不是觉得有点困惑?没关系,和我一起进入”已连接“的 UDP 的世界,回头再看这个标题,相信你就会恍然大悟。

从一个例子开始

我们先从一个客户端例子开始,在这个例子中,客户端在 UDP 套接字上调用 connect 函数,之后将标准输入的字符串发送到服务器端,并从服务器端接收处理后的报文。当然,和服务器端发送和接收报文是通过调用函数 sendto 和 recvfrom 来完成的。
#include "lib/common.h"
# define MAXLINE 4096
int main(int argc, char **argv) {
if (argc != 2) {
error(1, 0, "usage: udpclient1 <IPaddress>");
}
int socket_fd;
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
socklen_t server_len = sizeof(server_addr);
if (connect(socket_fd, (struct sockaddr *) &server_addr, server_len)) {
error(1, errno, "connect failed");
}
struct sockaddr *reply_addr;
reply_addr = malloc(server_len);
char send_line[MAXLINE], recv_line[MAXLINE + 1];
socklen_t len;
int n;
while (fgets(send_line, MAXLINE, stdin) != NULL) {
int i = strlen(send_line);
if (send_line[i - 1] == '\n') {
send_line[i - 1] = 0;
}
printf("now sending %s\n", send_line);
size_t rt = sendto(socket_fd, send_line, strlen(send_line), 0, (struct sockaddr *) &server_addr, server_len);
if (rt < 0) {
error(1, errno, "sendto failed");
}
printf("send bytes: %zu \n", rt);
len = 0;
recv_line[0] = 0;
n = recvfrom(socket_fd, recv_line, MAXLINE, 0, reply_addr, &len);
if (n < 0)
error(1, errno, "recvfrom failed");
recv_line[n] = 0;
fputs(recv_line, stdout);
fputs("\n", stdout);
}
exit(0);
}
我对这个程序做一个简单的解释:
9-10 行创建了一个 UDP 套接字;
12-16 行创建了一个 IPv4 地址,绑定到指定端口和 IP;
20-22 行调用 connect 将 UDP 套接字和 IPv4 地址进行了“绑定”,这里 connect 函数的名称有点让人误解,其实可能更好的选择是叫做 setpeername
31-55 行是程序的主体,读取标准输入字符串后,调用 sendto 发送给对端;之后调用 recvfrom 等待对端的响应,并把对端响应信息打印到标准输出。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(12)

  • 程序水果宝
    对于recvfrom函数,我们可以看成是TCP中accept函数和read函数的结合,前三个参数是read的参数,后两个参数是accept的参数。对于sendto函数,则可以看成是TCP中connect函数和send函数的结合,前三个参数是send的参数,后两个参数则是connect的参数。所以udp在发送和接收数据的过程中都会建立套接字连接,只不过每次调用sendto发送完数据后,内核都会将临时保存的对端地址数据删除掉,也就是断开套接字,从而就会出现老师所说的那个循环
    2019-09-02
    10
  • Liam
    按照老师的说法,只有connect才建立socket和ip地址的映射;那么,如果不进行connect,收到信息后内核又是如何把数据交给对应的socket呢

    作者回复: 在答疑篇里统一回复了。

    2019-09-02
    3
    3
  • 沉淀的梦想
    还是不太理解为什么UDP的sendto方法会有一个"连接"过程的性能损耗,直接按照目标地址发过去不就可以了吗?我的理解是操作系统会先用ICMP协议探一探目标地址是否存在,然后再用UDP协议发送具体的数据,不知道理解的对不?

    作者回复: 我不觉得会发ICMP来探一谈。ICMP是用的时候才触发的。

    这里我想表达的是操作系统协议栈在每次sendto的时候都会需要一个地址初始化的过程,如果这个过程省略掉了,是可以得到一点点性能的提升的。当然,其实这个是没有那么大的。

    2019-09-04
    2
  • 传说中的成大大
    udp 连接套接字 这个是什么过程? 断开套接字这又是什么过程呢?

    作者回复: 没有断开,这里都是一个系统调用,告诉了一些系统内核信息而已。

    2019-09-02
    2
  • GeekAmI
    问题1:亲测可以;
    问题2:可以参考https://yq.aliyun.com/articles/523036。

    作者回复: 👍

    2019-10-22
    1
  • 凤梨酥
    这个recvfrom得知icmp获取异常有时效性吗? 如果之前连接失败,下一秒服务端又打开了呢

    作者回复: 如果有重试,当然会有时效性。问题是大多数处理recvfrom的UDP程序不会考虑重试,因为是UDP,所以就会直接失败了。

    2019-11-12
  • 神秘的火柴人
    在 实现一个 connect 的客户端程序 章节中,“客户端 2 从操作系统内核得到了 ICMP 的错误,该错误在 recv 函数中返回,显示了“Connection refused”的错误信息“,这里的错误是在connect函数中返回吧

    作者回复: 是recv函数返回的,connection refused不只是connect函数会返回的。不信你可以运行一下试试。

    2019-11-11
  • 明翼
    老师我们遇到一个问题,A--》B发送UDP报文,发现在B上用tcpdump可以抓到udp报文信息,但是,用nc -ul 启动端口的却收不到报文,后面检查了下发现B上对A网段的路由走的是另外一个网卡eth1,而A发送到B的UDP包走的是eth0网卡,我把B上对A网段的路由改成也用eth0网卡,就正常了,请问老师是什么原因那?谢谢

    作者回复: B上的网卡eth0和eth1分别对应了不同的IP吧,两块网卡,A发送报文的时候指定不同的目标地址就可以了。

    2019-09-23
  • Leon📷
    老师 include "lib/common.h" 这个头文件在哪里

    作者回复: 在我给的代码lib文件夹下。

    2019-09-12
  • 沉淀的梦想
    在我电脑上,第一个案例的现象是阻塞在了connect函数上

    作者回复: 那是你没有开启服务器端程序吧,先开启服务器端,在开启客户端。

    2019-09-04
  • W.jyao
    第一个留言的问题,程序不调用connect的话是因为recvfrom带有目标地址吧,

    作者回复: 我说的是多次调用connect哦,一次肯定是可以的。

    2019-09-03
  • 星辰
    老师 文中提到的icmp报文 发源地 是本机的网卡吗?

    作者回复: 正常情况下应该是本机的网卡地址。

    2019-09-02
收起评论
12
返回
顶部