目录
1 字节序
指多字节数据存储的存储顺序
主机字节顺序HBO: 不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。(大端和小端模式与cpu设计有关而和操作系统无关)
如 Intel x86结构下, short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12
如 IBM power PC结构下, short型数0x1234表示为12 34, int型数0x12345678表示为12 34 56 78
分类
小端格式:将低位字节数据存储在低地址
大端格式:将高位字节数据存储在地址
LSB:低地址 数字小的是低地址 0x01
MSB:高地址 数字大的是高地址 0x04
例如: int i = 0x12345678
12是高字节序(靠近0x)
78是低字节序(远离0x)
如何判断当前字节的字节序
//用来判断当前字节的字节序
#include <stdio.h>
union un
{//公用体
int a;
char b;
};
int main(int argc, char const *argv[])
{
union un myun;
myun.a = 0x1234567;
printf("a = %#x\n", myun.a);
printf("b = %#x\n", myun.b);
if(myun.b == 0x78)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
2 字节序转换函数
特点:
1、网络协议指定了通讯字节序---大端
2、只有在多字节数据处理时才需要考虑字节序
3、运行在同一台计算机上的进程相互通信时,一般不用考虑字节序
4、异构计算机之间通讯,需要转换自己的字节序转化为网络字节序
在需要字节序转换的时候一般调用特定字节序转换函数
host --> network//主机字节顺序转换为网络字节顺序
#include<arpa/inet.h>
1--htonl
2--htons
htonl 是针对32位,4个字节而言
htons 是针对16位,2个字节而言
network -->host//网络字节顺序转换为主机字节顺序
3 --ntohl
4 --ntohs
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char const *argv[])
{
int a = 0x12345678;
short b = 0x1234;
printf("%#x\n",htonl(a));
printf("%#x\n",htons(b));
return 0;
}
3 ip地址转换函数
人为识别的时候,ip地址是点分十进制的字符串类型
计算机/网络识别的时候,ip地址是无符号的整型数据
所以需要转化
inet_pton //字符串ip地址转整型数据
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char const *argv[])
{
char ip_str[]="192.138.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("in_uint = %d,%d,%d,%d\n",*ip_p,*(ip_p+1),*(ip_p+2),*(ip_p+3));
return 0;
}
inet_ntop //整型数据转字符串格式ip地址
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char const *argv[])
{
unsigned char ip_int[] = {192, 168, 3, 103};
char ip_str[16] = "";
//将32位无符号整型数据换成点分十进制的ip地址转
inet_ntop(AF_INET,&ip_int,ip_str,16);
printf("ip_s = %s\n",ip_str);
return 0;
}
inet_addr()和inet_ntoa()
inet_addr:将点分十进制的ip地址转换为整型数据,只能用在ipv4地址之间的转化
inet_ntoa:将整型数据转化位点分十进制的ip地址,只能用在ipv4地址之间的转化
4 UDP概述
面向无连接;不建立连接(效率比TCP高);限制传输64k;不可靠协议(比TCP协议简单);传输层协议
功能:
提供不同主机上的进程通讯
特点
1、发送数据之前不需要建立链接,相对TCP速度稍快些
2、不对数据包的顺序进行检查
3、没有错误检测和重传机制
- 简单的请求/应答应用程序可以使用UDP
- 对于海量数据传输不应该使用UDP
- 广播和多播应用必须使用UDP
服务对象:
主要用于“查询-应答”的服务
如:NFS(网络文件系统)、RTP(流媒体)、DNS(域名解析)
一般的语音视频通话都是使用udp来通讯的
5 网络编程接口socket
网络通信要解决的是不同主机进程间的通讯
首要问题是网络进程识别问题
以及多重协议的识别问题
随着UNIX以及类UNIX操作系统的广泛应用,socket成为了最流行的网络程序开发接口
socket作用
提供不同主机上的进程之间的通信
socket特点
socket也称“套接字”
是一个文件描述符,代表了一个通信管道的一个端点
类似对文件的操作一样,可以使用read,wirte,close等函数对socket套接字进行网络数据的收取和发送等操作
得到socket套接字(描述符)的方法,调用socket函数
socket分类
SOCK_STREAM,流式套接字,用于TCP
SOCK_DGRAM,数据报套接字,用于UDP
SOCK_RAW,原始套接字,对于其他层次的协议操作时需要使用这个类型
6 UDP编程C/S架构
7 创建socket套接字
#include <sys/socket.h>
int socket(int domain, int type, int protocol)
功能:创建一个用于网络通信的socket套接字
参数:
domain:通信域,协议族
AF_UNIX 本地通信
AF_INET ipv4网络协议
AF_INET6 ipv6网络协议
AF_PACKET 底层接口
type:socket分类 SOCK_STREAM,流式套接字,用于TCP
SOCK_DGRAM,数据报套接字,用于UDP
SOCK_RAW,原始套接字,对于其他层次的协议操作时需要使用这个类型
protocol:附加协议,如果不需要,则设置于0
返回值:
成功:文件描述符
失败:-1
特点:
在创建套接字的时候,系统不会分配端口
创建的套接字默认属性是主动,即主动发起服务的请求;当作为服务器时,往往需要修改位被动
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
//使用socket函数创建套接字
//创建一个用于UDP网络编程的套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0))==-1)
{
perror("fail tp socket");
exit(1);
}
printf("sockfd = %d\n",sockfd);
return 0;
}
8 ipv4套接字地址结构
#include <netinet/in.h>
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;//ip地址
};
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_); //协议族
in_port_t sin_port; /* Port number. 端口号 */
struct in_addr sin_addr; /* Internet address. IP地址 */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
为了使不同格式地址能传入套接字函数,地址必须要强制转换位通用套接字地址结构
9 发送数据-sendto函数
10 向“网络调试助手”发送消息
ps:需要使用在Windows下使用的网络调试助手
ip地址不能随便设置,必须是当前windows的ip地址
ubnunt下客户端代码的编写
#include <stdio.h> //printf
#include <stdlib.h>//exit
#include <sys/types.h>
#include <sys/socket.h>//socket
#include <netinet/in.h>//sockaddr_in
#include <arpa/inet.h>//hton inet_addr
#include <unistd.h> //close
#include <string.h>
#define N 512
int main(int argc, char const *argv[])
{
//第一步,创建套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0))==-1)
{
perror("fail tp socket");
exit(1);
}
printf("sockfd = %d\n",sockfd);
//第二步,填充网络服务器网络信息结构体, sockaddr_in
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;//协议族,ipv4
serveraddr.sin_addr.s_addr = inet_addr("172.31.192.1");//ip地址
serveraddr.sin_port = htons(8080);//sin_port是uint16,所以用htons
//第三步,发送数据
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 tp sendto");
exit(1);
}
}
//第四步,关闭套接字文件描述符
close(sockfd);
return 0;
}
- 修改为比较通用的情况,在gcc后,在终端中输入 ./a.out ip port
#include <stdio.h> //printf
#include <stdlib.h>//exit
#include <sys/types.h>
#include <sys/socket.h>//socket
#include <netinet/in.h>//sockaddr_in
#include <arpa/inet.h>//hton inet_addr
#include <unistd.h> //close
#include <string.h>
#define N 512
int main(int argc, char const *argv[])
{
if(argc<3)
{
fprintf(stderr,"Usage: %s ip port\n",argv[0]);
exit(1);
}
//第一步,创建套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0))==-1)
{
perror("fail tp socket");
exit(1);
}
printf("sockfd = %d\n",sockfd);
//第二步,填充网络服务器网络信息结构体, sockaddr_in
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;//协议族,ipv4
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//ip地址
serveraddr.sin_port = htons(atoi(argv[2]));//sin_port是uint16,所以用htons
//第三步,发送数据
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 tp sendto");
exit(1);
}
}
//第四步,关闭套接字文件描述符
close(sockfd);
return 0;
}