字节序
- 字节序
- 是指多字节数据的存储顺序
分类:
1、小端格式:将低位字节数据存储在低地址
2、大端格式:将高位字节数据存储在低地址
注意:
LSB:低地址
MSB:高地址
如何判断当前系统的字节序:
#include <stdio.h>
// 判断当前系统的字节序
union un
{
int a;
char b;
};
int main()
{
union un myun;
myun.a = 0x12345678;
printf("a = %#x\n", myun.a);
printf("b = %#x\n", myun.b);
if(myun.b == 0x78)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
字节序转换函数
字节序转换函数
特点:
1、网络协议指定了通讯字节序是大端
2、只有在多字节数据处理时才需要考虑字节序
3、运行在同一台计算机上的进程相互通信时,一般不用考虑字节序
4、异构计算机之间通讯,需要转换自己的字节序为网络字节序
在需要字节序转换的时候一般调用特定字节序转换函数
#include <arpa/inet.h>
host ==> network
uint32_t htonl(uint32_t hostlong);
[htonl、htons][主机字节序数据转换为网络字节序数据]
uint16_t htons(uint16_t hostshort);
network ==> host
uint32_t ntohl(uint32_t netlong);
[ntohl、ntohs][网络字节序数据转换为主机字节序数据]
uint16_t ntohs(uint16_t netshort);
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
int a = 0x12345678;
short b = 0x1234;
printf("%#x\n", htonl(a));
printf("%#x\n", htons(b));
return 0;
}
地址转换函数
- 地址转换函数
人为识别的ip地址是点分十进制的字符串形式,但是计算机或者网络中识别的ip地址是整型数据,所以需要转化。
inet_pton->字符串ip地址转整型数据
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
family 协议族
这里的 family 协议族:
1、 AF_INET IPV4网络协议
2、 AF_INET6 IPV6网络协议
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
char ip_str[] = "192.168.3.103";
unsigned int ip_int = 0;
unsigned char *ip_p = NULL;
// 将点分十进制ip地址转化为32位无符号整型数据
inet_pton(AF_INET, ip_str, &ip_int);
printf("ip_int = %d\n", ip_int);
ip_p = (char *)&ip_int;
printf("ip_int = %d,%d,%d,%d\n", *ip_p, *(ip_p+1), *(ip_p+2), *(ip_p+3));
return 0;
}
inet_ntop->整型数据转字符串格式ip地址
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
unsigned char ip_int[] = {192, 168, 3, 103};
char ip_str[16] = ""; // ***.***.***.***\0 16个字节
// 将点分十进制ip地址转化为32位无符号整型数据
inet_ntop(AF_INET, &ip_int, ip_str, 16);
printf("ip_s = %s\n", ip_str);
return 0;
}
扩展的:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
inet_addr()和inet_ntoa() 这两个函数只能用在ipv4地址的转换
inet_addr->点分十进制ip转化为整形
inet_ntoa->整形转化为点分十进制ip
UDP
UDP
…
…
socket
socket
提供不同主机上的进程之间的通信
socket特点
1、socket也称“套接字”
2、是一种文件描述符,代表了一个通信管道的一个端点
3、类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作
4、得到socket套接字(描述符)的方法调用socket()
socket分类
1、SOCK_STREAM,流式套接字,用于TCP
2、SOCK_DGRAM,数据报套接字,用于UDP
3、SOCK_RAW,原始套接字,对于其他层次的协议操作时需要使用这个类型
UDP编程CS架构
UDP编程CS架构
UDP网络编程流程
服务器:
1、创建套接字socket()
2、将服务器的ip地址、端口号与套接字进行绑定bind()
3、接收数据recvfrom()
4、发送数据sendto()
客户端:
1、创建套接字socket()
2、发送数据sendto()
3、接收数据recvfrom()
4、关闭套接字close()
socket->创建socket套接字
创建socket套接字
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
特点:
1、创建套接字时,系统不会分配端口
2、创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的
创建UDP套接字demo
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<stdlib.h>
int main()
{
// 使用socket函数创建套接字
// 创建一个用于UDP网络编程的套接字
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("fail to socket");
exit(1);
}
printf("sockfd = %d\n", sockfd);
return 0;
}
UDP编程-发送、绑定、接收数据
套接字地址结构[sockaddr_in]
- IPv4套接字地址结构
在网络编程中经常使用的结构体 sockaddr_in
char sin_zero[8] 填充,不起什么作用。
通用结构体 sockaddr
通用结构体 sockaddr
注意:以上3个结构体在Linux系统中已经定义
- 两种地址结构体使用场合
在定义源地址和目的地址结构的时候,选用struct sockaddr_in;
struct sockaddr_in my_addr;
在调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换
bind(sockfd, (struct sockaddr*) &my_addr, sizeof(my_addr));
发送数据 sendto 函数
- 发送数据-sendto函数
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *dest_addr,
socklen_t addrlen);
向网络调试助手发送数据
- 向网络调试助手发送数据
#include <stdio.h> // printf
#include <sys/types.h> //
#include <sys/socket.h> // socket
#include<stdlib.h> // exit
#include<netinet/in.h> // sockaddr_in
#include<arpa/inet.h> // htons inet_addr
#include<unistd.h> // close
#include<string.h>
#define N 128
int main(int argc, char *argv[])
{
// argc为argument count的缩写,代表参数的个数
// argv为argument vector的缩写,可以理解成参数序列
if(argc < 3)
{
fprintf(stderr, "Usage: %s ip port\n", argv[0]);
exit(1);
}
// 第一步:使用socket函数创建套接字,创建一个用于UDP网络编程的套接字
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("fail to socket");
exit(1);
}
printf("sockfd = %d\n", sockfd);
// 第二步:填充服务器网络信息结构体 sockaddr_in
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET; // 协议族,AF_INET:ipv4网络协议
// serveraddr.sin_addr.s_addr = inet_addr("173.0.2.33"); // ip地址
// serveraddr.sin_port = htons(8080);
// atoi函数是将字符串转换成整数
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址
serveraddr.sin_port = htons(atoi(argv[2])); // 端口号
// 第三步:发送数据
char buf[N] ="";
while (1)
{
fgets(buf, N, stdin);
buf[strlen(buf) - 1] = '\0'; // 把buf字符串中的\n转化为\0
if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&serveraddr, addrlen) == -1)
{
perror("fail to sendto");
exit(1);
}
}
// 第四步:关闭套接字文件描述符
close(sockfd);
return 0;
}
绑定bind函数
绑定bind函数
- 接收端:使用bind函数,来完成地址结构与socket套接字的绑定,这样ip和port就固定了
- 发送端:在sendto函数中指定接收端的ip、port,就可以发送数据了
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
bind示例
#include <stdio.h> // printf
#include <sys/types.h> //
#include <sys/socket.h> // socket
#include<stdlib.h> // exit
#include<netinet/in.h> // sockaddr_in
#include<arpa/inet.h> // htons inet_addr
#include<unistd.h> // close
#include<string.h>
// bind示例,一般服务器都需要执行bind函数
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s ip port\n", argv[0]);
exit(1);
}
// 第一步:使用socket函数创建套接字,创建一个用于UDP网络编程的套接字
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("fail to socket");
exit(1);
}
// printf("sockfd = %d\n", sockfd);
// 第二步:填充服务器网络信息结构体 sockaddr_in
struct sockaddr_in serveraddr;
// socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET; // 协议族,AF_INET:ipv4网络协议
// atoi函数是将字符串转换成整数
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址
serveraddr.sin_port = htons(atoi(argv[2])); // 端口号
// 第三步:将网络信息结构体与套接字绑定
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
{
perror("fail to bind");
exit(1);
}
return 0;
}
接收数据recvfrom函数
接收数据recvfrom函数
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
接收网络调试助手发送的数据
此时网络调试助手作为客户端,写的代码程序作为服务器
#include <stdio.h> // printf
#include <sys/types.h> //
#include <sys/socket.h> // socket
#include<stdlib.h> // exit
#include<netinet/in.h> // sockaddr_in
#include<arpa/inet.h> // htons inet_addr
#include<unistd.h> // close
#include<string.h>
#define N 128
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s ip port\n", argv[0]);
exit(1);
}
// 第一步:使用socket函数创建套接字,创建一个用于UDP网络编程的套接字
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("fail to socket");
exit(1);
}
// printf("sockfd = %d\n", sockfd);
// 第二步:填充服务器网络信息结构体 sockaddr_in
struct sockaddr_in serveraddr;
// socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET; // 协议族,AF_INET:ipv4网络协议
// atoi函数是将字符串转换成整数
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址 173.0.205.84
serveraddr.sin_port = htons(atoi(argv[2])); // 端口号 9999
// 第三步:将网络信息结构体与套接字绑定
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
{
perror("fail to bind");
exit(1);
}
// 第四步:接收数据
// char buf[N] = "";
struct sockaddr_in clientaddr;
socklen_t addrlen = sizeof(struct sockaddr_in);
while (1)
{
char buf[N] = "";
if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
{
perror("fail to recvfrom");
exit(1);
}
// 打印接收到的数据 打印客户端的ip地址和port
printf("ip:%s, port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
printf("from clients: %s\n", buf);
}
return 0;
}
UDP客户端、服务端注意点
客户端代码
#include <stdio.h> // printf
#include <sys/types.h> //
#include <sys/socket.h> // socket
#include<stdlib.h> // exit
#include<netinet/in.h> // sockaddr_in
#include<arpa/inet.h> // htons inet_addr
#include<unistd.h> // close
#include<string.h>
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s ip port\n", argv[0]);
exit(1);
}
int sockfd; // 文件描述符
struct sockaddr_in serveraddr; // 服务器网络信息结构体
socklen_t addrlen = sizeof(serveraddr);
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("fail to socket");
exit(1);
}
// 客户端自己指定的ip地址和端口号 这一步一般不需要,系统会自动分配
#if 0
struct sockaddr_in clientaddr;
clientaddr.sin_family = AF_INET; // 协议族,AF_INET:ipv4网络协议
// atoi函数是将字符串转换成整数
clientaddr.sin_addr.s_addr = inet_addr(argv[3]); // 客户端的ip地址
clientaddr.sin_port = htons(atoi(argv[4])); // 客户端的端口号
if(bind(sockfd, (struct sockaddr *)&clientaddr, addrlen) < 0)
{
perror("fail to bind");
exit(1);
}
#endif
// 填充服务器网络信息结构体
serveraddr.sin_family = AF_INET; // 协议族,AF_INET:ipv4网络协议
// atoi函数是将字符串转换成整数
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址
serveraddr.sin_port = htons(atoi(argv[2])); // 端口号
char buf[32] = "";
// 进行通信
while (1)
{
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0'; // 把buf字符串中的\n转化为\0
if(sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, addrlen) == -1)
{
perror("fail to sendto");
exit(1);
}
char text[32] = "";
if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&serveraddr, &addrlen) == -1)
{
perror("fail to recvfrom");
exit(1);
}
// 打印接收到的数据
printf("from server: %s\n", text);
}
close(sockfd); // 关闭文件描述符
return 0;
}
服务端代码
#include <stdio.h> // printf
#include <sys/types.h> //
#include <sys/socket.h> // socket
#include<stdlib.h> // exit
#include<netinet/in.h> // sockaddr_in
#include<arpa/inet.h> // htons inet_addr
#include<unistd.h> // close
#include<string.h>
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s ip port\n", argv[0]);
exit(1);
}
int sockfd; // 文件描述符
struct sockaddr_in serveraddr; // 服务器网络信息结构体
socklen_t addrlen = sizeof(serveraddr);
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("fail to socket");
exit(1);
}
// 填充服务器网络信息结构体
serveraddr.sin_family = AF_INET; // 协议族,AF_INET:ipv4网络协议
// atoi函数是将字符串转换成整数
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址
serveraddr.sin_port = htons(atoi(argv[2])); // 端口号
// 将网络信息结构体与套接字绑定
if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
{
perror("fail to bind");
exit(1);
}
// 进行通信
while (1)
{
char text[32] = "";
struct sockaddr_in clientaddr;
if(recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
{
perror("fail to recvfrom");
exit(1);
}
printf("[%s - %d]\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
strcat(text, " *_*"); // strcat(函数) 将两个char类型连接
if(sendto(sockfd, text, sizeof(text), 0, (struct sockaddr *)&clientaddr, addrlen) == -1)
{
perror("fail to sendto");
exit(1);
}
}
close(sockfd); // 关闭文件描述符
return 0;
}