嵌入式学习日记(32)Linux下的网络编程
1. 目的
不同主机,进程间通信。
2. 解决的问题
1). 主机与主机之间物理层面必须互联互通。
2.) 进程与进程在软件层面必须互联互通。
IP地址:计算机的软件地址,用来标识计算机设备
MAC地址:计算机的硬件地址(固定)
网络的端口号:标记同一主机上的不同网络进程
3. 网络协议
网络通信标准。
OSI七层模型:开放系统互联模型(open system interconnect)
理论模型:
不同体系结构设备间,网络通信的通信标准。
应用层:要传输的数据信息,如文件传输,电子邮件等
表示层:数据加密,解密操作,压缩,解压缩
会话层:建立数据传输通道, ---》会话
传输层:传输的方式 UDP TCP 端口号
网络层:实现数据路由,路径规划 路由器 ip
数据链路层:封装成帧,点对点通信(局域网内通信),差错检测 交换机 ARP
物理层:定义物理设备标准、电气特性,比如网线,光纤等传输介质 比特流 bit 0 1
TCP/IP模型:应用模型
五层:
应用层:
HTTP:超文本传输协议
HTTPS:超文本传输协议(SSL加密算法)
FTP:文件传输协议(TCP)
TFTP:简单文件传输协议(UDP)
MQTT:消息队列遥测传输(物联网协议)
DNS:域名解析服务(www.baidu.com---》IP地址)
传输层:
TCP :传输控制协议
UDP:用户数据报协议
网络层:
IP协议: IPv4 IPv6
数据链路层:
ARP : 地址解析协议
物理层:
四层:
应用层 传输层 网络层 网络接口层:
4. IP协议
网络层:
IP协议
192.168.1.128
IPv4 32位
IPv6 128位
192.168.1.140 (用户表示形式) 点分十进制
11000000 10101000 00000000 01000011 (计算机存储形式) 32bits
IP地址 = 网络位 + 主机位
192.168.0.121/24
24:网络位的位数
网络位:该IP地址位于哪个网段(局域网)内
主机位:这个网段(局域网)第几台主机
子网掩码:
如:255.255.255.0
11111111.11111111.11111111.00000000
用来区分IP地址的网络位和主机位,搭配IP地址使用。
子网掩码是1的部分对应IP地址的网络位
子网掩码是0的部分对应IP地址的主机位
192.168.1.0
网段号:
IP地址网络位不变,主机位全为0,则为该IP地址的网段号
192.168.1.3
255.255.0.0
192.168.0.0
位于
192.168.1.0 网段内(网段内的IP能直接通信)
192.168.1.3
255.0.0.0
广播号:
192.168.1.255
IP地址网络位不变,主机位全为1,则为该IP地址的广播号
192.168.1.3
255.255.255.0
广播号:
192.168.1.255(向广播号发送信息,所有局域网内IP都能收到此信息)
feiQ VNC
192.168.1.255
网关地址:
192.168.1.1
IP地址的划分:
(1)A类地址:
范围:1.0.0.0 - 126.255.255.255
子网掩码:255.0.0.0 126*2^24
用于管理大规模网络
私有IP地址:10.0.0.0 - 10.255.255.255
127.0.0.0 回环地址
(2)B类地址:
范围:128.0.0.0 - 191.255.255.255
子网掩码:255.255.0.0 2^16
管理大中规模网络
私有IP地址:172.16.0.0 - 172.31.255.255
(3)C类地址:
范围:192.0.0.0 - 223.255.255.255
子网掩码:255.255.255.0 2^8
管理中小规模网络
私有IP地址:192.168.0.0 - 192.168.255.255
(4)D类地址:
224.0.0.0 - 239.255.255.255
组播和广播使用
(5)E类地址:
240.0.0.0 - 255.255.255.254
用来进行实验
公有IP:由电信公司直接分配,并需要付费的IP地址, 可以直接访问internet
私有IP:不能直接访问internet的ip地址
节省ip地址
5. 网络端口号
端口号:16位的整形数据(unsigned short)0-65535
端口号功能:标记同一主机上的不同网络进程
分类:
1)任何TCP/IP实现所提供的服务都用1-1023之间的端口号。
http : 80
FTP: 20/21
TFPT: 69
HTTPS: 443
2)端口号从1024-49151是被注册的端口号,被IANA指定为特殊服务使用。
MQTT:1883/8883
3)从49152-65535是动态或私有端口号。
6.网络配置
1. ping ip地址/域名
查看当前主机和IP/域名所对应的这台主机网络是否联通
ping www.baidu.com
2. ifconfig
在Linux查看当前主机的IP地址
ipconfig
在Windows上查看当前主机的IP地址
3. 网络配置
1)虚拟机--》设置--》网络适配器---》桥接模式
2)编辑--》虚拟网络编辑器--》更改设置--》VMnet0---》桥接至--》当前PC正在上网的网卡上--》应用--》确定
3)修改网络配置文件
sudo vim /etc/network/interfaces
auto lo
iface lo inet loopback
auto ens33
iface ens33 inet dhcp
4)重启网络服务
sudo /etc/init.d/networking restart
5) 测试
ping www.baidu.com
7. 网络协议--UDP
UDP:传输层
用户数据报协议(User Datagram Protocol)
1)网络编程模型
B/S模型:browser/server(浏览器/服务器)
1. 客户端是通用的客户端(浏览器)
2. 一般只做服务器开发
3. 客户端要加载的数据均来自服务器
C/S模型:client/server(客户端/服务端)
1. 客户端是一个专用的客户端
2. 服务器和客户端都需开发
3. 客户端可保存资源,本地加载,无需所有数据都请求服务器
2)UDP编程流程
套接字:文件描述符 , 网络通信时,应用层可操作的端口。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建通信的套接字
参数:
domain:网络层使用什么协议族
AF_INET:IPv4
AF_INET6:IPv6
type:规定传输层的协议
SOCK_DGRAM : UDP协议
SOCK_STREAM:TCP协议
SOCK_RAW :原始套接字
protocol :0 按照默认协议方式创建
返回值:
成功:套接字
失败:-1
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能:向网络套接字发送数据
参数:
sockfd:套接字
buf: 要发送的数据的首地址
len:要发送的字节数
flags: 0:按照默认方式发送
dest_addr:接收方的地址信息(IP+端口号)
addrlen:接收方地址的大小
返回值:
成功:实际发送的字节数
失败:-1
man 7 ip
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
网络字节序:大端 network
主机字节序:小端 50000 host
0x1234
小端:0x34
0x12
大端:
0x12
0x34
uint32_t htonl(uint32_t hostlong); 主机转网络
uint16_t htons(uint16_t hostshort); 主机转网络
uint32_t ntohl(uint32_t netlong); 网络转主机
uint16_t ntohs(uint16_t netshort); 网络转主机
in_addr_t inet_addr(const char *cp);
功能:
将字符串IP地址转换成二进制IP地址形式
char *inet_ntoa(struct in_addr in);
功能:
将二进制ip转换成字符串
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:绑定自己的IP地址和端口号
参数:
sockfd:套接字
addr:需要绑定的地址
addrlen:地址大小
返回值:
成功:0
失败:-1
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
功能:从套接字上接收数据
参数:
sockfd:套接字
buf:存放接收数据的内存首地址
len:希望接收的字节数
flags:0 :按照默认方式接收(阻塞)
src_addr:发送方的地址信息
addrlen:发送发地址的指针
功能:
成功:实际接收到的字节数
失败:-1
利用UDP实现通信
客户端程序
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>int main()
{pid_t pid = fork();if(pid > 0){int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){perror("socket error:");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50001);seraddr.sin_addr.s_addr = inet_addr("192.168.0.175");char buff[1024] = {0};while(1){scanf("%s",buff);ssize_t cnt = sendto(sockfd,buff,strlen(buff),0,(struct sockaddr *)&seraddr,sizeof(seraddr));if(cnt < 0){perror("send error:");return -1;}printf("cnt = %ld\n",cnt);}close(sockfd);}else if(pid == 0){int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){perror("socket error:");return -1;}struct sockaddr_in cliaddr;cliaddr.sin_family = AF_INET;cliaddr.sin_port = htons(50000);cliaddr.sin_addr.s_addr = inet_addr("192.168.0.175");int ret = bind(sockfd,(struct sockaddr *)&cliaddr,sizeof(cliaddr));if(ret < 0){perror("bind error:");return -1;}char buff[1024] = {0};while(1){memset(buff, 0, sizeof(buff));ssize_t cnt = recvfrom(sockfd,buff,sizeof(buff),0,NULL,NULL);if(cnt <= 0){break;}printf("cnt = %ld,server->client = %s\n",cnt,buff);}close(sockfd);}return 0;
}
服务端程序
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>int main()
{pid_t pid = fork();if(pid > 0){int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){perror("socket error:");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50001);seraddr.sin_addr.s_addr = inet_addr("192.168.0.175");int ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret < 0){perror("bind error:");return -1;}char buff[1024] = {0};while(1){memset(buff, 0, sizeof(buff));ssize_t cnt = recvfrom(sockfd,buff,sizeof(buff),0,NULL,NULL);if(cnt <= 0){break;}printf("cnt = %ld,client->server = %s\n",cnt,buff);}close(sockfd);}else if(pid == 0){int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){perror("socket error:");return -1;}struct sockaddr_in cliaddr;cliaddr.sin_family = AF_INET;cliaddr.sin_port = htons(50000);cliaddr.sin_addr.s_addr = inet_addr("192.168.0.175");char buff[1024] = {0};while(1){scanf("%s",buff);ssize_t cnt = sendto(sockfd,buff,strlen(buff),0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));if(cnt < 0){perror("send error:");return -1;}printf("cnt = %ld\n",cnt);}close(sockfd);}return 0;
}