TCP: Transmission Control Protocol,传输控制协议,类似于打电话
UDP: User Datagram Protocol,用户数据报协议,类似于写信
IP: Internet Protocol互联网协议,是上述两种协议的底层协议
IP地址(IP Address)
该设备在互联网中的唯一标识, 本质就是由32位二进制组成的整数(0~4294967295)(ipv4),也有128位二进制组成的整数(ipv6),日常生活中, 采用点分十进制表示法来描述IP地址, 就是将每个字节的二进制转换为一个十进制的整数, 不同的整数之间用小数点分隔0x01020304 => 1.2.3.4(整数=>字符串)
IP地址==网络地址+主机地址, 根据网络地址和主机地址的位数不同,分为以下4类
A类:0+7位网络地址+24位主机地址
B类:10+14位网络地址+16位主机地址
C类:110+21位网络地址+8位主机地址
D类:1110+28位多播地址
- 网络地址位数越多, 网络个数越多, 但是每个网络的主机个数比较少
- 当下的技术为了延长ipv4的使用寿命, 这种分类已经不适用了
端口号(Port Number)
网络编程需要提供: IP地址+端口号
IP地址(IP Address) :可以定位到具体的某台设备中
端口号(port number) :可以定位到具体的某个进程中,端口号是unsigned short, So 表示的范围是0~65535, 其中0~1024之间的端口号被系统占用, 因此编程中需要从1025起开始使用
字节序(byte order):
小端系统 :将低位数据存放在低位内存地址的系统
大端系统 :将低位数据存放在高位内存地址的系统
eg:对于数据0x12345678:
小端系统按照地址从小到大依次存放次序:0x78,0x56,0x34,0x12 (是78而不是87是因为最小单位是byte)
大端系统按照地址从小到大依次存放次序:0x12,0x34,0x56,0x78
- 为了实现不同主机之间的通信, 一般会将所有发送到网络中的多字节整数先转换为网络字节序再发送, 而将所有从网络中接受到的多字节整数先转换为主机字节序(本机)再解析, 而网络字节序本质就是大端系统的字节序
头文件
#include<unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<sys/un.h> #include <netinet/in.h>
#include<string.h> #include <arpa/inet.h>
htonl()/htons()/ntohl()/ntohs()
//主机和网络字节序转换
uint32_t htonl(uint32_t hostlong);converts the unsigned integer hostlong from host byte order to network byte order.
uint16_t htons(uint16_t hostshort);converts the unsigned short integer hostshort from host byte order to network byte order.
uint32_t ntohl(uint32_t netlong); converts the unsigned integer netlong from network byte order to host byte order.
uint16_t ntohs(uint16_t netshort); converts the unsigned short integer netshort from network byte order to host byte
inet_aton()/inet_addr()/inet_lnaof()/inet_netof()/inet_network()/inet_ntoa()/inet_makeaddr()
//IP地址格式转换,返回对应格式的IP地址
int · inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp); //将字符串形式的IP地址转换为整数类型
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
in_addr_t inet_network(const char *cp);
char* inet_ntoa(struct in_addr in); //将结构体类型的IP地址转换为字符串类型
struct in_addr inet_makeaddr(int net, int host);
socket()
//创建网络端点,返回socket文件描述符,失败返回-1设errno
int socket(int domain, int type, int protocol);
domain :协议族(protocol family)(网络通讯(IP)还是本地通讯(xxx.socket))
- AF_LOCAL本地通讯/AF_UNIX/
- AF_INET用于实现给予ipv4网络协议的网络协议
- AF_INET6用于实现给予ipv6网络协议的网络协议
- AF_IPXIPX - Novell protocols
- AF_NETLINKKernel user interface device
- AF_X25 ITU-T X.25 / ISO-8208 protocol
- AF_AX25 Amateur radio AX.25 protocol
- AF_ATMPVC Access to raw ATM PVCs
- AF_APPLETALK Appletalk
- AF_PACKET Low level packet interface
type :协议(TCP还是UDP)
- SOCK_STREAM //实现包括但不限于TCP协议,which is有序,可靠双向面向连接的字节流通信方式
- SOCK_DGRAM //实现包括但不限于UDP协议, which is不可靠,无连接的数据报通信方式
- SOCK_SEQPACKET
- SOCK_RAW
- SOCK_RDM
- SOCK_PACKET
protocol: 特殊协议, 一般给0
准备通信地址:
struct sockaddr{ //主要用于函数的形参类型, 很少定义结构体变量使用, 叫做通用的通信地址类型//$man bind
sa_family_t sa_family;
char sa_data[14];
}
struct sockaddr_un{ //准备本地通信的通信地址 //$man un.h
sa_family_t sun_family;//协议族,就是socket()的domain的AF_LOCAL
char sun_path[];//文件的路径
}
struct sockaddr_in{ //准备网络通信的通信地址 //$man in.h
sa_family_t sin_family; //协议族, 就是socket()的domain的AF_INET
in_port_t sin_port; //端口号
struct in_addr sin_addr; //IP地址
}
struct in_addr{
in_addr_t s_addr; //整数类型的IP地址
}
bind():
//把通信地址和socket文件描述符绑定,用在服务器端,成功返回0,失败返回-1设errno
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd: socket文件的fd(returned by socket())
addr: 需要强制类型转换成socketaddr_un或soketaddr_in, 参见上
addrlen: 通信地址的大小, 使用sizeof();
connect():
//初始化一个socket的连接,用在客户端,成功返回0,失败返回-1设errno
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd: socket文件的fd(returned by socket())
addr: 需要强制类型转换成socketaddr_un或soketaddr_in, 参见上
addrlen: 通信地址的大小, 使用sizeof();
例子
//xxx.socket/1vs1 , server
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
//1. create socket
int sockfd=socket(AF_LOCAL,SOCK_DGRAM,0);
if(-1==sockfd)
perror("socket"),exit(-1);
printf("create socket successfully\n");
//2. prepare communication addr
struct sockaddr_un addr;
addr.sun_family=AF_LOCAL;
strcpy(addr.sun_path,"a.sock"); //会自动创建a.sock文件
//3. bind socket and addr
int res=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)); //客户端是connect()
if(-1==res)
perror("bind"),exit(-1);
printf("bind successfully\n");
//4. communicate
char buf[100]={0};
res=read(sockfd,&buf,sizeof(buf)); //客户端是write()
if(-1==res)
perror("read"),exit(-1);
printf("data sent by client:%s,size is %d\n",buf,sizeof(buf));
//5. close socket
res=close(sockfd);
if(-1==res)
perror("close"),exit(-1);
printf("close successfully\n");
return 0;
}
//IP/1vs1,server
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
//1. create socket
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(-1==sockfd)
perror("socket"),exit(-1);
printf("create socket successfully\n");
//2. prepare communication addr
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(8888);
//转化端口号为网络字节序 //1~1024被系统占用,这里随便给个8888 addr.sin_addr.s_addr=inet_addr("176.43.1.34");
//转化字符串IP为整数IP //客户端也写这个服务器的地址
//3. bind socket and addr
int res=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)); //客户端是connect()
if(-1==res)
perror("bind"),exit(-1);
printf("bind successfully\n");
//4. communicate
char buf[100]={0};
res=read(sockfd,&buf,sizeof(buf)); //客户端是write()
if(-1==res)
perror("read"),exit(-1);
printf("data sent by client:%s,size is %d\n",buf,sizeof(buf));
//5. close socket
res=close(sockfd);
if(-1==res)
perror("close"),exit(-1);
printf("close successfully\n");
return 0;
}