@
目录- include <stdio.h>
- include <stdlib.h>
- include <string.h>
- include <sys/socket.h>
- include <netinet/ip.h>
- include <arpa/inet.h>
- include <unistd.h>
- include <netdb.h>
- define MAX 256
- define SERVER_HOST "localhost"
- define SERVER_PORT 1234
- include <stdio.h>
- include <stdlib.h>
- include <string.h>
- include <sys/socket.h>
- include <netinet/ip.h>
- include <arpa/inet.h>
- include <unistd.h>
- include <netdb.h>
- define MAX 256
- define SERVER_HOST "localhost"
- define SERVER_IP "81.70.18.119"
- define SERVER_PORT 1234
一、学习笔记
1.TCP/IP 协议
TCP/IP (Comer 1988, 2001; RFC1180 1991 )是互联网的基础。TCP 代表传输控制协议。IP 代表互联网协议。目前有两个版本的IP,即 IPv4 和 IPv6。IPv4 使用32位地址,IPv6 则 使用128位地址。本节围绕 IPv4 进行讨论,它仍然是目前使用最多的IP版本。TCP/IP 的组织结构分为几个层级,通常称为TCP/IP 堆栈,如图所示为TCP/IP的各个层级以及每一层级的代表性组件及其功能。
顶层是使用 TCP/IP 的应用程序。用于登录到远程主机的 ssh、用于交换电子邮件的 mail、用于 Web 页面的 http 等应用程序需要可靠的数据传输。通常,这类应用程序在传输层使用 TCP。另一方面,有些应用程序,例如用于查询其他主机的ping命令,则不需要可靠性。这类应用程序可以在传输层使用 UDP 来提高效率(RFC 768 1980; Comer 1988)。传 输层负责以包的形式向 IP 主机发送/接收来自 IP 主机的应用程序数据。进程与主机之间的传输层或其上方的数据传输只是逻辑传输。实际数据传输发生在互联网(IP) 和链路层,这些层将数据包分成数据帧,以便在物理网络之间传输。
2.IP 主机和 IP 地址
主机是支持TCP/IP协议的计算机或设备。每个主机由一个32位的 IP地址来标识。为 了方便起见,32位的 IP地址号通常用点记法表示,例如:134.121.64.1,其中各个字节用点 号分开。主机也可以用主机名来表示,如 dnsl.eec.wsu.edu。实际上,应用程序通常使用主机名而不是 IP 地址。在这个意义上说,主机名就等同于IP地址,因为给定其中一个,我们可以通过 DNS (域名系统)(RFC 134 1987; RFC 1035 1987 )服务器找到另一个,它将IP地址转换为主机名,反之亦然。
IP地址分为两部分,即NetworkID字段和HostID字段。根据划分,1P地址分为 A~E 类。例如,一个B类 IP 地址被划分为一个16位 NetworklD,其中前2位是10,然后是一个16位的 HostID 字段。发往IP地址的数据包首先被发送到具有相同 networkID 的路由器。路由器将通过 HostID 将数据包转发到网络中的特定主机。每个主机都有一个本地主机名 localhost,默认 IP 地址为127.0.0.1。本地主机的链路层是一个回送虚拟设备,它将每个数据包路由回同一个 localhost0 这个特性可以让我们在同一台计算机上运行 TCP/IP 应用程序,而不需要实际连接到互联网。
3.IP 协议
IP 协议用于在 IP 主机之间发送/接收数据包。IP 尽最大努力运行。IP 主机只向接收主机发送数据包,但它不能保证数据包会被发送到它们的目的地,也不能保证按顺序发送。这意味着 IP 并非可靠的协议。必要时,必须在 IP 层的上面实现可靠性。
4.IP 数据包格式
IP 数据包由 IP 头、发送方 IP 地址和接收方 IP 地址以及数据组成。每个 IP 数据包的大小最大为64KB。IP 头包含有关数据包的更多信息,例如数据包的总长度、数据包使用 TCP 还是 UDP、生存时间(TTL)计数、错误检测的校验和等。
5.路由器
IP 主机之间可能相距很远。通常不可能从一个主机直接向另一个主机发送数据包。路 由器是接收和转发数据包的特殊 IP 主机。如果有的话,一个 IP 数据包可能会经过许多路由器,或者跳跃到达某个目的地。图13.4显示了 TCP/IP 网络的拓扑结构。
每个 IP 包在 IP 报头中都有一个8位生存时间(TTL)计数,其最大值为255。在每个路由器上,TTL 会减小1。如果 TTL 减小到0,而包仍然没有到达目的地,则会直接丢弃它。这可以防止任何数据包在 IP 网络中无限循环。
6.UDP
UDP(用户数据报协议)(RFC 768 1980; Comer 1988)在 IP 上运行,用于发送/接收数据报。与 IP 类似,UDP 不能保证可靠性,但是快速高效。它可用于可靠性不重要的情况。 例如,用户可以使用ping命令探测目标主机,如ping 主机名或 ping IP 地址
ping 是一个向目标主机发送带时间戳 UDP 包的应用程序。接收到一个 pinging 数据包后,目标主机将带有时间戳的 UDP 包回送给发送者,让发送者可以计算和显示往返时间。如果目标主机不存在或宕机,当 TTL 减小为0时,路由器将会丢弃 pinging UDP 数据包。 在这种情况下,用户会发现目标主机没有任何响应。用户可以尝试再次 ping,或者断定目标主机宕机。在这种情况下,最好使用 UDP,因为不要求可靠性。
7.TCP
TCP(传输控制协议)是一种面向连接的协议,用于发送/接收数据流。TCP 也可在 IP 上运行,但它保证了可靠的数据传输。通常,UDP 类似于发送邮件的 USPS,而 TCP 类似于电话连接。
8.端口编号
在各主机上,多个应用程序(进程)可同时使用 TCP/UDP。每个应用程序由三个组成
部分唯一标识
应用程序 = (主机IP,协议,端口号)
其中,协议是 TCP 或 UDP,端口号是分配给应用程序的唯一无符号短整数。要想使用 UDP 或 TCP,应用程序(进程)必须先选择或获取一个端口号。前1024个端口号已被预留。其他端口号可供一般使用。应用程序可以选择一个可用端口号,也可以让操作系统内核分配端口号。
9.网络和主机字节序
计算机可以使用大端字节序,也可以使用小端字节序。在互联网上,数据始终按网络 序排列,这是大端。在小端机器上,例如基于Intel x86的 PC,htons()、htonl()、ntohs()、 ntohl()等库函数,可在主机序和网络序之间转换数据。例如,PC中的端口号1234按主机字节序(小端)是无符号短整数。必须先通过 htons(1234)把它转换成网络序,才能使用。相反,从互联网收到的端口号必须先通过 ntohs(port)转换为主机序。
10.TCP/IP 网络中的数据流
TCP/IP 层的数据格式
二、苏格拉底挑战
三、实践过程及截图
实践代码:
include <stdio.h>
include <stdlib.h>
include <string.h>
include <sys/socket.h>
include <netinet/ip.h>
include <arpa/inet.h>
include <unistd.h>
include <netdb.h>
define MAX 256
define SERVER_HOST "localhost"
define SERVER_PORT 1234
struct sockaddr_in server_addr;
int sock, r;
int client_init() {
printf("======= client init ==\n");
printf("1 : create a TCP socket\n");
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
printf("socket call failed\n");
exit(1);
}
printf("2 : fill server_addr with server's IP and PORT#\n");
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // localhost
server_addr.sin_port = htons(SERVER_PORT); // server port number
printf("3 : connecting to server ....\n");
r = connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (r < 0) {
printf("connect failed\n");
exit(3);
}
printf("4 : connected OK to\n");
printf("-------------------------------------------------\n");
printf("Server hostname=%s PORT=%d\n", SERVER_HOST, SERVER_PORT);
printf("-------------------------------------------------\n");
printf("= init done ==========\n");
}
int main() {
int n;
char line[MAX], ans[MAX];
client_init();
printf("***processing loop *********\n");
while (1) {
printf("input a line