IP地址
IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
分类::
IPV4 -- 4个字节
IPV6 -- 16个字节
IPV4
IPv4的姿势
192.168.101.5 --->点分十进制
IPv4的组成
网络地址 + 主机地址
主机地址:不能全部为0或者全部为1
--->全部为0 ---网络id号
--->全部为1 ---广播地址
IPv4的分类
A类: 1个字节网络地址 + 3个字节主机地址
网络地址:以0开头
取值范围:
00000000.0.0.1 ~ 01111111.255.255.254
0.0.0.1 ~ 127.255.255.254
B类: 2个字节网络地址 + 2个字节主机地址
网络地址:以10开头
取值范围:
10000000.0.0.1 ~ 10111111.255.255.254
128.0.0.1 ~ 191.255.255.254
C类: 3个字节网络地址 + 1个字节主机地址
网络地址:以110开头
取值范围:
11000000.0.0.1 ~ 11011111.255.255.254
192.0.0.1 ~ 223.255.255.254
---->我们的IP : 192.168.101.5
D类: 4个字节网络地址 + 0个字节主机地址 ---> 多播(组播)
网络地址:以1110开头
取值范围:
11100000.0.0.0 ~ 11101111.255.255.255
224.0.0.0 ~ 239.255.255.255
E类: 网络地址: 11110开头 ,待留后用
补充: tracert www.baidu.com ----- 查看网关
子网掩码
概念
用于减少IP浪费,细化IP分类,判断若干个网络是否在同一局域网内的机制
表现形式
网络地址+主机地址
网络地址全部为1 主机地址全部为0
姿势
255.255.255.0 ----> 3个字节的网络地址 1个字节主机地址
[192.168.101.5/24] ----> /24 代表 前 24位 子网掩码的 网络地址
判断是否在同一网络下的规则
ip地址 & 子网掩码 -----> 网络id号
如果网络id号相同,则在同一网络下
例:
192.168.101.5 & 255.255.255.0 ----> 192.168.101.0
192.168.101.10 & 255.255.255.0 ----> 192.168.101.0
---->就说 192.168.101.5 和 192.168.101.10 在同一网络下
将主机地址分出一部分作为 子网地址
255.255.255.1110000
192.168.101.0011000 & 255.255.255.1110000 ----> 192.168.101.0010000
192.168.101.0111000 & 255.255.255.1110000 ----> 192.168.101.0110000
称: 192.168.101.24 与 192.168.101.56 不在同一网络
网关
概念:
网关又称网间连接器、协议转换器。默认网关在网络层以上实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关的结构也和路由器类似,不同的是互连层。网关既可以用于广域网互连,也可以用于局域网互连。其实质是一个网络通向另外一个网络的IP地址
计算机网络体系结构
OSI 模型 OSI架构(七层)
--------------
| 应用层 |
--------------
| 表示层 |
--------------
| 会话层 |
--------------
| 传输层 |
--------------
| 网络层 |
--------------
| 数据链路层 |
--------------
| 物理层 |
--------------
TCP/IP架构(四层)
--------------
| 应用层 |
--------------
| 传输层 |
--------------
| 网际层 |
--------------
| 网络接口层 |
--------------
原理体系架构(五层)
--------------
| 应用层 | 通过应用进程的交互实现网络应用
--------------
| 传输层 | 解决进程间基于网络通信的问题 (TCP UDP 端口对端口)
--------------
| 网络层 | 解决在多个网络上传输的问题 (IP地址 寻找路线)
--------------
| 数据链路层 | 解决在一个网络上的传输问题
--------------
| 物理层 | 解决使用何种信号表示比特的问题
端口号:
2个字节 65536
用户使用时端口在5001-65536之间,建议范围内越大越好
数据发送的整个流程
数据
应用层 :HTTP+数据 (HTTP报文)
传输层 :TCP+HTTP+数据 (TCP报文)
网络层 :IP+TCP+HTTP+数据 (IP报文)
数据链路层: ETH+IP+TCP+HTTP+数据+ETH (帧)
物理层:前导码+帧 将帧看成比特流
路由器转发过程
物理层 : 去掉前导码 ---》 帧
数据链路层: 去掉帧头帧尾 ---》 IP数据报
网络层: 分析IP数据报确认路线重新打包IP数据报
数据链路层: 添加帧头帧尾 ---》 帧
物理层 : 前导码+帧 将帧看成比特流
数据接受的整个流程
物理层 : 去掉前导码 ---》 帧
数据链路层:去掉帧头帧尾 ---》 IP数据报
网络层: 去掉IP协议头 ---》 TCP+HTTP+数据
传输层: 去掉端口协议头 ---》 HTTP+数据
应用层: 去掉协议头 ---》 数据
ARP请求: 通过IP地址得到物理地址
RARP请求:通过物理地址 得到 IP地址
IP数据报的生存时间
TTL : 跳 次数 8
TCP数据报
URG : 紧急数据
SYN表示建立连接,
FIN表示关闭连接,
ACK表示响应,
PSH表示有 DATA数据传输,没有缓冲,直接发送给应用层
RST表示连接重置
传输层
TCP UDP 差异
TCP:事先必须建立连接,数据传输稳定,可靠,不丢包
UDP:不需要连接,不可靠,可能会丢包
TCP三次握手与四次挥手
三次握手: 1. 客户端给服务器发送SYN连接请求
2. 服务器给客户回复了确认序列ACK,并发起SYN连接请求
3. 客户端给服务器回复了确认序列ACK
四次挥手: 1. 客户端给服务器发送FIN断开请求,并回复确认序列ACK
2. 服务器给客户端回复了确认序列ACK
3. 服务器给客户端发送FIN断开请求,并回复确认序列ACK
4. 客户端给服务器回复确认序列ACK
Socket 编程
服务器
创建Socket
*socket:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
解释:
创建socket套接字
参数:
domain : 协议
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
type:
SOCK_STREAM :流式套接字 (TCP)
SOCK_DGRAM : 数据报套接字 (UDP)
SOCK_RAW : 原始套接字
protocol : 0
返回值:
int :成功返回socket文件描述符
失败返回-1 并设置错误码
绑定IP PORT
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
解释:
绑定IP和PORT
参数:
sockfd :sock套接字
addr :struct sockaddr *
struct sockaddr {
sa_family_t sa_family; //unsigned short
char sa_data[14];
}// 加起来 16个字节
addrlen : addr的长度
返回值:
成功返回0
失败返回-1 并设置错误码
grep struct\ sockaddr_in\ {/usr/src/linux-hwe-5.4-headers-5.4.0-122/include/* -rn */
IPV4:
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr)*/
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)] ;
};
struct in_addr {
__be32 s_addr;
};
IPV6:
struct sockaddr_in6
本地:
struct sockaddr_un
获取IP和端口号
端口号
htons: 主机数据转网络数据 host to net short
ntohs: 网络数据转主机数据 net to host short
获取IP
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
uint16_t ntohs(uint16_t netshort);
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
主机地址转网络地址
cp : 点分十进制ip地址
inp : struct in_addr
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
网络地址转主机地址
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
主机地址转网络地址
cp : 点分十进制ip地址
in_addr_t :网络数据类型的ip
INADDR_ANY; //自动获取到IP
监听
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
*解释:
监听,将主动连接变成被动连接
参数:
sockfd : sock套接字
backlog: 允许同一瞬间同时连接服务器的客户数量
返回值:
成功返回0
失败返回-1 并设置错误码
阻塞等待客户端的连接
accept:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd : sock套接字
addr : struct sockaddr * 客户端的数据(IP和PORT)
addrlen: addr的长度指针 socklen_t *
返回值:
成功返回文件描述符
失败返回-1 并设置错误码
客户端
连接服务器
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
sockfd : sock套接字
addr : 服务器的IP和PORT
addrlen: addr的长度
返回值:
成功返回0
失败返回-1 并设置错误码
recv:接收
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
sockfd : 文件描述符(套接字)
buf : 读取数据存储的位置
len : 长度
flags :0 阻塞
返回值:
成功返回得到的字节数
失败返回-1 并设置error
掉线则返回0
send:发送
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd : 文件描述符(套接字)
buf : 读取数据存储的位置
len : 长度
flags :0 阻塞
返回值:
成功返回发送的字节数
失败返回-1 并设置error
补充代码:
服务器:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define PORT 12345
#define IP "192.168.101.5"
struct sockaddr_in client;
void *readMsg (void *arg)
{
int fd = *(int *)arg;
char buf[100];
int n = 10;
int ret;
while(1){
bzero(buf,sizeof(buf));
ret = recv(fd,buf,sizeof(buf),0);
if(ret>0){
printf("客户说<ip:%s>说:%s\n",inet_ntoa(client.sin_addr),buf);
n = 10;
}else if(ret==0){
printf("客户掉线了\n");
close(fd);
exit(-1);
}else{
while(n--)
continue;
close(fd);
exit(-1);
}
}
}
int main(void)
{
//创建socket套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket error");
exit(-1);
}
int on = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定IP和PORT
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = INADDR_ANY; //自动获取到IP
// server.sin_addr.s_addr = inet_addr(IP);
//inet_aton(IP,&server.sin_addr);
int ret = bind(sockfd,(struct sockaddr *)&server,sizeof(server));
if(ret){
perror("bind error");
exit(-1);
}
//监听,将主动连接变成被动连接
ret = listen(sockfd,30);
if(ret){
perror("listen error");
exit(-1);
}
//阻塞等待客户端的连接
unsigned int len = sizeof(client);
int fd = accept(sockfd,(struct sockaddr *)&client,&len);
if(fd<0){
perror("accept error");
exit(-1);
}
printf("%d服务器已上线\n",fd);
printf("客户IP:%s\n",inet_ntoa(client.sin_addr));
printf("客户PORT:%hu\n",ntohs(client.sin_port));
pthread_t thread;
pthread_create(&thread,NULL,readMsg,&fd);
pthread_detach(thread);
char buf[100];
while(1){
bzero(buf,sizeof(buf));
scanf("%s",buf);
send(fd,buf,strlen(buf),0);
}
close(fd);
close(sockfd);
return 0;
}
客户端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *readMsg (void *arg)
{
int fd = *(int *)arg;
char buf[100];
int n = 10;
int ret;
while(1){
bzero(buf,sizeof(buf));
ret = recv(fd,buf,sizeof(buf),0);
if(ret>0){
printf("服务器说:%s\n",buf);
n = 10;
}else if(ret==0){
printf("服务器掉线了\n");
close(fd);
exit(-1);
}else{
while(n--)
continue;
close(fd);
exit(-1);
}
}
}
int main(int argc,char *argv[])
{
if(argc!=3){
printf("usage:%s <ip> <port>\n",argv[0]);
exit(-1);
}
//创建socket套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket error");
exit(-1);
}
//连接服务器
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);;
int ret = connect(sockfd,(struct sockaddr *)&server,sizeof(server));
if(ret){
perror("connect error");
exit(-1);
}
pthread_t thread;
pthread_create(&thread,NULL,readMsg,&sockfd);
pthread_detach(thread);
char buf[100];
while(1){
bzero(buf,sizeof(buf));
scanf("%s",buf);
send(sockfd,buf,strlen(buf),0);
}
close(sockfd);
return 0;
}