目录
[三、 字节序的转换函数](#三、 字节序的转换函数)
[编辑 3、socket函数创建一个套接字(通信的端点)](#编辑 3、socket函数创建一个套接字(通信的端点))
一、字节序
将多字节看成一个整体存储的顺序。 文件------字节流(流指针)
字节序分为:大端格式、小端格式。

大小端是系统决定的,不能自由更改。
**判断当前系统是大端还是小端:**只需要判断低地址 存放的是高字节 还是低字节

二、网络字节序和主机字节序
如果计算机没有考虑字节序的问题,导致传输的数据错误 :
解决上述问题:
网络中的数据 必须是大端。
发送数据:需要将 主机字节序 转 网络字节序 hton
接收数据:需要将 网络字节序 转 主机字节序 ntoh
三、 字节序的转换函数
头文件:#include <arpa/inet.h>
1、主机字节序 转 网络字节序(发送数据)
uint32_t htonl(uint32_t hostint32);//转IP
uint16_t htons(uint16_t hostint16);//转端口号
2、网络字节序 转 主机字节序(接收数据)
uint32_t ntohl(uint32_t netint32);//转IP
uint16_t ntohs(uint16_t netint16);//转端口

四、地址转换函数
"10.9.11.3" 点分十进制数串 (用户识别的IP地址)
计算机 是用32位无符号数据 存储IP地址 。
发送数据 将点分十进制数据 转换成 32为无符号数据 :
#include <arpa/inet.h>
int inet_pton(int family,const char *strptr, void *addrptr);
功能: 将点分十进制数串转换成32位无符号整数
参数:
- family 协议族 AF_INET‐‐>IPv4, AF_INET6‐‐>IPv6
- strptr 点分十进制数串
- addrptr 32位无符号整数的地址
返回值: 成功返回1 、 失败返回其它

接收数据 将32为无符号数据 转换成 点分十进制数串:
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len)
功能: 将32位无符号整数转换成点分十进制数串
参数:
- family 协议族 AF_INET
- addrptr 32位无符号整数
- strptr 点分十进制数串
- len strptr缓存区长度
len的宏定义
#define INET_ADDRSTRLEN 16 //for ipv4
#define INET6_ADDRSTRLEN 46 //for ipv6
返回值: 成功:则返回字符串的首地址 失败:返回NULL
五、UDP编程概述
1、socket
- 网络通信的3要素:IP、PORT、协议
- socket 他是网络编程接口的统称。
- socket 他是通信的一个端点(IP、PORT信息),也是特殊的文件描述符(套接字)。
2、udp的编程流程
3、socket函数创建一个套接字(通信的端点)
#include <sys/socket.h>
int socket(int family,int type,int protocol);
功能: 创建一个用于网络通信的socket套接字(描述符)
参数:
- family:协议族(AF_INET、AF_INET6、PF_PACKET(链路层编程接口)等)
- type:套接字类(SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW等)
- protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等
返回值: 套接字
特点: 创建套接字时,系统不会分配端口

4、IPv4地址结构体
定义 地址结构体变量
#include <netinet/in.h>
struct in_addr
{
in_addr_t s_addr;//4字节
};
struct sockaddr_in //IPv4地址结构体
{
sa_family_t sin_family;//2字节 AF_INET AF_INET6
in_port_t sin_port;//2字节
struct in_addr sin_addr;//4字节
char sin_zero[8]//8字节
};
5、通用套接字地址结构
对传递给函数的实参进行类型转换 不做赋值操作
struct sockaddr
{
sa_family_t sa_family; // 2字节
char sa_data[14] //14字节
};
6、sendto发送udp消息
ssize_t sendto(int sockfd,const void *buf,size_t nbytes,int flags,
const struct sockaddr *to, socklen_t addrlen);
功能: 向to结构体指针中指定的ip,发送UDP数据
参数:
- sockfd:套接字
- buf: 发送数据缓冲区
- nbytes: 发送数据缓冲区的大小
- flags:一般为0
- to: 指向目的主机地址结构体的指针
- addrlen:to所指向内容的长度
注意:
通过to和addrlen确定目的地址
可以发送0长度的UDP数据包
返回值: 成功:发送数据的字符数 ,失败: ‐1
例1:发送消息
#include <stdio.h>
#include <sys/socket.h> // socket
#include <unistd.h> // close
#include <string.h> // memset strlen
#include <netinet/in.h> // struct sockaddr_in
#include <arpa/inet.h> // inet_pton
int main(int argc, char const *argv[]) {
// 创建用于通信的udp套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return 0;
}
printf("sockfd = %d\n", sockfd);
// 发送数据 sendto
char buf[] = "hello sockfd";
// 目的地址信息
struct sockaddr_in dst_addr;
memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.sin_family = AF_INET; // IPv4
dst_addr.sin_port = htons(8080); // 端口
// 将目的IP 赋值到地址结构体中
inet_pton(AF_INET, "10.9.11.251", &dst_addr.sin_addr.s_addr);
// 如果套接字 没有事先bind绑定固定的IP以及端口
// 那么第一次调用sendto的时候系统会给本地主机IP,以及随机端口,作为源地址信息
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
char buf1[] = "hello buf1";
sendto(sockfd, buf1, strlen(buf1), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
close(sockfd);
return 0;
}

7、bind给套接字绑定固定的IP和端口
bind只能绑定本地机的IP地址
- 创建套接字
- bind固定IP以及端口
- sendto发送数据或recvfrom接收数据
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
功能: 将本地协议地址与sockfd绑定
参数:
- sockfd: socket套接字
- myaddr: 指向特定协议的地址结构指针
- addrlen:该地址结构的长度
返回值: 成功:返回0 ,失败:其他
#include <stdio.h>
#include <sys/socket.h> // socket
#include <unistd.h> // close
#include <string.h> // memset strlen
#include <netinet/in.h> // struct sockaddr_in
#include <arpa/inet.h> // inet_pton
int main(int argc, char const *argv[])
{
// 创建用于通信的udp套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket");
return 0;
}
printf("sockfd = %d\n", sockfd);
// bind固定的IP以及端口
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9000); // 源端口
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 通配地址
int ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if (ret < 0) {
// Handle error
}
{
perror("bind");
return 0;
}
// 发送数据 sendto
char buf[] = "hello sockfd";
// 目的地址信息
struct sockaddr_in dst_addr;
memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.sin_family = AF_INET; // IPv4
dst_addr.sin_port = htons(8080); // 端口
// 将目的IP 赋值到地址结构体中
inet_pton(AF_INET, "100.92.78.145", &dst_addr.sin_addr.s_addr);
// 如果套接字 没有事先bind绑定固定的IP以及端口
// 那么第一次调用sendto的时候系统会给本地主机IP,以及随机端口,作为源地址信息
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
char buf1[] = "hello buf1";
sendto(sockfd, buf1, strlen(buf1), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
close(sockfd);
return 0;
}
8、recvfrom接收数据(阻塞)
先bind固定的端口以及IP
ssize_t recvfrom(int sockfd, void *buf,size_t nbytes,int flags, struct sockaddr *from,socklen_t *addrlen);
功能: 接收UDP数据,并将源地址信息保存在from指向的结构中
参数:
- sockfd: 套接字
- buf:接收数据缓冲区
- nbytes:接收数据缓冲区的大小
- flags: 套接字标志(常为0)
- from: 源地址结构体指针,用来保存数据的来源
- addrlen: from所指内容的长度
注意:
通过from和addrlen参数存放数据来源信息
from和addrlen可以为NULL, 表示不保存数据来源
返回值: 成功:接收到的字符数 ,失败: ‐1
#include <stdio.h>
#include <sys/socket.h> // socket
#include <unistd.h> // close
#include <string.h> // memset strlen
#include <netinet/in.h> // struct sockaddr_in
#include <arpa/inet.h> // inet_pton
int main(int argc, char const *argv[])
{
// 创建用于通信的udp套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket");
return 0;
}
printf("sockfd = %d\n", sockfd);
// bind固定的IP以及端口
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8000); // 源端口
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 通配地址
int ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if (ret < 0)
{
perror("bind");
return 0;
}
// recvfrom接收数据
while (1)
{
unsigned char buf[1500] = "";
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
// &from_addr, &from_len可传NULL,表示不关心对方的ip
int len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&from_addr, &from_len);
if (len < 0)
{
perror("recvfrom");
return 0;
}
// 获取发送者的IP
char ip_str[16] = "";
inet_ntop(AF_INET, &from_addr.sin_addr.s_addr, ip_str, 16);
// 获取发送者的端口
unsigned short port = ntohs(from_addr.sin_port);
printf("IP:%s PORT:%hu 长度:%d 消息:%s\n", ip_str, port, len, buf);
}
close(sockfd);
return 0;
}

#include <stdio.h>
#include <sys/socket.h> // socket
#include <unistd.h> // close
#include <string.h> // memset strlen
#include <netinet/in.h> // struct sockaddr_in
#include <arpa/inet.h> // inet_pton
#include <pthread.h>
#include <stdlib.h> //htons
void *send_fun(void *arg) {
struct sockaddr_in dst_addr;
memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.sin_family = AF_INET;
int sockfd = *(int *)arg;
while (1) {
char buf[128] = "";
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = 0;
if (strncmp(buf, "sayto", 5) == 0) {
char ip_str[16] = "";
unsigned short port = 0;
sscanf(buf, "sayto %s %hu", ip_str, &port);
dst_addr.sin_port = htons(port);
inet_pton(AF_INET, ip_str, &dst_addr.sin_addr.s_addr);
continue;
} else if (strncmp(buf, "quit", 4) == 0) {
return NULL;
}
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
}
return NULL;
}
void *recv_fun(void *arg) {
int sockfd = *(int *)arg;
while (1) {
unsigned char buf[1500] = "";
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
int len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&from_addr, &from_len);
if (len < 0) {
perror("recvfrom");
return 0;
}
char ip_str[16] = "";
inet_ntop(AF_INET, &from_addr.sin_addr.s_addr, ip_str, 16);
unsigned short port = ntohs(from_addr.sin_port);
printf("IP:%s PORT:%hu 长度:%d 消息:%s\n", ip_str, port, len, buf);
}
}
int main(int argc, char const *argv[]) {
if (argc != 2) {
printf("./a.out 8000\n");
return 0;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return 0;
}
printf("sockfd = %d\n", sockfd);
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[1])); // 源端口
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 通配地址
int ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if (ret < 0) {
perror("bind");
return 0;
}
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, send_fun, (void *)&sockfd);
pthread_create(&tid2, NULL, recv_fun, (void *)&sockfd);
pthread_join(tid1, NULL);
pthread_cancel(tid2);
pthread_join(tid2, NULL);
close(sockfd);
return 0;
}

网络中的数据 必须是大端。