参考文献:35 张图解被问千百遍的 TCP 三次握手和四次挥手面试题
一、网络通信的基本概念
1、TCP和UDP的区别TCP&#xff08;Transmission Control Protocol&#xff09;&#xff1a;传输控制协议&#xff0c;面向连接的服务&#xff08;类似打电话&#xff09;&#xff0c;安全、可靠&#xff08;三次握手、响应&#43;重传、四次挥手&#xff09;&#xff0c;速度相对较慢&#xff0c;一般应用在对安全性、完整性有严格要求的场景&#xff1a;文件传输&#xff08;ftp&#xff09;、SMTP、HTTP三次握手&#xff1a;A要知道&#xff0c;A能到B,B能到AB要也要知道&#xff0c;A能到B&#xff0c;B能到AA 你听得到吗 -> B(此时B知道了A能到B)&#xff08;A能到B且B能到A&#xff09;A <- 我能听到&#xff0c;你叫 BA 我也能听到-> B&#xff08;此时B知道了B也能到A&#xff09;四次挥手&#xff1a;目的是保证关闭前发送完所有未发送的数据包&#xff08;应用层已经交给底层了&#xff0c;但底层还没有完全发送出去&#xff09;。A 发送关闭请求 -> BA <- 发送请求相应 BB检查 是否有未发送完成的数据<- 可以关闭 BA 发送关闭消息-> BUDP&#xff08;User Datagram Protocol&#xff09;&#xff1a;用户数据报文协议&#xff0c;面向无连接的服务&#xff08;发短信&#xff09;0&#xff0c;不保证安全、可靠&#xff0c;但大多数情况下是可靠的&#xff0c;相对较快&#xff0c;流媒体&#xff08;在线视频、音频&#xff09;。
2、消息流应用层->表示层->会话层->传输层->网络层->数据链路层->物理层->数据链路层->网络层->传输层->会话层->表示层->应用层
3、消息包
当socket收到一个要发送的数据时&#xff0c;会先把数据进行拆分成bit流&#xff0c;然后再组成&#xff08;防丢失&#xff09;数据包&#xff08;可能会丢包&#xff09;。
二、套接字
socket是一种接口机制&#xff0c;可以让程序无论使用什么端口、协议、都可以从socket进出数据&#xff0c;它负责了进程与协
议之间的连接。
1、编程模型点对点&#xff08;p2p&#xff09;&#xff1a;一对一通信客户机/服务器&#xff08;C/S&#xff09;&#xff1a;一对多通信
2、函数int socket(int domain, int type, int protocol);功能&#xff1a;创建socket描述符&#xff0c;可以把socket当作文件来看待&#xff0c;发送数据就是写文件&#xff0c;接收数据就是读文件。domain:地址类型AF_UNIX/AF_LOCAL/AF_FILE 本地通信&#xff08;进程间通信&#xff09;AF_INET 基本32IP地址通信&#xff0c;IPv4 Internet protocolsAF_INET6 基本128IP地址通信&#xff0c;IPv6&#xff0c;IPv6 Internet protocolstype:通信协议SOCK_STREAM 数据流协议&#xff0c;TCPSOCK_DGRAM 数据报协议&#xff0c;UDPprotocol&#xff1a;特别通信协议&#xff0c;给0即可。返回值&#xff1a;socket描述符&#xff0c;类似文件描述符
3、通信地址注意&#xff1a;函数接口定义的是sockaddr&#xff0c;而实际提供的是sockaddr_un或sockaddr_instruct socketaddr{sa_family_t sa_family;char sa_data[14];}struct sockaddr_un {__SOCKADDR_COMMON(sun_); /* AF_UNIX */地址类型 参看domain参数 char sun_path[108]; /* pathname */socket文件的路径};struct sockaddr_in{__SOCKADDR_COMMON(sin_);in_port_t sin_port; // 端口号 大端字节序 参看联合struct in_addr sin_addr // ip地址 大端4字节整数}struct in_addr{in_addr_t s_addr; // }
4、绑定socket描述符与物理通信载体&#xff08;网卡或socket文件&#xff09;绑定在一起。int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);sockfd&#xff1a;socket描述符&#xff1a;socket函数的返回值addr&#xff1a;通信地址结构体&#xff0c;实际给的是sockaddr_un或sockaddr_in&#xff0c;需要强制类型转换。addrlen&#xff1a;通信地址结构体类型的字节数&#xff0c;使用sizeof计算。
5、连接int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);sockfd&#xff1a;socket描述符addr&#xff1a;通信目标地址addrlen&#xff1a;通信地址结构体类型的字节数&#xff0c;使用sizeof计算。返回值&#xff1a;在不同的编程模型下返回值意义不同&#xff0c;在本地通信返回加0&#xff0c;失败返回-1。
6、数据接收与发送&#xff1a;read/writessize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t send(int sockfd, const void *buf, size_t len, int flags);recv&#xff0f;send与read/write功能一样&#xff0c;flags多了是否阻塞的功能&#xff08;0阻塞&#xff0c;1不阻塞&#xff09;。
7、关闭套接字&#xff1a;close如果是网络通信&#xff0c;端口号并不会立即回收&#xff0c;大概会占用3分钟左右。
8、字节序转换
#include uint32_t htonl(uint32_t hostlong);功能&#xff1a;把32位本机字节序转换成32位的网络字节序uint16_t htons(uint16_t hostshort);功能&#xff1a;把16位本机字节序转换成16位的网络字节序uint32_t ntohl(uint32_t netlong);功能&#xff1a;把32位网络字节序转换成32位的本机字节序uint16_t ntohs(uint16_t netshort);功能&#xff1a;把16位网络字节序转换成16位的本机字节序
9、ip地址转换
#include
#include
#include int inet_aton(const char *cp, struct in_addr *inp);功能&#xff1a;把点分十进制的ip地址&#xff08;字符串&#xff09;转换成32位无符号整数&#xff0c;使用指针获取。in_addr_t inet_addr(const char *cp);功能&#xff1a;把点分十进制的ip地址&#xff08;字符串&#xff09;转换成32位无符号整数&#xff0c;使用返回值直接返回。char *inet_ntoa(struct in_addr in);功能&#xff1a;32位无符号整数表示的ip地址&#xff0c;转换成点分十进制的ip地址&#xff08;字符串&#xff09;。
10、本地通信编程模型进程A 进程B创建套接字&#xff08;AF_LOCAL&#xff09; 创建套接字&#xff08;AF_LOCAL&#xff09; 准备地址&#xff08;sockaddr_un&#xff09; 准备地址&#xff08;sockaddr_un&#xff09;绑定&#xff08;自己的socket/地址&#xff09; 连接&#xff08;connect&#xff0c;连接进程A的地址&#xff09;接收数据 发送数据关闭套接字
三、基于TCP协议的C/S模型
int listen(int sockfd, int backlog);功能&#xff1a;设置等待连接的最大数量sockfd:被监听的socket描述符backlog:等待连接的最大数量&#xff08;排队的数量&#xff09;int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);功能&#xff1a;等待连接sockfd连接addr&#xff1a;获取连接的地址addrlen&#xff1a;设置连接地址结构体的长度返回值&#xff1a;专门用于通信的描述符编程模型&#xff1a;Server Client创建socket套接字 创建socket套接字准备地址&#xff08;sockaddr_in,本机地址&#xff09; 准备地址&#xff08;服务器地址&#xff09;绑定&#xff08;bind&#xff09; 。。。监听&#xff08;listen&#xff09; 。。。等待连接&#xff08;accept、fork&#xff09; 连接&#xff08;connect&#xff09;接收请求(read/recv) 发送请求&#xff08;write/send&#xff09;响应请求(write/send) 接收响应&#xff08;read/recv) 关闭(close) 关闭&#xff08;close&#xff09;
四、基于UDP协议的C/S模型
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
功能&#xff1a;UDP协议专用的数据发送函数sockfd&#xff1a;套接字描述符buf&#xff1a;待发送的缓冲区首地址len&#xff1a;待发送的数据字节数flags&#xff1a;0阻塞&#xff0c;1不阻塞dest_addr&#xff1a;目标计算机地址&#xff08;发送&#xff09;addrlen&#xff1a;地址结构体的字节数返回值&#xff1a;成功发送的字节数ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
功能&#xff1a;UDP协议专用的数据接收函数sockfd&#xff1a;套接字描述符buf&#xff1a;数据存储位置 len&#xff1a;最大接收字节数flags&#xff1a;0阻塞&#xff0c;1不阻塞src_addr&#xff1a;获取发送者的地址addrlen&#xff1a;设置地址结构体的字节数返回值&#xff1a;成功接收的字节数。编程模型&#xff1a;
Server Client
创建套接字&#xff08;socket&#xff09; 创建套接字&#xff08;socket&#xff09;
准备地址&#xff08;本机地址sockaddr_in&#xff09; 准备地址&#xff08;目标机地址sockaddr_in&#xff09;
绑定&#xff08;bind&#xff08;sockfd&#43;addr&#xff09;&#xff09; 。。。
接收请求&#xff08;recvfrom&#xff09; 发送请求&#xff08;sendto&#xff09;
响应请求&#xff08;sendto&#xff09; 接收响应&#xff08;recvfrom&#xff09;
关闭套接字&#xff08;close&#xff09; 关闭套接字&#xff08;close&#xff09;
注意&#xff1a;从服务器到客户端返回的路线是UDP协议自己设计的。
实例
C语言实现socket通信TCP 程序参考