嵌入式学习day33-网络-c/s
网络:
不同主机的 进程间通信
[西安] [成都]
QQ QQ网
无线 ---4G / 5G 卫星
有线 ---光纤
软件层面 -- 链接 --- 逻辑上
物理层面问题 --- 通路
逻辑层面问题 --- 通路
网络:
怎么通信的?
上网
www.baidu.com
网络通信
pc <-------------->百度服务器
网络 --- 因特网
tcp/ip协议:
实现不同硬件体系结构间以及不同软件间的通信
IP地址:
就是网络中身份证,用来唯一的标识一台主机
ip = 网络号 + 主机号(区号 + 电话号)
ip本质是32位的数值
点分十进制 0~255 | 0~255 | 0~255 | 0~255 |
子网掩码:
和ip地址与运算获取IP地址中的网络地址
和ip堵住或运算主机地址
区分网络
默认网关:
一个网络到另一个网络的关口
域名解析系统:
将www.baidu.com(域名,计算机名)转换成百度服务器的IP地址
通信:
物理链路
逻辑链路
分层:
- 7应用层 //你好
- 6表示层 //压缩 加密
- 5会话层 //管理一次通信过程
- 4传输层 //传输过程中网络信息控制
- 3网络层 //网络间如何通信
- 2数据链路层 //01组合的数据帧
- 1物理层 //物理通信电气特性
-
OSI模型(开放的系统互联模型): - OSI是一个参考模型(教学模型)
- 实际用到网络层次---TCP/IP模型
TCP(transmision control protocol):(类似 打电话)---可靠(1.连接 2.可靠传输 3.字节流)
特点:
a.面向连接(就是在进行通信之前,必须建立好一条逻辑上的通路)
b.提供可靠传输(四个"无",无丢失,无失序,无差错,无重复)建立连接:
tcp三次握手 目的:建立连接client ------------------ server
1 -- 我要通话 --> 1 //连接的请求
2 <--嗯,我知道了,你可以-- //对方接听电话 喂
3 -- 嗯,好的 --> //喂
可靠的连接。
类似,打电话.应用场合:
(1). 对可靠性要求较高场合
(2). QQ微信等 软件的登录时TCP(即传输控制协议):
是一种面向连接的传输层协议,它能提供高可靠性通信(即数
据无误、数据无丢失、数据无失序、数据无重复到达的通信)
适用情况:
1. 适合于对传输质量要求较高,以及传输大量数据的通信。
2. 在需要可靠数据传输的场合,通常使用TCP协议
3. MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议
UDP(Userdatagram Protocol):(类似 生活中的广播))(1.不可靠,2.无连接 3.数据报)
特点:
不提供可靠传输,
在数据发送时,不需要建立连接
应用:
(1).小数据,但是对速度要求较高
(QQ.及时文本信息,语音等),实时性要求较高场合!
(2).广播,组播//电子教师(vnc--- 广播)
(3).无线网的传输//udp
-
面向连接(connection-oriented)
指在通信之前,必须先建立一个 逻辑连接,之后才能传输数据。-
建立连接 → 传输数据 → 释放连接
-
典型代表:TCP
-
-
无连接(connectionless)
发送数据之前 不需要建立连接,直接把数据报文发出去即可。-
发数据 → 不管对方是否存在
-
典型代表:UDP
-
TCP 的“面向连接”
在 TCP 中,所谓的 连接 并不是物理电缆,而是一个 内核维护的通信状态。
-
TCP 在建立连接时会经过 三次握手,确保:
-
双方都能收发数据;
-
初始序列号确认,保证数据可靠传输;
-
为传输分配内核资源(缓冲区、控制块等)。
-
只有在连接建立后,应用程序才能 send()
和 recv()
数据。
网络通信模式:
c/s
client 客户端
server 服务器
qq 微信
qq_a <-----腾讯服务器-----> qq_b
b/s
browser 浏览器
server 服务器
p2p
peer to peer
点 对 点
tcp cs模型:
tcp通信 类似 打电话
tcp客户端: //角色 --- 主动的一方
socket //买了个电话
bind(可选) //买了sim卡
connect //拨打电话
----------------------
write //通信
read
close //挂机
tcp服务器://角色 --- 被动的一方
socket //买了个电话
bind //买了sim卡
listen //监听--待机
accept //接听
-------------------
read //通信
write
close //挂机
阶段 | 服务器端函数 | 客户端函数 |
---|---|---|
创建套接字 | socket() | socket() |
绑定地址端口 | bind() | (可省略,由内核自动分配端口) |
进入监听状态 | listen() | — |
建立连接 | accept() (被动等待) | connect() (主动发起) |
数据传输 | send()/recv() 或 write()/read() | send()/recv() 或 write()/read() |
关闭连接 | close() (关闭新旧套接字) | close() |
socket:
一种通信机制,支持不同主机、不同进程之间的数据交换。它既可以用于 本地通信(Unix 域套接字),也可以用于 网络通信(TCP/UDP 等)。
参数说明
-
domain(协议族/地址族)
-
AF_INET
:IPv4 -
AF_INET6
:IPv6 -
AF_UNIX
:本地 Unix 域套接字
-
-
type(套接字类型)
-
SOCK_STREAM
:字节流,面向连接(通常用于 TCP) -
SOCK_DGRAM
:数据报,无连接(通常用于 UDP) -
SOCK_RAW
:原始套接字,直接操作 IP 层(多用于网络工具/安全)
-
-
protocol(协议号)
-
一般为
0
,表示根据前两个参数自动选择。 -
如果
domain=AF_INET, type=SOCK_STREAM
,默认就是 TCP。 -
如果
domain=AF_INET, type=SOCK_DGRAM
,默认就是 UDP。
-
返回值
-
成功:返回一个 文件描述符(int 类型),用于标识 socket。
-
失败:返回 -1,并设置
errno
。
#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);
#include "head.h"/* 创建一个IPv4的流式套接字(TCP) */
int main(int argc, char const *argv[])
{int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){perror("socket fail");return -1;}printf("fd = %d\n", fd); //套接字文件描述符的值return 0;
}
P2P 是一种 网络通信模式,全称 Peer-to-Peer,中文叫 点对点 或 对等网络。
在这种模式下,网络中的每一台计算机(peer)都是平等的节点,既可以作为 客户端 请求服务,也可以作为 服务器 提供服务。
这与传统的 C/S(Client-Server)架构 不同:
-
在 C/S 架构中,服务器是中心节点,客户端只能向服务器请求。
-
在 P2P 架构中,没有绝对的中心,节点之间可以直接通信。
connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
1.作用
主动向服务器发起连接请求,完成 TCP 三次握手。
2.参数
-
sockfd
:由socket()
返回的套接字描述符。 -
addr
:服务器的地址结构体指针(如sockaddr_in
)。 -
addrlen
:地址结构体的大小(sizeof(struct sockaddr_in)
)。
3.返回值
-
成功:返回
0
,此时连接已建立。 -
失败:返回
-1
,并设置errno
(如ECONNREFUSED
、ETIMEDOUT
)。
struct sockaddr_in seraddr;seraddr.sin_family = AF_INET; // ipv4seraddr.sin_port = htons(8080); // 服务器端口seraddr.sin_addr.s_addr = inet_addr("192.168.32.1"); // 服务器 IP //点十分制->32位if (connect(fd, (const struct sockaddr *)&seraddr, sizeof(seraddr)) < 0){perror("connect fail");return -1;}while (1){fgets(buf, sizeof(buf), stdin);write(fd, buf, strlen(buf)+1);}
bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
1. 作用
-
把一个 套接字 (socket) 和一个 本地地址 (IP + 端口) 绑定起来。
-
只有绑定了地址,操作系统才知道:
-
服务器要在哪个端口上等待客户端连接;
-
UDP 通信时,要用哪个端口号发数据。
-
在 服务器端,bind()
是必需的;
在 客户端,如果不 bind()
,内核会自动分配一个临时端口。
2. 参数
-
sockfd
-
由
socket()
创建返回的文件描述符。 -
必须是一个还没被绑定的套接字。
-
-
addr
-
本地地址结构体指针。
-
一般用
struct sockaddr_in
(IPv4) 或struct sockaddr_in6
(IPv6)。 -
需要强制转换为
(struct sockaddr *)
。
-
-
addrlen
-
结构体的大小,通常是
sizeof(servaddr)
。
-
if (bind(fd, (struct sockaddr *)&seraddr, sizeof(seraddr)) < 0){perror("bind fail");return -1;}
litsen
int listen(int sockfd, int backlog);
1. 作用
-
把一个 主动套接字(默认)变成 被动套接字,用于在服务器端 监听客户端的连接请求。
-
调用
listen()
后,套接字就进入了 监听状态 (LISTEN),内核会为它维护一个 连接队列,保存尚未被accept()
处理的连接请求。
注意:
-
只有 面向连接的套接字(TCP,
SOCK_STREAM
) 才能调用listen()
。 -
UDP (
SOCK_DGRAM
) 是无连接的,不需要listen()
。
2. 参数
-
sockfd
-
由
socket()
创建并且已经bind()
过的套接字。 -
如果没
bind()
就listen()
,系统可能会自动分配一个随机端口(通常服务器不会这么做)。
-
-
backlog
-
未处理连接队列的最大长度。
-
内核为监听套接字维护两个队列:
-
未完成连接队列(半连接队列,三次握手还没完成)。
-
已完成连接队列(握手完成,等待
accept()
处理)。
-
-
backlog
控制这两个队列的长度(不同系统实现略有差异)。 -
常见值:
5
、128
,有些系统上会受到somaxconn
参数限制。
-
3. 返回值
-
成功:返回
0
。 -
失败:返回
-1
,并设置errno
。常见错误:-
EADDRINUSE
:地址已被占用(通常是端口冲突)。 -
EINVAL
:套接字未绑定,或类型不对(比如 UDP)。
-
if (listen(fd, 5) < 0){perror("listen fail");return -1;}
accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
1. 作用
-
从 已完成连接队列 中取出一个客户端连接,生成一个新的套接字,供服务器和该客户端通信。
-
调用时如果队列里没有连接:
-
在 阻塞模式 下:
accept()
会阻塞,直到有新的客户端连接。 -
在 非阻塞模式 下:立即返回
-1
,并设置errno=EAGAIN
或EWOULDBLOCK
。
-
👉 简单来说:
-
监听 socket(
listen
返回的那个sockfd
)只负责监听,不直接收发数据。 -
accept()
返回的新 socket 才能和客户端进行send()
/recv()
。
2. 参数
-
sockfd
-
由
socket()
和bind()
、listen()
创建的监听套接字。
-
-
addr
-
用来存放 客户端地址信息(如 IP 和端口)。
-
类型是
struct sockaddr *
,一般传struct sockaddr_in
。 -
如果不需要客户端地址,可以传
NULL
。
-
-
addrlen
-
输入输出参数:
-
传入时:指明
addr
结构体的大小。 -
返回时:存放客户端地址的实际长度。
-
-
如果不关心客户端信息,可以传
NULL
。
-
3. 返回值
-
成功:返回一个新的 套接字描述符(用于和客户端通信)。
-
失败:返回
-1
,并设置errno
,常见错误:-
EAGAIN
/EWOULDBLOCK
:非阻塞模式下,队列中没有连接。 -
ECONNABORTED
:客户端连接在完成三次握手后立即被关闭。 -
EMFILE
/ENFILE
:文件描述符耗尽(服务器太忙)。
-
struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(8080);seraddr.sin_addr.s_addr = inet_addr("192.168.32.128");int connfd = accept(fd, NULL, NULL);if (connfd < 0){perror("accept fail");return -1;} printf("---client connect---\n");
while (1)
{ struct sockaddr_in cliaddr;bzero(&cliaddr, 0);socklen_t len = sizeof(cliaddr);int connfd = accept(fd, (struct sockaddr *)&cliaddr, &len);if (connfd < 0){perror("accept fail");return -1; }printf("---client connect---\n");printf("client ip: %s\n", inet_ntoa(cliaddr.sin_addr)); // 将网络字节的地址转换成IP地址(转换为点分十进制格式的字符串)printf("client port: %d\n", ntohs(cliaddr.sin_port));
}return 0;
}