目录
背景

协议
1、为什么存在TCP/IP协议

2、TCP/IP协议每一层的作用?其实就是解决方案!!
本地一个计算机内部,内存和磁盘之间的通信距离很短,但二者也有通信协议。
北京的一台主机和上海的一台主机之间通信,会涉及许多问题,具体怎么协商解决,就由TCP/IP协议去商定的。其实本质是设备之间的距离变远了。


3、TCP/IP与操作系统的关系?协议在操作系统内部

个人再次理解协议:相当于世界上有白种人、黄种人和黑种人,人不同,但是要交流,说的语音必须相同,相当于规定英语为全国通用语,所以用英语这套标准,使得所有人都能够交流。那么网络也是,各种主机设备都不同,有的是电脑,有的是手机,有的是苹果,有的是华为,都不同,但怎么通信呢,各自都要学会统一的语音,也就是TCP/IP协议,然后各自利用这个协议就能沟通通信了。
同层之间也有协议,本质就是结构体数据。
4、通信:多种局域网通信+一种广域网通信标准
无数个局域网通信就能构成一种广域网通信
网络传输基本流程
局域网传输
引入:网有小(局域网),也有大(广域网),小网之间有通信协议,大网之间也有。
1、局域网3中传输方式:
令牌环网:谁有令牌谁就能发送数据,相当于拥有锁。

2、以太网就是临界资源,同一时刻只有一个主机能在上面发送数据。

3、两台主机在通信:其实是两个协议栈在通信

4、同层之间,好像在直接通信

5、为啥叫协议栈?栈的来源?发送方加报头的时候其实类似于入栈,接收方一层层接受解包的过程类似于出栈。
设置网卡模式为混杂模式,就可以

6、发送方:自顶向下-封装;接收方:自底向上-解包和分用

跨网络传输
1、IP地址:长期目标
MAC地址:短期目标

2、有目的IP,当前A主机就知道不是把数据发送给当前在同一个子网的其他主机,那只能向外发送,所以一定会发送给与A主机在同一局域网的路由器

3、下面是一个路由的过程
结论:路由过程中,IP地址不变,MAC地址一直在变。MAC地址只会在本局域网内有效!!
网络层往上,报文全都是一样的。不用关心底层网络不一样了。


Socket编程
1、主机间通信的本质:进程间通信,网络就是同一份资源

2、交付数据是交付给对应的进程,而进程是用端口号(port)唯一标识的
主机双方互相通信,互相唯一标识对方的方式就是对方的ip和进程的端口号(port)
socket=ip+port

3、传输层的典型代表

4、发送数据规定大端方式。保证数据是按照顺序的。



5、socket编程有服务器端和客户端
客户端:不需要绑定端口号,也不能绑定,防止APP之间绑定端口号冲突,OS会自动分配给客户端不用的端口号
服务器端:需要绑定端口号和IP地址,这是唯一的,不然客户端找不到服务器端
以上类似于,110、120这样的手机号不能变,但是用户的电话号码可以随意改变。
网络环回地址:c和s都在本地
6、服务器bind哪个ip,客户端就只能访问哪个ip,不然不能访问对应的服务器
服务器端不能绑定公网Ip,因为一个服务器可能有多个ip地址,客户端通过多个ip地址都要访问服务器上的某个端口号(多个ip指向同一个端口号),我们希望服务器能收到每个IP地址对应的客户端请求,而不是只能收到其中某一个ip地址,所以我们不应该因为ip地址导致某个请求不能被响应。
所以服务器端不需要绑定ip地址,只需要端口号。
任意ip地址绑定:服务器只需要指定端口号,不指定IP,客户端通过任意ip都能发送信息给服务器端的8080端口号。

7、UDP服务器:只负责收到消息,具体对消息的处理是由上层去处理,可以用一个回调函数去做。
cpp
//服务器端(收数据)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
int main() {
// 1. 创建UDP套接字(SOCK_DGRAM=数据报模式)
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("创建套接字失败");
return -1;
}
// 2. 绑定地址和端口
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; // IPv4
serv_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
serv_addr.sin_port = htons(8888); // 绑定8888端口
if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("绑定失败");
close(sockfd);
return -1;
}
// 3. 接收数据(无连接,不用accept)
char buf[1024];
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
ssize_t n = recvfrom(sockfd, buf, sizeof(buf)-1, 0,
(struct sockaddr*)&client_addr, &client_len);
if (n < 0) {
perror("接收数据失败");
close(sockfd);
return -1;
}
buf[n] = '\0';
std::cout << "收到客户端(" << inet_ntoa(client_addr.sin_addr) << ")数据:" << buf << std::endl;
// 4. 关闭套接字
close(sockfd);
return 0;
}
cpp
//客户端(发数据)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
int main() {
// 1. 创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("创建套接字失败");
return -1;
}
// 2. 配置服务器地址
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP
serv_addr.sin_port = htons(8888); // 服务器端口
// 3. 发送数据(无连接,直接发)
const char* msg = "Hello UDP!";
ssize_t n = sendto(sockfd, msg, strlen(msg), 0,
(struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (n < 0) {
perror("发送数据失败");
close(sockfd);
return -1;
}
std::cout << "发送了 " << n << " 字节数据" << std::endl;
// 4. 关闭套接字
close(sockfd);
return 0;
}