Skip to content

ascendho/Hands-On-Network-Programming-with-C

Repository files navigation

封面

第一章:Introducing Networks and Protocols

Q1: IPv4 和 IPv6 之间的主要区别是什么?

IPv4 仅支持40亿个独特的地址,并且由于它们分配得不够高效,我们现在正在耗尽这些地址。IPv6 支持 $3.4 * 10^{38}$ 个可能的地址。IPv6 提供了许多其他改进(比如增加了scope和lifetime等属性),但这是我们网络编程直接受到影响的一个。

Q2: 通过 ipconfig 和 ifconfig 命令获得的 IP 地址,是你连接到远程服务器时服务器看到的同一个 IP 地址吗?

有时候,这些地址会匹配,但并不总是如此。如果你处于一个私有的 IPv4 网络中,那么你的路由器很可能执行了网络地址转换(NAT)。在这种情况下,远程服务器看到的是转换后的地址。如果你拥有一个公开可路由的 IPv4 或 IPv6 地址,那么远程服务器看到的地址将与你通过 ipconfig 和 ifconfig 命令报告的地址相匹配。

Q3: IPv4环回地址是什么?

IPv4的环回地址是127.0.0.1,它允许在同台机器上执行的网络程序相互通信。

Q4: IPv6环回地址是什么?

IPv6的环回地址是::1。它的工作原理与IPv4的环回地址相同。

Q5: 域名(例如,example.com)是如何解析成IP地址的?

DNS用于将域名解析成IP地址。这个协议在第5章“主机名解析和DNS”中有详细说明。

Q6: 你如何找到你的公网IP地址?

最简单的方法是访问一个能为你报告IP地址的网站。例如:

  1. http://api.ipify.org/

  2. http://ifconfig.me/ip等

Q7: 操作系统是如何知道哪个应用程序负责处理传入的数据包的?

每个IP数据包都有一个本地地址、远程地址、本地端口号、远程端口号和协议类型。操作系统通过记忆这五个属性来确定哪个应用程序应该处理任何给定的传入数据包。

第二章:Getting to Grips with Socket APIs

Q1: 什么是socket?

套接字是一个抽象概念,它代表了系统之间通信链路的一个端点。

Q2: 什么是无连接协议?什么是面向连接的协议?

面向连接的协议在更大的数据流的上下文中发送数据包。无连接协议独立地发送每个数据包,与之前的或之后的任何数据包无关。

Q3: UDP是一个无连接还是面向连接的协议?

UDP被认为是一个无连接协议。每个消息都是独立于它之前或之后的消息发送的。

Q4: TCP是无连接还是面向连接的协议?

TCP被认为是一个面向连接的协议。数据是按照顺序作为流发送和接收的。

Q5: 哪些类型的应用程序通常从使用UDP协议中受益?

UDP应用程序在牺牲可靠性的同时,能够获得更好的实时性能。它们还能够利用IP多播的优势。

Q6: 通常哪些类型的应用程序会从使用TCP协议中受益?

需要可靠数据流传输的应用程序会从TCP协议中受益。

Q7: TCP是否保证数据会成功传输?

TCP提供了一些关于可靠性的保证,但没有什么是能够真正保证数据成功传输的。例如,如果有人拔掉了你的调制解调器,没有任何协议能够克服这种情况。

Q8: 伯克利套接字和WinSock套接字之间的一些主要区别是什么?

头文件是不同的。套接字本身被表示为有符号整数与无符号整数。当socket()或accept()调用失败时,返回值是不同的。伯克利套接字也是标准文件描述符。这并不总是适用于WinSock。错误代码是不同的,并且以不同的方式检索。还有其他的区别,但这些是我们程序受影响的主要区别。

Q9: bind()函数是做什么的?

bind()函数将一个套接字与特定的本地网络地址和端口号关联起来。它几乎总是需要在服务器上使用,而客户端通常不需要。

Q10: accept()函数是做什么的?

accept()函数会阻塞,直到新的TCP客户端连接。然后它返回这个新连接的套接字。

Q11: 在TCP连接中,是客户端还是服务器先发送应用程序数据?

客户端或服务器都可以先发送数据。它们甚至可以同时发送数据。在实践中,许多客户端-服务器协议(如HTTP)的工作方式是客户端先发送请求,然后服务器发送响应。

第三章:An In-Depth Overview of TCP Connections

Q1: 我们如何判断下一次调用recv()是否会阻塞?

我们使用select()函数来指示哪些套接字准备好了可以被读取,而不会阻塞。

Q2: 如何确保select()不会阻塞超过指定的时间?

你可以给select()传递一个超时参数。

Q3: 当我们使用tcp_client程序连接到一个web服务器时,为什么我们需要在web服务器响应之前发送一个空行?

HTTP,即Web服务器的协议,期望一个空行来表示请求的结束。如果没有这个空行,服务器就不会知道客户端是否会继续发送额外的请求头。

Q4: send()函数会阻塞吗?

是的。你可以使用select()来确定何时套接字准备好了可以被写入而不会发生阻塞。或者,可以将套接字设置为非阻塞模式。

Q5: 我们如何判断一个套接字是否已经被对端断开连接?

recv()的返回值可以表明一个套接字是否已经被断开连接。

Q6: 通过recv()接收到的数据大小是否总是与通过send()发送的数据大小相同?

不。TCP是一个流式协议。没有办法知道从一次recv()调用返回的数据是通过一次还是多次send()调用发送的。

Q7: 考虑以下代码:

 recv(socket_peer, buffer, 4096, 0);
 printf(buffer);

这段代码有什么问题吗?另外,看看这段代码有什么问题:

 recv(socket_peer, buffer, 4096, 0);
 printf("%s", buffer);

由recv()返回的数据不是以空字符终止的!上述两个代码片段可能会导致printf()读取超出recv()返回的数据末尾。此外,在第一个代码示例中,接收到的数据可能包含格式说明符(例如%d),这将导致额外的内存访问违规。

第五章:Hostname Resolution and DNS

Q1: 哪个函数以一种可移植且与协议无关的方式填充了套接字编程所需的地址?

getaddrinfo() 是用于此目的的函数。

Q2: 哪个套接字编程函数可以用来将IP地址转换回名称?

getnameinfo() 函数可以用来将地址转换回名称。

Q3: DNS查询将名称转换为地址,而反向DNS查询将地址转换回名称。如果你对一个名称进行DNS查询,然后对得到的地址进行反向DNS查询,你总是能得到你开始时的名称吗?

有时你会得到相同的名称,但并不总是这样。这是因为正向和反向查找使用的是独立的记录。同时,许多名称可能指向同一个地址,但那个地址只能有一个记录指向一个单一的名称。

Q4: 用于返回名称对应的IPv4和IPv6地址的DNS记录类型是什么?

A记录类型返回IPv4地址,而AAAA记录类型返回IPv6地址。

Q5: 哪种DNS记录类型存储了有关邮件服务器的特殊信息?

MX记录类型用于返回邮件服务器信息。

Q6: getaddrinfo() 总是立即返回吗?还是它可以阻塞?

如果getaddrinfo()正在进行名称查找,它通常会阻塞。在最坏的情况下,可能需要向不同的DNS服务器发送许多UDP消息,因此这可能会造成明显的延迟。这就是为什么DNS缓存很重要的一个原因。如果你只是使用getaddrinfo()将文本IP地址转换,那么它不应该阻塞。

Q7: 当DNS响应太大而无法放入单个UDP数据包时会发生什么?

DNS响应的头部会设置TC位。这表示消息被截断了。查询应该使用TCP重新发送。

About

《C语言网络编程实践(影印版)》源码和练习题

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published