资讯详情

资讯详情

嵌入式学习[网络编程]——UDPTCP

一、概述1、OSI模型OSI 模型Open Systems Interconnection Reference Model开放系统互连参考模型是国际标准化组织ISO提出的网络通信框架。它把网络通信过程划分为7 层便于理解、设计和排查网络问题。从下到上依次是物理层负责比特流在物理介质中的传输如网线、光纤、电压、接口。数据链路层负责相邻节点之间的可靠传输以“帧”为单位典型协议/技术有 Ethernet、MAC 地址、交换机。网络层负责路径选择和逻辑寻址以“包/分组”为单位典型协议有 IP、ICMP、路由器。传输层负责端到端的数据传输提供可靠传输、流量控制等典型协议有 TCP、UDP使用端口号。会话层负责建立、管理和终止通信会话。表示层负责数据格式转换、加密、解密、压缩等。应用层直接为用户提供网络服务如 HTTP、HTTPS、DNS、FTP、SMTP。一个常见记忆顺序是物、数、网、传、会、表、应2、TCP/IP 模型TCP/IP 模型是互联网实际使用的网络通信体系结构得名于其中最重要的两个协议TCP传输控制协议IP网际协议它通常分为4 层网络接口层负责数据在具体网络介质上的传输包括网卡、以太网、Wi-Fi 等。网际层负责逻辑寻址和路由转发核心协议是IP还包括 ICMP、ARP 等。传输层负责端到端通信主要包括TCP和UDP。应用层为用户提供网络服务包括 HTTP、HTTPS、DNS、FTP、SMTP 等。3、IP地址的组成IP地址分为 IPv4 地址和 IPv6 地址IP地址通常由网络号和主机号两部分组成。IPv4地址长32 位通常写成 4 个十进制数例192.168.1.10 每部分范围是0255IPv6地址长128 位通常写成 8 组十六进制数例2001:0db8:85a3:0000:0000:8a2e:0370:73344、常用名词解释4.1. Socket 套接字Socket 是操作系统内核提供的一种网络通信接口和通信端点。它把网络通信抽象成类似文件的操作因此可以使用read/writesend/recvclose等接口进行数据收发。Linux中使用 socket() 函数 创建套接字函数返回一个文件描述符应用程序访问这个端点的句柄常见类型SOCK_STREAM 面向连接、可靠传输通常对应 TCPSOCK_DGRAM 无连接、数据报传输通常对应 UDPSOCK_RAW 原始套接字可直接操作较底层的协议数据socket函数使用socket(AF_INET, SOCK_STREAM, 0); // TCP socket(AF_INET, SOCK_DGRAM, 0); // UDP4.2 IP地址端口号网络通信中通常需要 IP地址端口号 来确定一个通信端口。IP地址用来解决数据要发送哪个主机端口号用来解决数据要传给主机上的哪个程序。一个 IP 地址上可以同时运行多个网络程序端口号用来区分它们。IP 端口的完整含义例如192.168.1.10:8080表示IP192.168.1.10 端口80804.3 端口号的范围端口号是一个16 位无符号整数所以理论范围是0 ~ 65535不过0通常具有特殊含义实际使用的端口通常从1开始故端口号范围为 1 ~ 65535。常见端口号划分范围类型说明01023知名端口通常分配给系统服务如 80、443、22102449151注册端口分配给常见应用程序4915265535动态/私有端口客户端程序临时使用一个端口同一时间通常只能被一个程序绑定但具体还取决于 Socket 的绑定选项。端口号不是应用程序本身而是应用程序绑定或连接的通信入口。4.4 字节序4.4.1概述计算机中的数据通常以多个字节表示例如一个 32 位整数0x12345678它占 4 个字节。不同 CPU 在内存中排列这些字节的顺序可能不同而排列的顺序称为字节序。大端存储大端模式中高位字节存放在低地址内存地址 低 ---------------- 高 数据字节 12 34 56 78这称为Big Endian。小端存储小端模式中低位字节存放在低地址内存地址 低 ---------------- 高 数据字节 78 56 34 12这称为Little Endian。4.4.2 网络字节序TCP/IP 协议规定网络协议字段使用大端格式4.4.3主机字节序主机字节序是当前主流计算机 CPU 使用的字节序通常使用小端存储。4.4.4转换字节序如果不同主机直接把整数发给对方可能出现字节顺序不一致。例如发送端发送整数0x12345678小端主机在内存中可能存成78 56 34 12而网络协议规定传输时使用大端12 34 56 78如果不转换接收方可能把它解析成完全不同的数值。常用转换函数uint16_t htons(uint16_t hostshort); uint16_t ntohs(uint16_t netshort); uint32_t htonl(uint32_t hostlong); uint32_t ntohl(uint32_t netlong);含义h host主机to to转换到n network网络s short16 位l long32 位二、UDP1、概述UDPUser Datagram Protocol用户数据报协议是 TCP/IP 模型传输层中的一个无连接传输协议。它位于 IP 层之上、应用层之下主要负责将数据从一个应用程序发送到另一个应用程序。数据报特点 1.数据与数据之间有边界2.发送和接收的次数要对应3.没有写阻塞但发送太快会导致数据丢失。4.会发生读阻塞UDP 的核心特点是简单、开销小、速度快但不保证可靠传输。2、UDP 的通信过程2.1 UDP 服务端提供服务的一端 1个大致过程 1. 创建 UDP Socket2. bind 本地 IP 和端口3. recvfrom 接收数据4. sendto 发送响应5. close2.2 UDP 客户端使用服务的一端 可以是多个大致过程 1. 创建 UDP Socket2. sendto 发送数据3. recvfrom 接收响应4. close3、相关函数3.1 socketint socket(int domain, int type, int protocol)功能程序向内核提出创建一个基于内存的套接字描述符参数domain 地址族PF_INET AF_INET 互联网程序PF_UNIX AF_UNIX 单机程序type 套接字类型SOCK_STREAM 流式套接字 》TCPSOCK_DGRAM 用户数据报套接字UDPSOCK_RAW 原始套接字 》IPprotocol 协议 》0 表示自动适应应用层协议。返回值成功 返回申请的套接字id失败返回-13.2 bindint bind(int sockfd, struct sockaddr *my_addr,socklen_t addrlen);功能如果该函数在服务器端调用则表示将参数1相关的文件描述符文件与参数2 指定的接口地址关联用于从该接口接受数据。参数sockfd 需要被绑定 ip 和端口号的套接字my_addr ipv4 对应的地址结构体man 7 ipstruct _sockaddr_in ///网络地址结构 { u_short sin_family; ////地址族 u_short sin_port; ///地址端口 struct in_addr sin_addr; ///地址IP };地址端口需要大小端转换函数htons IP地址 需要使用inet_addr( )函数将IPv4 地址字符串转换为网络字节序二进制地址。socklen_t addrlen: 参数2 的长度。返回值成功 0失败 -13.3 sendtossize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);功能用于UDP协议中向对方发送数据。参数sockfd 本地的套接字idbuff 本地的数据存储一般是要发送的数据。len 要发送的数据长度 字符串通常使用strlenflags 要发送数据方式0 表示阻塞发送。dest_addr: 必选表示要发送到的目标主机信息结构体。addrlen 目标地址长度。返回值成功 发送的数据长度失败 -13.4recvfromssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);功能用于UDP协议中获取对方发送的数据。参数sockfd 本地的套接字idbuff 要存储数据的内存区一般是数组或者动态内存。len 要获取的数据长度一般是buff的大小。flags 获取方式0 阻塞src_addr 可选表示对方的地址信息结构体如果为NULL表示不关心对方地址。addrlen 对方地址信息结构体大小。如果对方地址是NULL则该值也为NULL。返回值成功 接收到的数据长度失败 -1代码示例服务端#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include arpa/inet.h #include time.h typedef struct sockaddr *(SA); int main(int argc, char **argv) { int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (-1 sockfd) { perror(socket); return 1; } struct sockaddr_in server, client; bzero(server, sizeof(server)); bzero(client, sizeof(client)); // man 7 ip server.sin_family AF_INET; server.sin_port htons(50000); // host to net short server.sin_addr.s_addr inet_addr(xxx.xxx.xxx.xxx); int ret bind(sockfd, (SA)server, sizeof(server)); if (-1 ret) { perror(socket); return 1; } while (1) { char buf[512] {0}; socklen_t len sizeof(client); recvfrom(sockfd, buf, sizeof(buf), 0, (SA)client, len); printf(cli:%s\n, buf); time_t tm; time(tm); sprintf(buf, %s %s, buf, ctime(tm)); sendto(sockfd, buf, strlen(buf), 0, (SA)client, len); } close(sockfd); return 0; }客户端#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include arpa/inet.h #include time.h typedef struct sockaddr *(SA); int main(int argc, char **argv) { int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (-1 sockfd) { perror(socket); return 1; } struct sockaddr_in server; bzero(server, sizeof(server)); // man 7 ip server.sin_family AF_INET; server.sin_port htons(50000); // host to net short server.sin_addr.s_addr inet_addr(xxx.xxx.xxx.xxx); while (1) { char buf[512] hello,this is udp test; sendto(sockfd, buf, strlen(buf), 0, (SA)server, sizeof(server)); bzero(buf, sizeof(buf)); int ret 0; recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL); printf(ser:%s, buf); fflush(stdout); sleep(1); } }三、TCP1、概述TCPTransmission Control Protocol是 TCP/IP 模型中的传输层协议通信前必须先建立连接客户端 - 服务器SYN服务器 - 客户端SYN ACK客户端 - 服务器ACK这就是常说的三次握手。通信结束后还要释放连接通常涉及四次挥手。2、模型2.1 C/S 模型基本概念 C Client客户端S Server服务器C/S 模型就是专用客户端 ------网络------ 服务器2.2 B/S模型基本概念B Browser浏览器S Server服务器B/S 模型就是浏览器 ------网络------ Web 服务器用户不需要安装专用客户端只需要使用浏览器访问网页。2.3 P2P模型基本概念P2P Peer to Peer 对等网络 点对点网络P2P 中每个节点既可以下载数据也可以上传数据即在 P2P 网络中每个节点既是下载者也是上传者典型应用迅雷下载部分游戏联机网络3、特点3.1 面向连接TCP通信前必须三次握手通信结束后需要四次挥手结束3.2 可靠传输TCP 通过以下机制尽量保证数据正确到达序号标记数据的位置确认应答接收端确认已经收到数据超时重传数据丢失后重新发送校验和检测数据是否损坏去重丢弃重复数据重排按照序号恢复正确顺序3.3 面向字节流TCP 把数据看作连续的字节流不保留发送时的数据边界通常需要自己定义消息格式。3.4 全双工通信TCP 连接建立后双方可以同时发送和接收数据3.5 点对点通信TCP 通常是一对一通信一个发送端 ---- TCP 连接 ---- 一个接收端TCP 本身不直接支持广播和组播通常用于单播通信3.6 提供流量控制和拥塞控制4、相关函数4.1 socket与UDP相同4.2 bindint bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);功能如果该函数在服务器端调用则表示将参数1相关 的文件描述符文件与参数2 指定的接口地址关联 用于从该接口接受数据。4.3 listenint listen(int sockfd, int backlog);功能在参数1所在的套接字id上监听等待链接。参数sockfd 套接字id backlog 允许三次握手的排队数。返回值成功 0 失败 -14.4 acceptint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);功能从已经监听到的队列中取出有效的客户端链接并 接入到当前程序。参数sockfd 套接字idaddr 如果该值为NULL 表示不论客户端是谁都接入。如果要获取客户端信息则事先定义变量 并传入变量地址函数执行完毕将会将客户端 信息存储到该变量中。addrlen 参数2的长度如果参数2为NULL则该值 也为NULL如果参数不是NULLlen; 一定要写成len sizeof(struct sockaddr);返回值成功 返回一个用于通信的新套接字id 通信套接字 从该代码之后所有通信都基于该id失败 -14.5 recvssize_t recv(int sockfd, void *buf, size_t len, int flags);功能从指定的sockfd套接字中以flags方式获取长度为len字节的数据到指定的buff内存中。参数sockfd 如果服务器则是accept的返回值的新fd 如果客户端则是socket的返回值旧fdbuff 用来存储数据的本地内存一般是数组或者 动态内存。len 要获取的数据长度flags 获取数据的方式0 表示阻塞接受。返回值成功 表示接受的数据长度一般小于等于len失败 -14.6 sendint send(int sockfd, const void *msg, size_t len, int flags);功能从msg所在的内存中获取长度为len的数据以flags方式写入到sockfd对应的套接字中。参数sockfd 如果是服务器则是accept的返回值新fd 如果是客户端则是sockfd的返回值旧fdmsg 要发送的消息len 要发送的消息长度flags 消息的发送方式。返回值成功 发送的字符长度失败 -14.7 connect客户端 自动连接服务器。int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能该函数固定有客户端使用表示从当前主机向目标 主机发起链接请求。参数sockfd 本地socket创建的套接子idaddr 远程目标主机的地址信息。addrlen: 参数2的长度。返回值成功 0 失败 -1注意1、服务端流程socket----bind----listen----accept----recv----send----close客户端流程socket----connect----send----recv----close2、三次握手中的函数listen() 让内核准备接收连接 ——服务端connect() 让内核主动发起连接 ——客户端accept() 获取已经建立的连接 ——服务端三次握手 TCP 内核自动完成3、服务端accep获取的为通信套接字通信套接字已经包含客户端的地址后续recv与send只需要使用通信套接字即可不用像UDP一样传输地址。实例代码服务端#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include time.h #include arpa/inet.h typedef struct sockaddr *(SA); int main(int argc, char **argv) { // 监听套接字 int listfd socket(AF_INET, SOCK_STREAM, 0); if (-1 listfd) { perror(socket); return 1; } struct sockaddr_in ser, cli; bzero(ser, sizeof(ser)); bzero(cli, sizeof(cli)); ser.sin_family AF_INET; ser.sin_port htons(50000); ser.sin_addr.s_addr inet_addr(xxx.xxx.xxx.xxx); int ret bind(listfd, (SA)ser, sizeof(ser)); if (-1 ret) { perror(bind); return 1; } listen(listfd, 3); //最多容纳 3 个等待处理的连接 socklen_t len sizeof(cli); //通信套接字 后续接收都用通信套接字 int conn accept(listfd, (SA)cli, len); if (-1 conn) { perror(accept); return 1; } while (1) { char buf[256] {0}; int ret recv(conn, buf, sizeof(buf), 0); // conn包含地址不需要像udp一样传地址 if (ret 0) { printf(cli offline\n); break; } time_t tm; time(tm); sprintf(buf, %s %s, buf, ctime(tm)); send(conn, buf, strlen(buf), 0); } close(listfd); close(conn); return 0; }客户端#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include time.h #include netinet/in.h #include arpa/inet.h #define SENDIP xxx.xxx.xxx.xxx typedef struct sockaddr *(SA); int main(int argc, char **argv) { int sockfd socket(AF_INET, SOCK_STREAM, 0); if (-1 sockfd) { perror(socket); return 1; } struct sockaddr_in ser; bzero(ser, sizeof(ser)); ser.sin_family AF_INET; ser.sin_port htons(50000); ser.sin_addr.s_addr inet_addr(SENDIP); int ret connect(sockfd, (SA)ser, sizeof(ser)); if (-1 ret) { perror(connect); return 1; } while (1) { char buf[256] tcp test......; send(sockfd, buf, strlen(buf), 0); bzero(buf, sizeof(buf)); recv(sockfd, buf, sizeof(buf), 0); printf(%s, buf); sleep(1); } close(sockfd); return 0; }
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →