域名打不开原来的网站,wordpress防爆破插件,做微景观的网站,长安响应式网站建设Linux套接字通信 在网络通信的时候, 程序猿需要负责的应用层数据的处理(最上层)#xff0c;而底层的数据封装与解封装#xff08;如TCP/IP协议栈的功能#xff09;通常由操作系统、网络协议栈或相关网络库#xff08;如Socket库#xff09;实现。#xff08;程序员只需要…Linux套接字通信 在网络通信的时候, 程序猿需要负责的应用层数据的处理(最上层)而底层的数据封装与解封装如TCP/IP协议栈的功能通常由操作系统、网络协议栈或相关网络库如Socket库实现。程序员只需要调用对应的API接口 什么是Socket编程 Socket套接字目的是将TCP/IP协议相关软件移植到UNIX类系统中。设计者开发了一个接口以便应用程序能简单地调用该接口通信这个接口不断完善最终形成了Socket套接字。 简单来说套接字对应程序猿来说就是一套网络通信的接口使用这套接口就可以完成网络通信。 一、字节序大端与小端 小端主机字节序 低位字节存储到内存的低位地址PC机的数据存储默认小端 大端网络字节序 低位字节存储到内存的高位地址套接字通信过程中操作的数据都是大端存储的包括接收/发送的数据、IP地址、端口 主机字节序与网络字节序的转换函数 #include arpa/inet.h
// h: host, 主机字节序
// n: net, 网络字节序
// s:shortport
// l:longIP// 这套api主要用于 网络通信过程中 IP 和 端口 的 转换
// 将一个短整形从主机字节序 - 网络字节序
uint16_t htons(uint16_t hostshort);
serv_addr.sin_port htons(SERV_PORT);//举例将端口号主机字节序转成网络字节序// 将一个整形从主机字节序 - 网络字节序
uint32_t htonl(uint32_t hostlong); // 将一个短整形从网络字节序 - 主机字节序
uint16_t ntohs(uint16_t netshort)
// 将一个整形从网络字节序 - 主机字节序
uint32_t ntohl(uint32_t netlong);二、IP地址转换 IP地址本质是一个整形数但是在使用的过程中都是通过一个字符串来描述下面的函数描述了如何将一个字符串类型的IP地址进行大小端转换 //IP地址转换函数点分十进制字符串→ 网络二进制
int inet_pton(int af, const char *src, void *dst);
/*afAF_INET、AF_INET6src传入参数IP地址点分十进制dst传出参数转换后的 网络字节序的 IP地址。 返回值转换是否成功
*///网络字节序→String
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
/*afAF_INET、AF_INET6src: 传入参数网络字节序IP地址dst传出参数主机字节序string IPsize dst 的大小。返回值指向dst的指针含格式化后的字符串形式 IP 地址。
*/
//仅限处理IPv4
// 点分十进制IP - 大端整形
in_addr_t inet_addr (const char *cp);
// 大端整形 - 点分十进制IP
char* inet_ntoa(struct in_addr in);三、sockaddr 数据结构
//sockaddr的地址结构 man手册man 7 ip
struct sockaddr_in {sa_family_t sin_family; /* address family: AF_INET */in_port_t sin_port; /* port in network byte order */struct in_addr sin_addr; /* internet address */
};四、套接字相关函数
socket函数 创建套接字 #include sys/socket.hint socket(int domain, int type, int protocol);
/*
domain 使用的地址族协议AF_INET、AF_INET6
typeSOCK_STREAM流式传输协议、SOCK_DGRAM报文传输协议
protocol: 一般写0即可, 使用默认的协议
返回值新套接字所对应文件描述符
*/bind函数 给socket绑定地址结构 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
/*
sockfd: socket文件描述符socket()的返回值
addr: 传入的地址结构包括IP和端口号(struct sockaddr *)addr struct sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(8888);addr.sin_addr.s_addr htonl(INADDR_ANY);
addrlen: sizeof(addr) 地址结构的大小。
*/listen函数 设置同时与服务器建立连接的上限数 int listen(int sockfd, int backlog);
/*
sockfd: socket文件描述符socket()的返回值
backlog: 上限连接数max 128
*/★accept函数 阻塞等待客户端建立连接返回一个与客户端成功连接的socket文件描述符一个新的用于通信的文件描述符 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
sockfd: 监听的文件描述符socket()的返回值
addr传出参数, 存储了建立连接的客户端的地址信息
addrlen 传入传出参数存储addr指向的内存大小
返回值返回能与客户端进行数据通信的 socket 对应的文件描述。
*/accept函数被调用时它会阻塞程序的执行直到有客户端连接请求到达。当有客户端连接请求到达时accept函数会接受连接请求并创建一个新的套接字用于与客户端通信。这个新的套接字是一个专门用于与该客户端进行通信的套接字而原始的服务器套接字仍然保持在监听状态以接受其他连接请求。如果提供了非空的addr参数accept函数将会填充客户端的地址信息包括IP地址和端口号。accept函数返回新创建的套接字的文件描述符。通过该文件描述符服务器可以与客户端进行通信发送和接收数据。
connect函数 客户端socket与服务器建立连接。成功连接服务器之后, 客户端会自动随机绑定一个端口 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
sockfd: socket文件描述符socket()函数返回值
addr要连接的服务器的地址结构(客户端通常无法自己指定端口号由操作系统动态分配) struct sockaddr_in srv_addr; // 服务器地址结构srv_addr.sin_family AF_INET;srv_addr.sin_port 9527 跟服务器bind时设定的 port 完全一致。inet_pton(AF_INET, 服务器的IP地址srv_adrr.sin_addr.s_addr);
addrlen服务器的地址结构的大小
*/write/send函数 向已建立链接的Socket的写缓冲区中写入数据 #include unistd.h
ssize_t write(int fd, const void *buf, size_t count);
ssize_t send(int fd, const void *buf, size_t len, int flags);
/*fd 用于通信的文件描述符 accept() 函数的返回值buf 存放要写入的数据的缓冲区首地址count 想要写入的字节数len: 要发送的字符串的长度flags: 特殊的属性, 一般不使用, 指定为 0返回值实际发送的字节数
*/read/recv函数 从一个已建立连接的 Socket 读缓冲区中读取数据 #include unistd.h
ssize_t read(int fd, void *buf, size_t count);
ssize_t recv(int fd, void *buf, size_t size, int flags);
/*fd 用于通信的文件描述符accept() 函数的返回值buf 一个指向内存区域的指针用于存储读取到的数据count 指定最多读取的字节数即缓冲区大小。size: 参数buf指向的内存的容量flags: 特殊的属性, 一般不使用, 指定为 0返回值实际发送的字节数 0代表连接断开 -1 接收数据失败
*/⭐五、TCP通信流程 TCP面向连接、安全、流式传输。传输层协议 面向连接是一个双向连接通过三次握手完成断开连接需要通过四次挥手完成。安全tcp通信过程中会对发送的每一数据包都会进行校验, 如果发现数据丢失, 会自动重传流式传输发送端和接收端处理数据的速度数据的量都可以不一致 TCP通信流程图 5.1 服务端文件描述符种类 在tcp的服务器端, 有两类文件描述符客户端只有通信的文件描述符 监听的文件描述符 lfd 只需要有一个不负责和客户端通信, 负责检测客户端的连接请求, 检测到之后调用accept就可以建立新的连接 通信的文件描述符 cfd 负责和建立连接的客户端通信如果有N个客户端和服务器建立了新的连接, 通信的文件描述符就有N个每个客户端和服务器都对应一个通信的文件描述符 ⭐⭐⭐5.2 文件描述符的内存结构 强烈建议结合视频观看 文件描述符对应的内存结构 一个文件文件描述符对应两块内存, 一块内存是读缓冲区, 一块内存是写缓冲区 注意read/wirte函数并不是将数据直接发送/读取到网络流中而是通过两个缓冲区进行操作然后再由操作系统的内核来将缓冲区的数据发送到网络流中。 读数据: 通过文件描述符将内存中的数据读出, 这块内存称之为读缓冲区 写数据: 通过文件描述符将数据写入到内存中, 这块内存称之为写缓冲区 监听的文件描述符: 客户端的连接请求会发送到服务器端监听的文件描述符的读缓冲区中读缓冲区中有数据, 说明有新的客户端连接调用accept()函数, 这个函数会检测监听文件描述符的读缓冲区 检测不到数据, 该函数阻塞如果检测到数据, 解除阻塞, 新的连接建立 通信的文件描述符: 客户端和服务器端都有通信的文件描述符发送数据调用函数 write() / send()数据进入到内核中 数据并没有被发送出去, 而是将数据写入到了通信的文件描述符对应的写缓冲区中内核检测到通信的文件描述符写缓冲区中有数据, 内核会将数据发送到网络中 接收数据: 调用的函数 read() / recv(), 从内核读数据 数据如何进入到内核程序猿不需要处理, 数据进入到通信的文件描述符的读缓冲区中数据进入到内核, 必须使用通信的文件描述符, 将数据从读缓冲区中读出即可 int main(int argc, char* argv[]) {struct sockaddr_in serv_addr, clit_addr;socklen_t clit_addr_len;
//初始化服务器IP地址和端口号serv_addr.sin_family AF_INET;serv_addr.sin_port htons(SERV_PORT);serv_addr.sin_addr.s_addr htonl(INADDR_ANY);
//socketlfd socket(AF_INET, SOCK_STREAM, 0);
//bindbind(lfd, (const struct sockaddr *)serv_addr, sizeof(serv_addr));
//listenlisten(lfd, 128);clit_addr_len sizeof(clit_addr);
//accept阻塞等待连接当连接成功时客户端的地址结构会填入clit_addrcfd accept(lfd, (struct sockaddr *)clit_addr, clit_addr_len);
//TCP通信已建立处理逻辑省略close(lfd);close(cfd);
}5.3 客户端通信流程 只有一个用于通信的套接字cfd int main(int argc, char *argv[])
{int cfd socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serv_addr;serv_addr.sin_port htons(SERV_PORT);serv_addr.sin_family AF_INET;inet_pton(AF_INET, 127.0.0.1, serv_addr.sin_addr.s_addr);int ret connect(cfd, (const struct sockaddr *)serv_addr, sizeof(serv_addr));
//通信已建立自行处理数据逻辑close(cfd);return 0;
}参考文献
爱编程的大丙B站同名视频