套接字网络编程 :
IP地址:唯一标识网络上的一台主机
IPV4:uint32_t ---为了便于记忆,以点分十进制字符串形式展示
IPV6:uchar addr[16]---不向下兼容IPV4---没有发展起来
saddr(源IP地址)--> daddr(目的IP地址) 从哪个主机发送到指定主机
PORT端口:标识一台主机上的进程
uint16_t ---端口的返回:0-65535 其中0-1024不推荐使用
特性: 一个端口只能被一个进程使用 一个进程可以使用多个端口
sport(源端口)—> dport(目的端口) 从哪个进程发送到指定进程
saddr sport daddr dport proto(协议) ---五元组--标识一条通信
网络字节序:
字节序:cpu对数据在内存中存取的顺序
大端字节序:高地址存低位
小端字节序:低地址存地位
计算机的字节序取决于cpu架构:
X86_64----小端字节序
MIPS------大端字节序
字节序检测:联合体
因为在网络通信中,通信双方因为主机字节序不同,而导致数据二义性,
因此就指定标准:在网络上通信,通信双方都必须使用网络字节序--大端字节序
通信的时候总是分客户端和服务端
客户端是主动请求的一方,服务端是被动的一方
但是服务端要告诉客户端自己的IP+PORT ,客户端才能将请求发送过来
用户层协议自己定,但是传输层有两个协议,用哪一个更好?
tcp:传输控制协议:面向连接,可靠传输,面向字节流->提供字节流服务
数据安全性要求较高的程序,文件传输
udp:用户数据报协议:无连接,不可靠,面向数据报
数据安全性没那么高,传输速度快适用于实时性要求高的程序->视频,音乐传输
因为tcp为了实现安全传输,牺牲了部分性能,因此虽然安全性相较于udp较低
传输层基于udp的网络通信流程:
客户端 服务端
1.创建套接字---返回套接字句柄 socket 1.创建套接字---返回套接字句柄
2.为套接字绑定地址和端口 bind 2.为套接字绑定地址和端口
3.发送数据 sendto 3.开始接收数据
4.接收数据 recvfrom 4.恢复数据
5.关闭套接字 close 5.关闭套接字
客户端我们不建议手动绑定地址,因为如果端口冲突,客户端就会启动失败,但是
客户端对端口并没有硬规定;客户端发送数据,操作系统检测到还没绑定地址,则
会选择合适的地址进行绑定
//1.创建套接字
int socket(int domain,int type, int protocol);
//domain: 地址域
// AF_INET 表示使用IPV4协议
// type: 套接字类型
// SOCK_STREAM 流式套接字--默认协议TCP
// SOCK_DGRAM 数据报套接字--默认协议UDP
// protocol:
// 0 使用默认协议
// IPPROTO_TCP 6 TCP协议
// IPPROTO_UDP 17 UDP协议
//返回值: 套接字操作句柄-文件描述符 失败:-1
//2.为套接字绑定地址信息
//因为网络通信使用网络字节序,因此端口和ip地址信息都需要被转换
//成网络字节序的数据
//uint16_t htons(uint16_t hostshort);
//将十六位色数据从主机字节序转换为网络字节序
//uint32_t htonl(uint32_t hostshort)
//将32位的数据从主机字节序转换成网络字节序
//int_addr_t inet_addr(const char* cp)
//将字符串点分十进制IP地址转换位网络字节序的整数IP地址
//bind(int sockfd,struct sockadd* addr,sock_len addrlen);
//sockfd:创建套接字返回的套接字描述符
//addr:要绑定的地址信息
//addrlen:地址信息长度
//返回值: 0 失败:-1
bool Recv(std::string& buf,sockaddr_in* _addr=NULL)//接收数据
//ssize_t recvfrom(int sockfd,void* buf,size_t len,
//int flags,struct sockaddr* src_addr,socklen_t* addrlen);
//sockfd:套接字描述符
//bf: 用于保存接收铺的数据
//len: 要接收数据的长度
//flags: 默认0--阻塞接收--没有数据则阻塞
//src_addr: 发送端的地址信息
//addrlen: 输入输出型参数,用于获取地址信息的长度
//返回值:实际接收的数据长度 失败:-1
bool Send(std::string &buf, struct sockaddr_in &addr)//发送数据
//ssize_t sendto(int sockfd, const void *buf, size_t len,
//int flags,struct sockaddr *dest_addr, socklen_t addrlen);
// dest_addr: 目的端地址信息
// addrlen: 地址信息长度
./udp_srv 0.0.0.0 6666 //0.0.0.0 监控当前主机所有发送到6666端口的信息
netstat -anptu //查看网络状态
服务端监听了什么地址,客户端发送数据的时候目的端地址就必须是多少
(云服务器需要注意,服务端监听应该是内网地址,但是客户端要访问公网地址)
服务端监听0.0.0.0地址表示监听当前计算机上所有网卡
(需要注意客户端不能使用0.0.0.0地址)
mysql 端口:3306 8080
基于TCP协议的网络通信客户端服务端程序编写
客户端 服务端
1.创建套接字 1.创建套接字
2.不推荐绑定地址信息 2.绑定地址信息
3.向服务端发起连接请求 3.开始监听
int listen(int socket,int backlog)
告诉操作系统可以接收连接请求
建立连接的过程由操作系统完成
backlog:同一时间的客户端最大并发连接数
客户端一旦建立成功,服务端会单独创建一个socket
专门与指定客户进行通信
4.阻塞接收已完成连接客户端信息
newfd=accept(sockfd,srd_addr,len)
从已完成连接队列中取出一个socket
4.收发数据 5.使用这个针对客户端与新建的socket与客户端进行通信
Recv(newfd,)
Send()
5.关闭套接字 6.关闭套接字
tcp在操作系统会进行保活检测,如果连续好几次保活检测失败,则认为连接断开
sysctl -a|grep keep tcp具有自己的保活探测机制
在代码中,若断开连接/或对端关闭连接-->recv返回0;
send 会触发异常--SIGPIPE-导致进程退出
基本的tcp服务端程序只能同时只能与一个客户端进行通信:因为服务端不知道
客户端数据什么时候到来,因此服务端的程序流程只能写死,而写死可能导致
阻塞(accept/recv) accept阻塞:新的客户端一直没来,就会阻塞
recv阻塞:客户端没有发数据就会一直阻塞
多进程tcp服务程序:一个子进程处理一个客户端,实现多个客户端同时处理
父进程必须关闭客户端socket
多线程tcp服务程序:一个子线程处理一个客户端,实现多个客户端同时处理
主线程不能关闭socket;同时一个进程中的线程共享文件描述符表;