做word文档什么网站好,网站开发人员的行业分析,网站建设公司何去何从,成都高端网站目录 前言
一、基础知识
1、跨主机传输 1、字节序 2、主机字节序和网络字节序 3、IP转换
2、套接字
3、什么是UDP通信
二、如何实现UDP通信 1、socket():创建套接字 2、bind():绑定套接字 3、sendto():发送指定套接字文件数据 4、recvfrom():接收指定地址信息的数据 三…目录 前言
一、基础知识
1、跨主机传输 1、字节序 2、主机字节序和网络字节序 3、IP转换
2、套接字
3、什么是UDP通信
二、如何实现UDP通信 1、socket():创建套接字 2、bind():绑定套接字 3、sendto():发送指定套接字文件数据 4、recvfrom():接收指定地址信息的数据 三、具体实现代码 前言 在前面我们知道在使用UDP通信是在传输层选择使用UDP协议并且在传输层只有两个协议分别是UDP和TCP协议在本节中我们就来学习如何实现UDP通信
一、基础知识
1、跨主机传输 1、字节序 字节序不同类型的CPU主机内存存储多字节数据时的存在不同序列存储方式 a、小端字节序小端存储低序字节存储在内存低地址上高序字节存储在内存高地址上 b、大端字节序大端存储低序字节存储在内存高地址上高序字节存储在内存低地址上 short、int、long 有字节序的概念 char、float、字符串没有字节序的说法 如何查看电脑是大端存储还是小端存储 //查看电脑大端存储还是小端存储
#includestdio.h
int main(int argc, const char *argv[])
{int a0x87654321;char *pa;printf(a%#x\n,*p);return 0;
}我的电脑输出的是a0x21说明是小段存储其实大多数电脑都是小端存储 2、主机字节序和网络字节序 主机字节序主机本身在计算机中存储多字节数据的方式大端、小端CPU现目前电脑一般都是小端 网络字节序数据在网络中规定的传输方式网络字节序使用大端字节序方式传输 所以在跨主机传输过程中需要使用统一的字节序即网络字节序避免兼容性问题 主机字节序转换为网络字节序便于传输 #include arpa/inet.huint32_t htonl(uint32_t hostlong);
功能把 hostlong 主机字节序整型转换为网络字节序返回值就是网络字节序整数uint16_t htons(uint16_t hostshort);
功能把 hostshort 主机字节序短整型转换为网络字节序返回值就是网络字节序短整数 网络字节序转换为主机字节序便于解析识别 #include arpa/inet.huint32_t ntohl(uint32_t netlong);
功能把 netshort 网络字节序 整型 转换为主机存储的主机字节序返回值就是 主机字节序整数uint16_t ntohs(uint16_t netshort);
功能把 netshort 网络字节序短整型 转换为主机存储的主机字节序返回值就是 主机字节序 3、IP转换 在主句传输数据时会对大于两个字节的数据进行网络字节序的转换那么IP地址通常是大于两个字节的也同样要进行IP转换 IP地址整数转换为二进制网络字节序的IP地址 #include sys/socket.h
#include netinet/in.h
#include arpa/inet.hin_addr_t inet_addr(const char *cp);
功能将 ip地址字符串 转换为 IP地址整数(网络字节序IP地址)参数const char *cp要转换的 IP地址的点分十进制字符串首地址返回值
成功返回 转换后的 网络字节序的IP地址 typedef uint32_t in_addr_t; IP地址的二进制网络字节序转换为IP地址的整数 #include sys/socket.h
#include netinet/in.h
#include arpa/inet.hchar *inet_ntoa(struct in_addr in);
功能把 IP地址转换为 点分十进制字符串格式参数struct in_addr in指定要转换的IP地址 的结构体类型结构体中的成员为 IP地址类型typedef uint32_t in_addr_t;struct in_addr {in_addr_t s_addr;//IP地址 网络字节序 整数}; 2、套接字 最早套接字和共享内存、消息队列、管道一样只能实现一个主机内部的进程间通信随着TCP/IP网络模型的引入使得套接字能够支持不同主机之间的进程间通信socket函数创建一个套接字文件可以在内核空间中创建两块缓冲区用于发送数据接收数据。也包含对应的TCP/IP协议规则 使用socket()函数创建套接字文件 #include sys/types.h /* See NOTES */
#include sys/socket.hint socket(int domain, int type, int protocol);
功能创建socket套接字用于网络通信参数
参数1int domain地址族协议族AF_UNIX, AF_LOCAL Local communication unix(7)AF_INET IPv4 Internet protocols ip(7)AF_INET6 IPv6 Internet protocols ipv6(7)
参数2int type类型SOCK_STREAM字节流套接字流式套接字默认使用TCP协议SOCK_DGRAM数据报套接字报式套接字默认使用UDP协议SOCK_RAW原始套接字其协议需要在第三个参数中指定
参数3int protocol协议0使用默认协议IPPROTO_TCPIPPROTO_UDP返回值
成功返回 套接字文件描述符套接字
失败返回-1设置errno3、什么是UDP通信 根据传输层的协议不同通信的实现、通信的方式也各不相同是不同的方式完成通信 传输层TCP、UDP 通信方式有两种UDP通信 与 TCP通信 UDP通信的步骤 二、如何实现UDP通信 1、socket():创建套接字 #include sys/types.h /* See NOTES */
#include sys/socket.hint socket(int domain, int type, int protocol);
功能创建socket套接字用于网络通信在内核空间中创建套接字文件(有两个缓冲区发送缓冲区、接收缓冲区)返回该套接字文件缓冲区的文件描述符参数
参数1int domain地址族协议族AF_UNIX, AF_LOCAL Local communication unix(7)AF_INET IPv4 Internet protocols ip(7)AF_INET6 IPv6 Internet protocols ipv6(7)
参数2int type类型SOCK_STREAM字节流套接字流式套接字默认使用TCP协议SOCK_DGRAM数据报套接字报式套接字默认使用UDP协议SOCK_RAW原始套接字其协议需要在第三个参数中指定
参数3int protocol协议0使用默认协议IPPROTO_TCPIPPROTO_UDP返回值
成功返回 套接字文件描述符套接字
失败返回-1设置errno 2、bind():绑定套接字 #include sys/types.h /* See NOTES */
#include sys/socket.hint bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);功能绑定地址信息到指定的套接字文件描述符上为套接字通信指定使用的IP、port参数
参数1int sockfd指定要将地址信息绑定到哪个套接字上套接字文件描述符
参数2const struct sockaddr *addr地址信息结构体的指针通用的一个地址结构体由于存在不同的地址族选择所以地址的表示方式不一样为了统一表示所以参数为通用的地址信息结构体类型用来表示有一个地址信息结构体通用结构体struct sockaddr {sa_family_t sa_family;char sa_data[14];}真实的地址信息结构体需要根据地址族来指定不同的地址族有不同的地址信息结构体AF_INET地址族的地址信息结构体struct sockaddr_in {sa_family_t sin_family;//指定地址族AF_INETin_port_t sin_port;//端口号的网络字节序2个字节struct in_addr sin_addr;//使用的ip地址的网络字节序(结构体类型)};struct in_addr {//ip地址的网络字节序结构体uint32_t s_addr;//ip地址网络字节序};
参数3 socklen_t addrlen真实的地址信息结构体大小返回值
成功返回0
失败返回-1设置errno 3、sendto():发送指定套接字文件数据 #include sys/types.h
#include sys/socket.hssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能发送数据给指定的接收方即当前进程套接字发送数据给指定的ip、port进程参数
参数1int sockfd套接字通信使用指定的套接字描述符来发送数据数据传入哪个套接字的缓冲区用于发送
参数2const void *buf指定要发送的数据的首地址(把指定地址的数据进行发送)
参数3size_t len发送多少个字节
参数4int flags选项0阻塞方式发送当缓冲区满阻塞等待不继续执行MSG_DONTWAIT非阻塞方式发送当缓冲区满不等待返回错误失败
参数5const struct sockaddr *dest_addr地址信息结构体指定将数据发送给谁(ip、port)填写对方的地址信息地址信息根据地址族不同结构体信息内容不同如果AF_INETAF_INET地址族的地址信息结构体struct sockaddr_in {sa_family_t sin_family;//指定地址族AF_INETin_port_t sin_port;//端口号的网络字节序2个字节struct in_addr sin_addr;//使用的ip地址的网络字节序(结构体类型)};struct in_addr {//ip地址的网络字节序结构体uint32_t s_addr;//ip地址网络字节序};
参数6
socklen_t addrlen真实的地址信息结构体的大小返回值
成功返回发送的字节数
失败返回-1设置errno 4、recvfrom():接收指定地址信息的数据 #include sys/types.h
#include sys/socket.hssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
功能接收数据包同时可以接收到数据包从哪里来(额可以获取发送方的地址信息)参数
参数1int sockfd通信使用的套接字文件描述符指定获取哪个套接字的数据(对方发送发送到ip、port的套接字缓冲区)
参数2void *buf存储读取到的数据接收的数据存储的地址
参数3size_t len要读取多少个字节
参数4int flags选项 0阻塞方式接收当缓冲区为空没有接收到数据时阻塞等待不继续执行MSG_DONTWAIT非阻塞方式接收当缓冲区为空没有数据不等待返回错误失败
参数5struct sockaddr *src_addr地址信息结构体不同的地址族地址信息结构体不同不同的地址族使用对应的结构体来存储发送方的地址信息如果不想知道发送方的地址信息则填NULL
参数6socklen_t *addrlen地址信息结构体的大小指针对应空间存储如果不想获取则填NULL返回值
成功返回收到的字节数
失败返回-1设置错误码 三、具体实现代码 在实现通信之前我们使用下面网络调试助手来进行通信传输
通过网盘分享的文件scomm.exe 链接: https://pan.baidu.com/s/1OkiZLT_CeoryEZepaOSGqQ 提取码: 8a85 首先更改下面的代码 #include stdio.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h
#include netinet/in.h
#include string.h
#include pthread.h#define PORT 20000
#define DEST_PORT 10000
#define DEST_IP 192.168.124.29
#define BUF_SIZE 128int sockfd;
struct sockaddr_in destaddr;void *send_thread(void *arg) {char buf[BUF_SIZE];while (1) {bzero(buf, BUF_SIZE);fgets(buf, BUF_SIZE, stdin);sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)destaddr, sizeof(struct sockaddr_in));}return NULL;
}void *recv_thread(void *arg) {char buf[BUF_SIZE];while (1) {struct sockaddr_in rcv_addr;socklen_t rcv_addr_len sizeof(rcv_addr);int size recvfrom(sockfd, buf, BUF_SIZE - 1, 0, (struct sockaddr*)rcv_addr, rcv_addr_len);if (size 0) {buf[size] \0;printf(Received: %s\n, buf);}}return NULL;
}int main(int argc, const char *argv[]) {// 创建套接字使用UDP通信sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0) {perror(socket creation failed);return -1;}// 绑定套接字struct sockaddr_in udpaddr;udpaddr.sin_family AF_INET;udpaddr.sin_port htons(PORT);udpaddr.sin_addr.s_addr INADDR_ANY;if (bind(sockfd, (struct sockaddr*)udpaddr, sizeof(struct sockaddr_in)) 0) {perror(bind failed);close(sockfd);return -1;}// 设置对方的信息destaddr.sin_family AF_INET;destaddr.sin_port htons(DEST_PORT);destaddr.sin_addr.s_addr inet_addr(DEST_IP);// 创建发送和接收线程pthread_t send_tid, recv_tid;pthread_create(send_tid, NULL, send_thread, NULL);pthread_create(recv_tid, NULL, recv_thread, NULL);// 等待线程完成pthread_join(send_tid, NULL);pthread_join(recv_tid, NULL);// 关闭套接字close(sockfd);return 0;
}但是代码中的IP地址要更改 更改步骤 1、将下面的DEST_IP改为打开网络调试助手的IP地址 如何查找打开的网络调试助手的代码和端口 在这里查看网络端口和接收端的IP地址每个人的不一样因此要读者自己去设置