网络基础知识

1.网络主要是为了解决远距离、跨主机的数据通讯;


2.网络体系结构

为了保证数据在网络中完整性,安全性传输,网络将整个传输过程“分而治之”
1)OSI提供的七层网络的体系结构
2)TCP/IP四层网络体系结构
image.png

3.传输层协议:

1.TCP(传输控制协议)
TCP是传输层提供给应用层面向连接的数据传输服务;
面向连接:数据传输必须在建立连接的基础上
三次握手过程:
image.png
四次挥手过程:
image.png
2.UDP(用户数据报)
UDP是传输层提供给应用层面向无连接,不可靠的数据传输服务;
UDP特点:
1、资源消耗少,数据包头只有8BYTE字节,远比TCP协议包头要少;
2、不连接,所以也就不需要系统维护连接状态;
3、实时性是UDP的优势;
image.png
image.png

4.网络编程相关概念

1.IP:网络中主机的唯一标识,

本质:整形数
两种表示形式:1.点分十进制: 192.168.14.15
2.二进制数
IP的组成:
1.网络ID:主要是用于区分主机处于哪个网络
2.主机ID;主要是区分同一网络中不同的主机
image.png
分类:IPV4 / IPV6

2.端口:

主要是为了制定同一台主机上不同网络程序由谁来处理网络数据;
本质:16位整形数
image.png

3.网络字节序

由于网络数据是在不同的主机之间传送的,那么不同的主机字节序就可能会造成数据解析的错误,为了解决该问题,提出网络字节序这个规范标准;
,所有的主机按照大端字节序来存储数据;对于小端字节序的主机就需要转换数据后进行存储;
image.png

4.socket(套接字)

socket是网络编程的接口,简单理解就是应用层使用传输层提供服务的凭证。socket是一种特殊的I/O 接口,也是一种文件描述符;
image.png

网络编程

1.基于UDP协议的网络编程

image.png
套接字类型:
1、流式套接字 (SOCK_STREAM)
2、数据报套接字 (SOCK_DGRAM) 面向无连接
3、原始套接字 (SOCK_RAW)

服务器端:

1、(接口)创建套接字 socket
实现:int socket(int family,int type,int prot );
2、绑定自身地址信息(协议族,IP,端口)
实现:int bind(int sockfd,struct sockaddr addr,int addlen);
实际应用中 需要用 struct sockaddr_in 结构体替换;
3、从客户端接收数据
实现:int recvfrom(int sockfd,void
buf,size_t size,int flag,struct sockaddr from,int len);
4、发送信息给客户端
实现int sendto(int sockfd,const void buf,size_t len,int flag,struct sockaddr to,int len);
5、关闭套接字
实现:int close(int sockfd);

客户端:

1、创建套接字 socket
实现:int socket(int family,int type,int prot );
2、指定对方(服务器端)地址信息(协议族,IP,端口)
实现:利用 struct sockaddr_in 结构体填充;
3、发送信息给服务器端
实现int sendto(int sockfd,const void buf,size_t len,int flag,
struct sockaddr
to,int len);
4、从服务器端接收数据
实现:int recvfrom(int sockfd,void buf,size_t size,int flag,struct sockaddr from,int* len);
5、关闭套接字
实现:int close(int sockfd);
创建套接字 socket
image.png
绑定套接字 bind
image.png
image.png
转换网络字节序
image.png
h(host) to n (network)s (short)
n(network)to h (host) l (long)
image.png
接收数据的 recvfrom
image.png
发送数据的
sendto
image.png

2.基于UDP协议的网络编程2

2.1基于UDP的广播通讯

image.pngimage.png

1、广播地址:

A:直接广播地址: 主机号 二进制位全为1;
B:受限广播地址:网络号和主机号二进制位都为1;255.255.255.255 INADDR_BROADCAST;

2、发送端流程:

1、创建数据报套接字 int sockfd = (AF_INET,SOCK_DGRAM,0);
2、设置套接字广播属性 setsockopt();
image.png
例;int enable = 1 ; setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&enable,4)
3、设置广播地址和广播端口
#define PORT 8888
struct sockaddr_in broadcast = {AF_INET};
broadcast.sin_port = PORT;
broadcast.sin_addr.s_addr =htonl(INADDR_BROADCAST);
4、向设置广播地址和广播端口发送数据:
int len = sizeof(struct sockaddr);
sendto(sockfd,str,strlen(str),0,(struct sockaddr*)&(brodcast),len);

3、接收端流程:

1、创建数据报类型套接字 :int sockfd = (AF_INET,SOCK_DGRAM,0);
2、绑定 INADDR_ANY ,广播端口到套接字
#define PORT 8888
struct sockaddr_in broadcast = {AF_INET};
broadcast.sin_port = htons(PORT);
broadcast.sin_addr.s_addr =htonl(INADDR_ANY);
bind(sockfd,(struct sockaddr)&broadcast,len);
3、接收广播
recvfrom();
4、发送响应数据
sendto(sockfd,str,strlen(str),0,(struct sockaddr
)&(brodcast),len);
5、关闭套接字;

2.2基于UDP的组播通讯

现实应用:好友群image.png

1、组播地址:

永久组播地址:
临时组播地址:224.0.2.0~238.255.255.255

2、发送端流程:

1、创建数据报套接字 int sockfd = (AF_INET,SOCK_DGRAM,0);
2、设置组播地址和组播端口
#define PORT 8888
struct sockaddr_in multicast = {AF_INET};
multicast .sin_port = htons(PORT);
multicast .sin_addr.s_addr =inet_addr(“224.0.2.1”);
3、向设置组播地址和组播端口发送数据:
int len = sizeof(struct sockaddr_in);
sendto(sockfd,str,strlen(str),0,(struct sockaddr*)&(multicast),len);
4、接收数据
recvfrom()
5、关闭套接字
close(sockfd);

3、接收端流程:

1、创建数据报套接字 int sockfd = (AF_INET,SOCK_DGRAM,0);

2、绑定 INADDR_ANY ,组播端口到套接字
struct sockaddr_in multicast = {0};
multicast.sin_addr.s_addr = htonl(INADDR_ANY);
//inet_pton(AF_INET,”0.0.0.0”,multicast.sin_addr.s_addr);
multicast.sin_port =htons(MULTIPORT)
bind(sockfd,(struct sockaddr*)&multicast,len);
3、设置套接字IPPROTO_IP层的IP_ADD_MEMBERSHIP属性 setsockopt();
例子:
第一种:
struct in_addr multiaddr = {0};
multiaddr.s_addr = inet_addr(“224.0.2.1”);

struct in_addr localaddr = {0};
multiaddr.s_addr = inet_addr(“0.0.0.0”);
struct ip_mreqn mreq = {
.imr_multiaddr = multiaddr,
.imr_address = localaddr,
.imr_ifindex = if_nametoindex (“ens33”)
};
第二种:
struct ip_mreqn mreq = {.imr_ifindex = if_nametoindex(“ens33”)};
inet_pton(AF_INET, “224.0.2.1”, &mreq.imr_multiaddr);
inet_pton(AF_INET, “0.0.0.0”, &mreq.imr_address);
setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(struct ip_mreqn ));

4、接收组播信息
recvfrom();
5、发送响应数据
sendto(sockfd,str,strlen(str),0,(struct sockaddr*)&peer,len);
6、关闭套接字;
组播通讯相关结构体

  1. struct ip_mreqn {
  2. struct in_addr imr_multiaddr; /* IP multicast group
  3. address */
  4. struct in_addr imr_address; /* IP address of local
  5. interface */
  6. int imr_ifindex; /* interface index */
  7. };
  8. struct ip_mreq {
  9. struct in_addr imr_multiaddr; /* IP multicast group
  10. address */
  11. struct in_addr imr_interface; /* IP address of local
  12. interface */
  13. };

3.基于TCP协议的网络编程

image.pngimage.png
套接字类型:
1、流式套接字 (SOCK_STREAM)
2、数据报套接字 (SOCK_DGRAM) 面向无连接
3、原始套接字 (SOCK_RAW)

1、服务器端

需要两种功能的套接字:
1)主要用于监听客户端连接;
2)主要用于和客户端进行数据收发;
操作流程:
1、创建套接字(流式)int sockl = socket(AF_INET,SOCK_STREAM,0);//监听客户端连接
备注:创建出的该套接字用于监听客户端连接;
2、绑定自身地址信息
len = sizeof(struct sockaddr_in);
struct sockaddr_in server = {AF_INET};
server.sin_port = htons(PORT);
inet_pton(AF_INET,argv[1],&server.sin_addr);
bind(sockl,(struct sockaddr*)&server,len);
3、开始监听
listen(sockl,5);
image.png
4、接收客户端连接
accept();
image.png
5、发送数据给客户端
send()
image.png
6、从客户端接收数据
recv
image.png
7、关闭套接字
close(sockl);

2、客户端

1.创建套接字(流式)int sockfd = socket(AF_INET,SOCK_STREAM,0)
2.连接服务器
len = sizeof(struct sockaddr_in);
struct sockaddr_in server = {AF_INET};
server.sin_port = htons(PORT);
inet_pton(AF_INET,argv[1],&server.sin_addr);
connect(sockfd,(struct sockaddr)&server,len);
image.png
3.发送数据给服务器
send(int, const void
buff, size_t len, int flags);
4.从服务器接收数据
recv(int, const void *
buff, size_t len, int flags);
5.关闭套接字
close(sockfd);

4、基于TCP协议的并发服务器设计

1.socket + 多线程
设计思想:
1)让主线程用于接收客户端连接;
2)一旦接收某个客户端连接后,产生子线程,在子线程中和每个客户端进行数据的收发,
具体实现:
1.创建监听套接字;
2.绑定服务器端地址信息;
3.监听客户端连接;

4.实现一个while(1)循环;
5.在循环内,接收客户端连接;
6.一旦接收客户端连接,就创建一个子线程;
7.在线程函数中实现数据的收发,处理;

8.回收socket;

5、利用多路复用技术设计TCP并发服务器

image.png

image.pngimage.png

image.png
多路复用技术核心:检测文件描述符状态,告知应用程序操作是否可以以非阻塞的方式进行;
多路复用技术应用于网络编程步骤:
服务器:
1、创建套接字
2、绑定服务器端地址信息
3、监听客户端连接
4、定义描述符集合并初始化
5、接收客户端连接

6、while(1)
7、添加0,通讯套接字 到描述符集合;
8、 调用select 函数,传递相应参数;
9、用 FD_ISSET 依次确定0,通讯套接字是否可读;
10、如果可读状态发生,则进行IO操作;如果获取的数据为quit 退出循环;

11、关闭套接字