20201307梁辰鱼第十一周学习笔记
第十三章 TCP/IP和网络
一、知识点总结
1 TCP/IP协议
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。TCP/IP的各个层级以及每个层级的代表性组之间及其功能如下:
2 IP主机和IP地址
- 一个 IP地址的网络部分被称为网络号或者网络地址,主机可以与具有相同的网络号的设备直接通讯
- IP地址是TCP/IP网络中用来唯一标识每台主机或设备的地址,IP地址由32位(共四个八位组)的二进制组成。IP地址分为两部分,左边网络编号部分用来标识主机所在的网络;右边部分用来标识主机本身,这部分称为主机地址。连接到同一网络的主机必须拥有相同的网络编号。
3 IP协议
IP协议是TCP/IP协议族的动力,它为上层协议提供无状态、无连接、不可靠的服务。
-
无状态是指IP通信双方不同步传输数据的状态信息,因此所有IP数据报的发送、传输和接收都是相互独立、没有上下文关系的。这种服务最大的缺点就是无法处理乱序和重复的IP数据报。面向连接的协议,比如TCP协议,能够自己处理乱序的、重复的报文段,它递交给上层协议的内容绝对是有序的、正确的。无状态服务的优点也很明显:简单、高效。我们无需为保持通信的状态而分配一些内核资源,也无需每次传输数据时都携带状态信息。
-
无连接是指IP通信双方都不长久地维持对方的任何信息。这样上层协议每次发送数据的时候,都必须明确指定对方的IP地址。
-
不可靠是指IP协议不能保证IP数据报准确地到达接收端,它只是承诺尽最大努力。很多情况都可以导致IP数据报发送失败。比如,某个中转路由器发现IP数据报在网络上存活地时间太长,那么它将丢弃该报文,并返回一个ICMP错误消息给发送端。因此,使用IP服务地上层协议需要自己实现数据确认、超时重传等机制以达到可靠传输的目的。
4 ip数据包格式
5 路由器的功能
路由器的功能就是将不同的子网之间的数据进行传递。 具体功能有以下几点:
- 实现IP、TCP、UDP、ICMP等网络的互连。
- 对数据进行处理。收发数据包,具有对数据的分组过滤、复用、加密、压缩及防护墙等各项功能。
- 依据路由表的信息,对数据包下一传输目的地进行选择。
- 进行外部网关协议和其他自治域之间拓扑信息的交换。
- 实现网络管理和系统支持功能。
6 UDP
UDP提供了无连接通信,且不对传送数据包进行可靠性保证,适合于一次传输少量数据,UDP传输的可靠性由应用层负责。常用的UDP端口号有:53(DNS)、69(TFTP)、161(SNMP),使用UDP协议包括:TFTP、SNMP、NFS、DNS、BOOTP。
UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等。
7 ping命令
Linux ping 命令用于检测主机。
执行 ping 指令会使用 ICMP 传输协议,发出要求回应的信息,若远端主机的网络功能没有问题,就会回应该信息,因而得知该主机运作正常。
8 套接字
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。
套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的lP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。例如:如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)。
struct sockaddr_in {
sa_family_t sin_family; // AF_INET for TCP/IP
in_port_t sin_port; // port number
struct in_addr sin_addr;// IP address
};
struct in_addr { // internet address
uint32_t s_addr; // IP address in network byte order
);
二、实践内容与截图
service:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
#define PORT 8000
int main(void){
//定义服务器监听套接字和连接套接字
int listen_fd = -1, connect_fd = -1;//初始化为-1
struct sockaddr_in servaddr;//定义服务器对应的套接字地址
//服务器接收和发送缓冲区
char sendbuf[MAXLINE], recbuf[MAXLINE];
//初始化套接字地址结构体
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;//IPv4
servaddr.sin_port = htons(PORT);//设置监听端口
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//表示接收任意IP的连接请求
//创建套接字
if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
//如果创建套接字失败,返回错误信息
//strerror(int errnum)获取错误的描述字符串
printf("create socket error: %s(error: %d)\n", strerror(errno), errno);
exit(0);
}
//绑定套接字和本地IP地址和端口
if(bind(listen_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
//绑定出现错误
printf("bind socket error: %s(error: %d)\n", strerror(errno), errno);
exit(0);
}
//使得listen_fd变成监听描述符
if(listen(listen_fd, 10) == -1){
printf("listen socket error: %s(error: %d)\n", strerror(errno), errno);
exit(0);
}
//accept阻塞等待客户端请求
printf("等待客户端发起连接\n");
while(1){
if((connect_fd = accept(listen_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(error: %d)\n", strerror(errno), errno);
continue;
}
//可以一直保持连接
while(1){
//读取客户端发来的信息
ssize_t len = read(connect_fd, recbuf, sizeof(recbuf));
if(len < 0){
if(errno == EINTR){
continue;
}
exit(0);
}
printf("接收客户端的请求:%s\n", recbuf);
//向客户端发送信息
printf("回复客户端信息:");
fgets(sendbuf, sizeof(sendbuf), stdin);
write(connect_fd, sendbuf, sizeof(sendbuf));
}
//关闭连接套接字
close(connect_fd);
}
//关闭监听套接字
close(listen_fd);
}
client:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<time.h>
#define MAXLINE 4096
#define PORT 8000
int main(void){
time_t t1,t2;
//定义客户端套接字
int sockfd = -1;
//定义想连接的服务器的套接字地址
struct sockaddr_in servaddr;
//发送和接收数据的缓冲区
char sendbuf[MAXLINE], recbuf[MAXLINE];
//初始化服务器套接字地址
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;//IPv4
servaddr.sin_port = htons(PORT);//想连接的服务器的端口
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器的IP地址
//创建套接字
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
printf("create socket error: %s(error: %d)\n", strerror(errno), errno);
exit(0);
}
//向服务器发送连接请求
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
//连接失败
printf("connect socket error: %s(error: %d)\n", strerror(errno), errno);
exit(0);
}
while(1){
//向服务器发送信息
time(&t1);
printf("向服务器发送信息:");
fgets(sendbuf, sizeof(sendbuf), stdin);
time(&t2);
if (t2-t1>5)
{
printf("too long time didn't confirm,exit now!\n");
exit(0);
}
write(sockfd, sendbuf, sizeof(sendbuf));
//从服务器接收信息
ssize_t len = read(sockfd, recbuf, sizeof(recbuf));
if(len < 0){
if(errno == EINTR){
continue;
}
exit(0);
}
printf("服务器回应:%s\n", recbuf);
int l;
int i;
l = strlen(recbuf);
for(i=0;i<l;i++)
{
recbuf[i] = recbuf[i] + 4;
}
printf("加密服务器回应:%s\n", recbuf);
for(i=0;i<l;i++)
{
recbuf[i] = recbuf[i] - 4;
}
printf("解密服务器回应:%s\n", recbuf);
}
//关闭套接字
close(sockfd);
}
参考链接:
https://blog.csdn.net/qq_41727218/article/details/82461089
https://blog.csdn.net/m0_69354915/article/details/124394605
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术