《Linux 网络编程一:网络编程导论及UDP 服务器的创建与数据接收》
Linux下的网络编程
1. 目的
实现不同主机之间进程的通信。
2. 问题
- 主机之间在物理层面必须互联互通。
- 进程之间在软件层面必须互联互通。
- IP地址:计算机的软件地址,用于标识计算机设备。
- MAC地址:计算机的硬件地址(固定)。
- 网络的端口号:标记同一主机上的不同网络进程。
3. 网络协议
网络通信标准。
- OSI七层模型(理论模型):开放系统互连模型,是不同体系结构设备间网络通信的通信标准。
- 应用层:要传输的数据,如文件传输、电子邮件。
- 表示层:数据加密解密操作、压缩解压缩操作。
- 会话层:建立数据传输通道。
- 传输层:传输的方式,如UDP、TCP、端口号。
- 网络层:实现数据路由,如路由器、IP。
- 数据链路层:封装成帧、点对点通信(局域网内通信)、差错检测,如交换机。
- 物理层:定义物理设备标准、电气特性,如网线、光纤等传输介质,比特流(bit 0/1)。
- TCP/IP模型(应用模型):
- 五层:
- 应用层:
- HTTP:超文本(数据类型较多)传输协议。
- HTTPS:超文本传输协议(SSL加密算法)。
- FTP:文件传输协议(TCP传输)。
- TFTP:简单文件传输协议(UDP传输)。
- MQTT:消息队列遥测传输协议(广泛应用于物联网协议传输)。
- DNS:域名解析服务(将一个域名转换为IP地址)。
- 传输层:
- TCP:传输控制协议(构建路径)。
- UDP:用户数据报协议(传输时不构建路径,找效率高的,可能导致乱序;不安全,可能会导致丢包)。
- 网络层:
- IP协议:
- IPv4。
- IPv6。
- IP协议:
- 数据链路层:
- ARP:地址解析协议(IP地址和MAC地址之间的转换)。
- 物理层。
- 应用层:
- 四层:
- 应用层。
- 传输层。
- 网络层。
- 网络接口层。
- 五层:
4. IP协议
网络层协议。
- IPv4:32位(用ipconfig在Windows终端上查看)。
- IPv6:128位。
- IP地址 = 网络位 + 主机位,和子网掩码搭配使用,子网掩码全为1的是网络位,全为0的是主机位。
- 示例:192.168.0.121/24(24表示网络位的位数)。
- 网络位:标识该IP地址所在的网段(局域网)。
- 主机位:标识该网段中的具体主机。
- 网段号:如192.168.1.0(网络位保留,主机位为0)。
- 广播号:如192.168.1.255(网络位不变,主机位全为1)。
- 网关地址:如192.168.1.1(网络位保留,主机位为1)。
- IP地址划分:
- A类地址:
- 范围:1.0.0.0 - 126.255.255.255。
- 子网掩码:255.0.0.0。
- 主机数量:2^24(大规模网络)。
- 私有IP地址:10.0.0.0 - 10.255.255.255。
- 回环地址:127.0.0.0。
- B类地址:
- 范围:128.0.0.0 - 191.255.255.255。
- 子网掩码:255.255.0.0。
- 主机数量:2^16(管理大中规模网络)。
- 私有IP地址:172.16.0.0 - 172.31.255.255。
- C类地址:
- 范围:192.0.0.0 - 223.255.255.255。
- 子网掩码:255.255.255.0。
- 主机数量:2^8(管理中小规模网络)。
- 私有IP地址:192.168.0.0 - 192.168.255.255。
- D类地址:
- 范围:224.0.0.0 - 239.255.255.255(组播和广播使用)。
- E类地址:
- 范围:240.0.0.0 - 255.255.255.254(用于实验)。
- A类地址:
- 共用IP:由电信公司直接分配,需要付费的IP地址,可以直接访问Internet。
- 私有IP:不能直接访问Internet的IP地址,由路由器转为共用IP。
- 这样可以节省IP地址。
5. 网络端口号
端口号:16位的整型数据(unsigned short),范围0 - 65535。
端口号功能:标记同一主机上的不同网络进程。
分类:
- 任何TCP/IP实现所提供的服务都用1 - 1023之间的端口号。
- HTTP:80。
- FTP:20/21。
- TFTP:69。
- HTTPS:443。
- MQTT:1883/8883。
- 端口号从1024 - 49151是被注册的端口号,被IANA指定为特殊(如MQTT:1883/8883)。
- 从49152 - 65535是动态或私有端口号。
- 数据包封装与解封过程
6. 网络配置
ping ip地址/域名
:查看当前主机和IP/域名所对应的网络是否联通,例如ping www.baidu.com
。ifconfig
:- Linux上查看IP地址。
- Windows上使用
ipconfig
。
- 网络配置步骤:
- 虚拟机->设置->网络适配器->桥接模式。
- 编辑->虚拟网络编辑器->更改设置->VMnet0->桥接至->当前上网的网卡->应用。
- 修改网络配置文件:
sudo vim /etc/network/interfaces
auto lo iface lo inet loopback auto ens33 iface ens33 inet dhcp
- 重启网络服务:
sudo /etc/init.d/networking restart
- 测试:
ping www.baidu.com
7. 网络层-UDP
在传输层(用户数据报协议User Datagram Protocol)。
- 网络编程模型:
- B/S模型:browser(浏览器)/server(服务器)。
- 客户端是一个通用的客户端(浏览器)。
- 一般只做服务器开发。
- 客户端要加载的数据均来自服务器。
- C/S模型:client(客户端)/server(服务端)。
- 客户端是一个专用的客户端。
- 服务器和客户端都需开发。
- 客户端保存资源,本地加载,无需所有数据都请求服务器。
- B/S模型:browser(浏览器)/server(服务器)。
- 编程:
- 客户端:
socket()
:创建通信的套接字(文件描述符,网络通信时应用层可操作的端口)。sendto()
:发送数据。recvfrom()
:接受数据。close()
:关闭套接字。
- 服务端:
socket()
:创建通信的套接字。bind()
:绑定服务器的IP和端口。sendto()
:发送数据。recvfrom()
:接受数据。close()
:关闭套接字。
- 客户端:
- 相关函数:
socket()
函数:#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。
sendto()
函数: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
:接收方地址的大小。
- 返回值:成功返回实际发送的字节数。
3.
uint32_t htonl(uint32_t hostlong); 主机转网络
uint16_t htons(uint16_t hostshort); 主机转网络
uint32_t ntohl(uint32_t netlong); 网络转主机
uint16_t ntohs(uint16_t netshort); 网络转主机
4.
in_addr_t inet_addr(const char *cp);
功能:将字符串IP地址转换成二进制IP地址形式char *inet_ntoa(struct in_addr in);
功能:将二进制ip转换成字符串
5.
网络字节序:大端 network
主机字节序:小端 50000 host
8.代码
UDP相关编程
客户端
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>int main(int argc, char const *argv[])
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);//man 7 ip/创建套接字(网络类型;UDP;0)if(sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;//种类seraddr.sin_port = htons(50000);//端口号seraddr.sin_addr.s_addr = inet_addr("192.168.0.179");//ip地址// while(1)// {// ssize_t cnt = sendto(sockfd, "1111", 13, 0, (struct sockaddr *)&seraddr, sizeof(seraddr));//发送// }char buff[1024] = {0};while(1){fgets(buff,sizeof(buff),stdin);ssize_t cnt = sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&seraddr, sizeof(seraddr));//发送if(cnt < 0){perror("sendto error");return -1;}printf("cnt = %ld\n", cnt);}close(sockfd);//关闭return 0;
}
服务端
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>int main(int argc, char const *argv[])
{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(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.192");int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("bind error");return -1;}char buff[1024] = {0};while(1){ssize_t cnt = recvfrom(sockfd, buff, sizeof(buff), 0, NULL, NULL);if(cnt < 0){perror("recvfrom error");return -1;}printf("cnt = %ld, buff = %s\n", cnt, buff);memset(buff, 0, sizeof(buff));}close(sockfd);return 0;
}