1. 本地字节数:主机字节序(Host Byte Order) HBO
2. 网络字节序(Network Byte Order) NBO,网络字节序规定使用大端宇节序
3. 在跨主机传输过程中,需要使用统一的字节序,即网络字节序,避免兼容性问题
主机字节序和网络字节序的转换
头文件:
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
参数:
指定要转换成网络字节序的整型:分别是32bit和16bit;
返回值:
成功,返回转换后网络字节序的整型
#include
#include
int main(int argc, const char *argv[])
{
unsigned int a = 0x87654321;
printf("%#x\n", a); //0x87654321
printf("%#x\n", htonl(a)); //0x21436587
printf("%#x\n", htons(a)); //0x2143
return 0;
}
头文件:
#include
原型:
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
参数:
uint32_t hostlong:32位网络字节序整型;
uint16_t hostshort:16位网络字节序整型;
返回值:
成功,返回转换成主机字节序的整型;
编译器会对结构体进行对齐,加速CPU取值周期,由于数据对齐也是与操作系统相关,不同的主机如果使用不同的对齐方式,会导致数据无法解析。
所以网络传输结构体的时候需要取消结构体对齐;
#pragma pack(1) 设置默认对齐系数 :() 中的参数只能填2^n (n=0,1,2,3,4,5......)
__attribute__((packed)) :取消该结构体对齐
====================================
typedef struct
{
int a;
float b;
char c
}__attribute__((packed)) buff_type;
因为涉及到跨平台,不同平台会有不同的字长
intlongint不同操作系统这两个数据类型所占的字节数可能是不一样的
解决方式:可以通过通用类型: uint8_t,uint16_t,uint32_t
如:
typedef struct
{
uint8_t a;
uint16_t b;
uint32_t c;
}buff_type;
由于IP地址本质上是一个4个字节的整数,所以在跨主机传输中也有字节序的概念
所以需要将IP地址转换成网络字节序
1. "192.168.8.189”---->本机字节序的整型0xCOA808BD---->网络字节序0xBD08A8C0
2. "192.168.31.42"---->0xC0A81F2A---->0x2A1FA8C0
3. "192168.2145"----本机字节序的整型0xCOA80291--->网络字节序的整型数0x9102A8C0
头文件:
#include
#include
#include
原型:
int inet_aton(const char *cp, struct in_addr *inp);
参数:
char *cp:源IP地址的点分十进制字符串,例如 “192.168.1.10”;
struct in_addr *inp:存储转换成网络字节序的IP;
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
返回值:
成功,返回非0;
失败,返回0;
只能转换IPv4
例子:
#define IP "192.168.1.10" //0xC0A8010A
int main(int argc, const char *argv[])
{
struct in_addr inp;
if(inet_aton(IP, &inp) == 0)
{
printf("转换失败\n");
return -1;
}
printf("%#X\n", inp.s_addr); //0X0A01A8C0
return 0;
}
头文件:
#include
原型:
int inet_pton(int af, const char *src, void *dst);
参数:
int af:协议族
AF_INET IPV4
AF_INET6 IPV6
char *src:指定要转换成网络字节序的点分十进制字符串;
void* dst
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
af == AF_INETa;
struct in6_addr
{
}
返回值:
成功,返回1;
失败,返回0或者-1,更新errno;
#define IP "192.168.1.3" //0xC0A80103 --> 0x301A8C0
struct in_addr inp;
inet_pton(AF_INET, IP, &inp);
printf("%#X\n", inp.s_addr); //0x301A8C0
头文件:
#include
#include
#include
原型:
uint32_t inet_addr(const char *cp);
参数:
char *cp:源IP地址的点分十进制字符串,例如 “192.168.1.10”;
返回值:
成功,返回转换后的网络字节序IP地址;
typedef uint32_t in_addr_t;
失败,返回INADDR_NONE (usually -1);
只能转换IPv4;
例子:
printf("%#X\n", inet_addr(IP));
头文件:
#include
#include
#include
原型:
char *inet_ntoa(struct in_addr in);
参数:
struct in_addr in:指定要转换成点分十进制字符串的IP地址;
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
返回值:
成功,返回点分十进制字符串的首地址;
只能转换IPv4;
printf("%s\n", inet_ntoa(inp));
头文件:
#include
原型:
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数:
int af:协议族
AF_INET IPV4
AF_INET6 IPV6
void* src:存储要转换成点分十进制字符串的IP首地址;
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
af == AF_INETa;
struct in6_addr
{
}
char *dst:存储转换后的结果,点分十进制的首地址;
socklen_t size:缓冲区大小,其实就是指定多大的空间用于转换IP;
返回值:
成功,返回字符串的首地址,就是dst;
失败,返回NULL,更新errno;
例子:
char ip[20];
if(inet_ntop(AF_INET, &inp, ip, sizeof(ip)) == NULL)
{
perror("ient_ntop");
return -1;
}
printf("%s\n", ip);
- 最早的套接字和共享内存,消息队列,管道一样,只能直线一个主机内部的进程间通信。
- 后期加入了TCP/IP协议,是的套接字能够支持不同主机之间的进程间通信。
- socket函数,可以在内核空间中创建两块缓冲区,供于发送数据,接收数据。
功能:在内核空间中创建一个接收缓冲区,一个发送缓冲区,并获取到套接字文件描述符;
原型:
#include /* See NOTES */
#include
int socket(int domain, int type, int protocol);
参数:
int domain:协议族,地址族:
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
int type:
SOCK_STREAM:字节流式套接字,流式套接字。默认指定的是TCP协议
SOCK_DGRAM:数据报式套接字,报式套接字。默认指定的是UDP协议。
SOCK_RAW:原始套接字,协议需要在第三个参数指定。
int protocol:默认协议填0;
IPPROTO_TCP IPPROTO_UDP
返回值:
成功,返回套接字文件描述符;
失败,返回-1,更新errno;
/*输入打印的错误类型 打印错误信息,行,具体函数*/
#define ERR_MSG(msg) do{\
fprintf(stderr, "Line:%d :%s %s\n",__LINE__, __FILE__,__func__);\
perror(msg);\
}while(0)
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{//如果创建失败则
ERR_MSG("socket");
}
函数原型:int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:将地址信息结构体与套接字我加你绑定
参数1:套接字文件描述符,就是socket的返回值
参数2:通用地址信息结构体,真实的地址信息结构体给v就地址族指定
需要手动填充绑定到套接字上的IP和端口号
参数3:真实地址信息的大小
返回值:成功返回0,失败返回-1,并置位错误码
===========================================================
地址信息结构体:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */必须填AF_INET
in_port_t sin_port; /* port in network byte order */端口号的网络字节序,1024~49151
struct in_addr sin_addr; /* internet address */本机IP地址的网络字节序
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
struct sockaddr_in sin_addr;//定义通用地址信息结构体
char TCP_ADDR[20] = {0};//存放本机IP
sin_addr.sin_family = AF_INET;//必须填AF_INET
sin_addr.sin_port = htons(8080);//端口号
printf("input address : ");
fgets(TCP_ADDR,sizeof(TCP_ADDR),stdin);
sin_addr.sin_addr.s_addr = inet_addr(TCP_ADDR);//填充IP地址
/*绑定服务器的IP和端口*/
if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
{
ERR_MSG("socket");
}
当套接字设置为被动监听状态,则会让内核维护两个队列,未完成连接的队列,已完成连接的队列
函数原型:int listen(int sockfd, int backlog);
功能:将套接字设定为被动监听状态
参数1:指定的套接字
参数2:允许同时多少个客户端建立连接;就是未完成连接的队列的容量
返回值:成功返回0,失败返回-1,并置位错误码
/*将套接字设定为被动监听状态*/
if(listen(sfd,BACKLOG) == -1)
{
ERR_MSG("listen");
}
函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:阻塞等待客户端函数,若客户端连接成功,则会从已完成连接的队列头在获取一个客户端信息
生成一个新的文件描述符,新文件描述符才是与客户端通信的文件描述符
参数1:转换成被动监听状态的文件描述符,提供该描述符才能找到已完成的队列
参数2:通用地址信息结构体,真实的地址信息结构体,根据地址族指定
函数运行后会存储连接成功的客户端的地址信息,若不想获取填NULL
参数3:真实的地址信息结构体的大小,是一个指针类型
返回值:成功回新的文件描述符,失败返回-1并置位错误码
函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:通过套接字文件描述符接收数据
参数1:通信套接字
参数2:读取任意类型的数据
参数3:指定读取的字节数
参数4:读取方式
0:阻塞方式,当填0,则该函数完全等价于read
MSG_DONTWAIT:不阻塞,如果没有数据则函数运行失败
MSG_CMSG_CLOEXEC:
MSG_ERRQUEUE:
MSG_OOB:
MSG_PEEK:
MSG_TRUNC:
MSG_WAITALL:
返回值:成功返回接收到的字节数>0,失败返回-1并置位错误码
在流式套接字:当对端关闭返回0;
/*接收来自客户端的数据*/
char str[128] = {0};
int res = 0;
printf("接收客户端数据\r\n");
while (1)
{
bzero(str,sizeof(str));//清空字符串
res = recv(newfd,str,sizeof(str),0);//阻塞接收字符串
if(res == -1)
{
perror("recv");
}else if(res == 0)
{
printf("客户端关闭\r\n");
return -1;
}else
{
printf("%s\r\n",str);
}
}
close(sfd);//关闭套接字文件
close(newfd);//关闭通讯
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
{
ERR_MSG("setsockopt");
return -1;
}
函数原型:ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数1:通信套接字文件描述符
参数2:指定要发送的数据的首地址
参数3:指定要发送的字节数
参数4:发送方式
0:阻塞方式,当填0,则该函数完全等价于read
MSG_DONTWAIT:不阻塞,如果没有数据则函数运行失败
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{//如果创建失败则
ERR_MSG("socket");
return -1;
}
函数原型:int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:连接服务器
参数1:
参数2:
参数3:
/*创建报式套接字*/
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
printf("创建报式套接字...\r\n");
if(cfd < 0)
{//如果创建失败则
ERR_MSG("socket");
return -1;
}
printf("创建成功\r\n");
/*绑定绑定服务器IP和端口到文件描述付*/
struct sockaddr_in sin_addr;
char server_IP[20] = {0};//存储服务器IP
int server_port = 0;//存储服务器端口
sin_addr.sin_family = AF_INET;
printf("input server IP : \r\n");
fgets(server_IP, sizeof(server_IP),stdin);//获取服务器IP
printf("input server port : \r\n");
scanf("%d",&server_port);//获取服务器端口
printf("绑定服务器IP和端口......\r\n");
if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
{
ERR_MSG("bind");
return -1;
}
printf("绑定成功\r\n");
函数原型:ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数1:通信套接字;
参数2:存储读取到的数据,可以读取任意类型数据
参数3:指定要读取多少个字节
参数4:读取方式
0:阻塞方式,当没有数据的时候,该函数阻塞
MSG_DONTWAIT:非阻塞方式,当没有数据的时候,该函数不阻塞,立即返回,且函数运行失败;
参数5:存储该数据包从谁哪里发过来的,存储发送方的地址信息。若不想接收可以填NULL
参数6:真实的地址信息结构体大小,注意是指针类型,所以需要在外面定一个普通变量
获取大小若地址信息结构体填NULL,则该参数填NULL:
返回值:成功返回接收到的字节数
0:对端关闭。仅限于TCP
-1:失败,并更新错误码;
/*允许端口被快速重用*/
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
{
ERR_MSG("setsockopt");
return -1;
}
函数原型:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
功能:发送数据到指定的地址
参数1:通信的套接字文件描述符
参数2:指定要发送的数据的首地址
参数3:指定要发送的字节数
参数4:接收端的地址信息结构体
参数5:
返回值:
int main(int argc, char const *argv[])
{
time_t sys_t = time(NULL);
struct tm *fomattime = localtime(&sys_t);
char time_str[50] = {0};
/*创建流式套接字*/
int sfd = socket(AF_INET, SOCK_STREAM, 0);
printf("创建流式套接字...\r\n");
if(sfd < 0)
{//如果创建失败则
ERR_MSG("socket");
return -1;
}
printf("创建成功\r\n");
/*允许端口被快速重用*/
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
{
ERR_MSG("setsockopt");
return -1;
}
/*绑定服务器的IP和端口*/
struct sockaddr_in sin_addr;//定义本机通用地址信息结构体
char TCP_ADDR[20] = {0};//存放本机IP
int port = 0;
sin_addr.sin_family = AF_INET;//必须填AF_INET
printf("input address : ");
fgets(TCP_ADDR,sizeof(TCP_ADDR),stdin);//获取IP
printf("input port : ");
scanf("%d",&port);//获取端口号
sin_addr.sin_port = htons(port);//端口号
sin_addr.sin_addr.s_addr = inet_addr(TCP_ADDR);//填充IP地址
printf("绑定服务器IP和端口......\r\n");
if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
{
ERR_MSG("bind");
return -1;
}
printf("绑定成功\r\n");
/*将套接字设定为被动监听状态*/
printf("设置监听模式\r\n");
if(listen(sfd,BACKLOG) == -1)
{
ERR_MSG("listen");
return -1;
}
printf("设置完成\r\n");
/*获取链接成功后的与客户端通信的套接字文件描述符s*/
struct sockaddr_in addr;//存储客户端的地址信息
socklen_t addr_len;
int newfd;
addr_len = sizeof(addr);
while(1)
{
printf("等待客户端链接......\r\n");
if((newfd = accept(sfd,(struct sockaddr*)&addr,&addr_len)) == -1)
{
ERR_MSG("accept");
return -1;
}
printf("客户端已链接\r\n");
printf("客户端IP : %s\r\n",inet_ntoa(addr.sin_addr));
printf("客户端端口 : %d\r\n",ntohs(addr.sin_port));
}
/*接收来自客户端的数据*/
char str[128] = {0};
int res = 0;
printf("接收客户端数据\r\n");
bzero(str,sizeof(str));//清空字符串
res = recv(newfd,str,sizeof(str),0);//阻塞接收字符串
if(res == -1)
{
perror("recv");
}else if(res == 0)
{
printf("客户端关闭\r\n");
return -1;
}else
{
GET_TIME(time_str);//获取时间戳
printf("%s : %s\r\n",time_str,str);
TCP_printf(newfd,"%s\r\n",str);
}
close(sfd);//关闭套接字文件
close(newfd);//关闭通讯
return 0;
}
int main(int argc, char const *argv[])
{
time_t sys_t = time(NULL);
struct tm *fomattime = localtime(&sys_t);
char time_str[50] = {0};
/*创建流式套接字*/
int cfd = socket(AF_INET, SOCK_STREAM, 0);
printf("创建流式套接字...\r\n");
if(cfd < 0)
{//如果创建失败则
ERR_MSG("socket");
return -1;
}
printf("创建成功\r\n");
/*连接服务器*/
struct sockaddr_in cin_addr;//定义本机通用地址信息结构体
cin_addr.sin_family = AF_INET;//必须填AF_INET
char TCP_ADDR[20] = {0};//存放服务器IP
int TCP_PORT = 0;
printf("input address : ");
fgets(TCP_ADDR,sizeof(TCP_ADDR),stdin);//获取IP
printf("input port : ");
scanf("%d",&TCP_PORT);//获取端口号
cin_addr.sin_port = htons(TCP_PORT);//端口号
cin_addr.sin_addr.s_addr = inet_addr(TCP_ADDR);//填充IP地址
printf("连接服务器......\r\n");
if(connect(cfd,(const struct sockaddr*)&cin_addr,sizeof(cin_addr)) == -1)//链接服务器
{
ERR_MSG("connect");
return -1;
}
printf("服务器连接完成\r\n");
/*接收来自服务器的数据*/
char str[128] = {0};
char buf[128] = {0};
int flage = 0;
int res = 0;
printf("接收服务器数据\r\n");
while (1)
{
bzero(str,sizeof(str));//清空字符串
fgets(str,sizeof(str),stdin);
// buff(str,sizeof(str?)
flage = send(cfd,str,sizeof(str),0);
if(flage == -1)
{
perror("send");
return 0;
}
res = recv(cfd,buf,sizeof(buf),0);
if(res == -1)
{
perror("recv");
return 0;
}else if(res == 0)
{
printf("服务器关闭\r\n");
}else
{
printf("");
}
}
close(cfd);//关闭套接字文件
return 0;
}
int main(int argc, char const *argv[])
{
time_t sys_t = time(NULL);
struct tm *fomattime = localtime(&sys_t);
char time_str[50] = {0};
/*创建报式套接字*/
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
printf("创建报式套接字...\r\n");
if(sfd < 0)
{//如果创建失败则
ERR_MSG("socket");
return -1;
}
printf("创建成功\r\n");
/*允许端口被快速重用*/
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
{
ERR_MSG("setsockopt");
return -1;
}
/*绑定绑定服务器IP和端口到文件描述付*/
struct sockaddr_in sin_addr;
char server_IP[20] = {0};//存储服务器IP
int server_port = 0;//存储服务器端口
sin_addr.sin_family = AF_INET;
printf("input server IP : ");
fgets(server_IP, sizeof(server_IP),stdin);//获取服务器IP
printf("input server port : ");
scanf("%d",&server_port);//获取服务器端口
sin_addr.sin_addr.s_addr = inet_addr(server_IP);
sin_addr.sin_port = htons(server_port);
printf("绑定服务器IP和端口......\r\n");
if(bind(sfd,(const struct sockaddr*)&sin_addr,sizeof(sin_addr))<0)
{
ERR_MSG("bind");
return -1;
}
printf("绑定成功\r\n");
/*接收消息*/
struct sockaddr_in cin_addr;
socklen_t cin_len = sizeof(cin_addr);
char str[128];
int res = 0;
printf("开始接收数据\r\n");
while(1)
{
bzero(str, sizeof(str));
if(recvfrom(sfd,str,sizeof(str),0,(struct sockaddr*)&cin_addr,&cin_len) == -1)
{
ERR_MSG("recvfrom");
return -1;
}
printf("UDP get : %s\r\n",str);//终端打印接收到的数据
sendto(sfd,str,sizeof(str),0,(struct sockaddr*)&cin_addr,cin_len);//按原地址发回
}
close(sfd);
return 0;
}
int main(int argc, char const *argv[])
{
time_t sys_t = time(NULL);
struct tm *fomattime = localtime(&sys_t);
char time_str[50] = {0};
/*创建报式套接字*/
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
printf("创建报式套接字...\r\n");
if(sfd < 0)
{//如果创建失败则
ERR_MSG("socket");
return -1;
}
printf("创建成功\r\n");
/*允许端口被快速重用*/
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))
{
ERR_MSG("setsockopt");
return -1;
}
/*绑定绑定服务器IP和端口到文件描述付*/
struct sockaddr_in cin_addr;
char server_IP[20] = {0};//存储服务器IP
int server_port = 0;//存储服务器端口
cin_addr.sin_family = AF_INET;
printf("input server IP : ");
fgets(server_IP, sizeof(server_IP),stdin);//获取服务器IP
printf("input server port : ");
scanf("%d",&server_port);//获取服务器端口
cin_addr.sin_addr.s_addr = inet_addr(server_IP);
cin_addr.sin_port = htons(server_port);
/*接收消息*/
socklen_t cin_len = sizeof(cin_addr);
char str[128];
char buf[128];
int res = 0;
printf("开始发送数据\r\n");
while(1)
{
bzero(buf, sizeof(buf));
fgets(str, sizeof(str),stdin);
if(sendto(sfd,str,sizeof(str),0,(struct sockaddr*)&cin_addr,cin_len) == -1)
{
ERR_MSG("sendto");
return -1;
}
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin_addr,&cin_len) == -1)
{
ERR_MSG("recvfrom");
return -1;
}
printf("UDP get : %s\r\n",buf);
}
close(sfd);
return 0;
}