一、udp通信
1. 基本流程
udp发送端 | udp接收端 |
---|---|
socket() | socket() |
bind(); | bind(); |
sendto/recvfrom | sendto/recvfrom |
close() | close() |
注意: udp通信双方,谁先发送的,谁就是发送端
2.相关接口函数
(一)发送信息
#include <socket.h>
ssize_t sendto(int socket, void *message, size_t length, int flags, struct sockaddr *dest_addr,socklen_t dest_len);
返回值:length写成多少,返回值就是多少
参数:前面三个参数跟write一样的
flags --- 设置为0
dest_addr --- 存放对方的ip和端口号
dest_len --- 地址结构体大小
(二)接收信息
#include <socket.h>
ssize_t recvfrom(int socket, void *buffer, size_t length,int flags, struct sockaddr *address,socklen_t *address_len);
返回值:只要sendto发送的字节数不超过length,sendto发送多少,接收多少
如果sendto发送的字节数超过length,就按照length的大小接收
参数:前面三个参数跟read一样的
flags --- 设置为0
address --- 存放对方的ip和端口号
address_len --- 结构体大小,要求是指针
3. 总结ip和端口号转换的常用函数
(一)小端序(主机字节序) -> 大端序(网络字节序)
(1)第一个
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
返回值:成功 --- 返回转换得到大端序二进制ip
失败 --- -1
参数:cp --- 你需要转换的点分十进制小端序ip
(2)第二个
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
参数:af --- 地址协议类型 AF_INET
src --- 字符串ip
dst --- 保存转换得到的大端序ip
(3)第三个
#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 --- 保存转换得到的大端序ip
(4)第四个
#include <arpa/inet.h>
uint32_t htonl(INADDR_ANY);//改宏定义见TCP协议绑定
(二)大端序(网络字节序) -> 小端序(主机字节序)
(1)第一个
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
返回值:转换得到的字符串ip(小端序)
失败 --- -1
参数:in --- 大端序ip
(2)第二个
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
返回值:转换得到的小端序字符串ip
参数:af --- 地址协议类型 AF_INET
src --- 你要转换的大端序ip
dst --- 存放转换得到的小端序字符串ip
size --- ip地址的大小
4. udp广播和udp的组播
广播: 把信息发送给该网段中所有的主机
ip地址分类:A B C D 四类ip地址
流程:
发送端 | 接收端 |
---|---|
socket | socket |
bind 注意必须使用INADDR_ANY | bind 注意必须使用INADDR_ANY |
设置套接字的属性为可以广播 | |
收发信息 | 收发信息 |
close | close |
(一)设置udp套接字可以广播
int on=1;
setsockopt(udp套接字,SOL_SOCKET, SO_BROADCAST ,&on,sizeof(on));
(二) udp组播
组播是广播一种特殊形式,广播是发送信息给所有主机,组播仅仅只是发送给一小部分主机,单播是一对一的
流程:
发送端 | 接收端 |
---|---|
socket | socket |
bind 注意必须使用INADDR_ANY | bind 注意必须使用INADDR_ANY |
设置套接字的属性为可以广播 | 加入到组播组里面 |
收发信息 | 收发信息 |
close | close |
重点代码:
//加入组播组
struct ip_mreq mreq; //存放组播地址的结构体变量
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr("224.168.11.10"); //D类地址,必须跟sendto一致
mreq.imr_interface.s_addr = htonl(INADDR_ANY); // 必须用这个宏定义
setsockopt(udpsock, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof mreq); //设置加入到组播组里面
5. tcp和udp的区别(理论知识)
TCP是面向连接的可靠的通信方式
面向连接: TCP客户端和服务器在通信之前是必须经历三次握手TCP协议底层帮我们去完成,程序员是感受不到的,理论分析)
三次握手: 发生在TCP建立连接的时候
四次握手: 发生在TCP断开连接的时候
可靠: TCP协议支持错误校验和错误重传,不容易丢失数据包,用于通信要求严格的场合(发送账号,密码,占用的网络带宽资源比较大)
UDP是无连接的不可靠的通信方式
不可靠:UDP协议没有错误重传,网络拥堵情况下容易丢失数据包(视频点播,占用的网络带宽资源要小)
二、多路复用
1. 引用及概念
多路复用: 帮助我们去监控网络中IO通道中数据的流入和流出的
每个IO通道又对应一个文件描述符,所有最终可以认为帮助我们去监控网络中文件描述符是否有数据可读(I),是否有数据可写(O)
linux中定义了一个变量类型,名字fd_set,专门帮助我们存放所有需要监测的文件描述符
fd_set类型: 专业术语称作文件描述符集合
第一步: 定义fd_set类型变量存放需要监测的文件描述符
fd_set myset;
第二步:往集合变量中存放你想监测的文件描述符
void FD_CLR(int fd, fd_set *set); //将fd从集合中删除
int FD_ISSET(int fd, fd_set *set); //判断fd在不在set集合中 在 --- 返回1 不在 --- 返回0
void FD_SET(int fd, fd_set *set); //将fd添加到set集合中
void FD_ZERO(fd_set *set); //清空set集合
FD_ZERO(&myset);
FD_SET(fd1, &myset);
FD_SET(fd2, &myset);
FD_SET(fd3, &myset);
第三步:调用select去监控刚才的三个文件描述符
select(最大+1,&myset,NULL,NULL,NULL); //监测读就绪
select(最大+1,NULL,&myset,NULL,NULL); //监测写就绪
第四步:判断究竟是哪个文件描述符发生了状态改变(读就绪,写就绪,异常就绪)
select的特点:
①如果监测了ABC三个文件描述符,A发生了状态改变,select就会自动把没有发生状态改变的BC从集合中删除
换句话来说:select只保留状态发生改变的文件描述符
②select会阻塞,直到监测到某个文件描述符发生了状态改变才不会阻塞
2. 相关的接口函数
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *restrict writefds, fd_set *restrict errorfds,struct timeval *restrict timeout);
返回值: 成功 >0
失败 -1
超时 0
参数:nfds(重点) --- 你要监测的所有IO通道(文件描述符)中最大的文件描述符+1
readfds --- 监测集合中文件描述符是否有数据可读(读就绪)
writefds --- 监测集合中文件描述符是否有数据可写(写就绪)
errorfds --- 监测集合中文件描述符是否发生了异常(异常就绪)
timeout --- 超时,一般设置为NULL表示永远等待
struct timeval
{
tv_sec; //秒
tv_usec; //微秒
}