TCP目录
-
一、Internet(冷战时期的产物)的发展历史
- 1937年,ARAP(阿帕网)问世,互联网雏形,NCP。
- 1974年,TCP协议问世,不能保证数据准确交互。
- 1983年,TCP/IP协议问世,能够实现不同类型计算机之间通信,保证数据传输准确性。
-
二、网络模型
- 协议:事先约定好的一种规则(协议对等)
-
1、OSI七层理论模型
- 物、数、网、传、会、表、应
- 协议:事先约定好的一种规则(协议对等)
-
-
-
- 应用层:用户/应用程序使用网络的接口。
- 表示层:对数据进行加密、解密。
- 会话层:建立通信端点。
- 传输层:实现点对点通信,保证数据传输的质量。进行差错检测。
- 网络层:路由寻址。
- 数据链路层:对数据进行帧格式的封装,进行纠错流控。--帧
- 物理层:指定物理设备标准(网线、光纤接口类型等),屏蔽底层硬件的差异。--比特流
-
-
2、TCP/IP协议模型(协议族)
-
-
-
- 应用层:HTTP(超文本传输协议)+‘s’(加密)/FTP/STMP
- 传输层:
- TCP:
- 面向连接的协议(打电话等),关心数据的确认、超时、重传(保证数据传输质量)。
- 提供有序的、可靠的、全双工的、基于字节流的服务。
- 应用场景:登录程序(登录账号密码),重要文件。
- UDP:
- 无连接的协议(寄信等)但是效率高
- 应用场景:视频直播(抖音、快手)等流媒体,大型游戏。
- TCP:
- 网络层:IP ICMP IGMP(组播、广播)
- 网络接口层:SLIP PPP Ethernet ARP
-
-
3、三次握手
-
第一次握手:建立连接时,客户端发送SYN包((SYN=i)到服务器,并进入SYN SEND状态,等待服务器确认;
第二次握手:服务器收到SYN包,必须确认客户的SYN (ACK=i+1 ),同时自己也发送一个SYN包((SYN j)}即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN十ACK包,向服务器发送确认包ACK(ACK=j+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手,客户端与服务器开始传送数据。
-
-
三、网络编程基础
-
1、套接字
-
- 网络中不同主机之间进行通信的两个端口的抽象,简化编程。
- Linux提供了一个socket的套接口,给用户提供了一种使用Linux内核当中集成的各种网络协议。(用户态<--->转化)
- 套接字的本质就是文件描述符。
-
2、IP地址
- IPV4———4个字节
- 1111 1111 1011 1111 1101 1111 1111 1111
- 点分十进制:255.191.223.255
- inet_addr("192.168.87.64")————将点分十进制的IP地址转换网络字节序的二进制形式。
- IPV6———16个字节
- IPV4———4个字节
-
3、端口号——Port
- unsigned short —— 0—65535
- 1-1024:系统保留端口
- 1025-5000:常用应用程序端口
- 5001-65535:用户自定义端口
- unsigned short —— 0—65535
-
4、字节序
-
小端字节序
- 当存储多字节数据时,数据的低位放到低地址空间中。——低低小
- unsigned int a = 0x11223344;
- unsigned char *p = (unsigned char *)&a;
- if(*p == 0x44)
- printf("小端\n");
- else
- printf("大端\n");
- 当存储多字节数据时,数据的低位放到低地址空间中。——低低小
-
大端字节序
-
主机字节序——host
-
网络字节序——network
-
字节序转换
- htons()将16位(unsigned short)主机字节顺序转网络字节顺序
- ntohs()将16位(unsigned short)数量从网络字节顺序转主机字节顺序
- htonl()将32位(unsigned long)整数从主机字节顺序转换为网络字节顺序
- ntohl()将32位(unsigned long)数量从网络字节顺序转换为主机字节顺序
-
-
-
-
-
四、TCP服务器
- sudo service network-manager restart----重启网络服务
- socket->bind->listen->accept->read/write->close
-
创建套接字---socket
- #include <sys/socket.h>
- #include <sys/types.h>
- int socket(int domain, int type, int protocol);
- 功能:在内核当中创建一个端点,并且返回一个文件描述符指向该端点。
- 参数:
- domain:
- AF_UNIX:本地通信
- AF_INET:网络通信IPV4
- type
- SOCK_STREAM:唯一与TCP对应————流式套接字
- SOCK_DGRAM:唯一与UDP对应————数据报套接字
- SOCK_RAM:原始套接字
- protocol:一般传0
- domain:
-
绑定--bind
- #include <sys/socket.h>
- #include <sys/types.h>
- int bind(int sockfd, const struct sockaddr*addr, socklen_t addrlen);
- 功能:为socket所指向套接字分配addr结构中所指定的IP地址
- 参数:
- sockfd:socket创建的套接字文件描述符
- addr :要绑定的地址
- addrlen:地址结构体的长度
- 结构体
struct sockaddr { sa_family_t sa_family; char sa_data[14]; } #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ struct sockaddr_in { //man 7 ip 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 */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ }; struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888);//将8888转换成网络字节序 saddr.sin_addr.s_addr = inet_addr("192.168.7.26"); bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr))
- 返回值: 成功:返回0 失败:返回-1
-
监听--listen
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- int listen(int sockfd, int backlog);
- 功能:让内核将绑定之后的套接字挂起,并且设置监听队列。
- 参数:
- sockfd:绑定之后的流式套接字(SOCK_STREAM).
- backlog:队列长度 > 0
- 返回值:
- 成功:0
- 失败:-1
-
等待连接--accept
-
- #include /* See NOTES */
- #include
- int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 功能:阻塞等待连接,如果sockfd所对应的监听队列里面有连接请求,accept处理队列队头的请求:创建一个新的套接字文件描述(连接套接字/读写套接字)符与该请求一一对应。
- 参数:
- sockfd:监听套接字
- addr:用于保存对端的IP地址 ---->NULL---->不保存对端的IP地址
- addrlen:地址结构体的长度---->长度一定要先计算出来
- 返回值:
- 成功:返回一个新的套接字文件描述符(连接/读写套接字)与新连接的客户端一一对应
- 失败:-1
- * read 去读accpet返回的连接套接字(connfd)时
- 如果connfd对应的读缓冲区没有数据 ----read 阻塞
- 如果connfd对应的读缓冲区有数据 ----read 解除阻塞 并且返回实际读到的字节数
- 如果connfd对应的客户端断开连接 ----read 解除阻塞 并且返回0
-
connect函数
- #include /* See NOTES */
- #include
- int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 功能:系统调用将文件描述符sockfd引用的套接字连接到由addr指定的地址。
- connect 不会像accept一样返还新的连接套接字
- 参数
- sockfd:标识一个套接字。
- serv_addr:套接字s想要连接的主机地址和端口号。
- addrlen:name缓冲区的长度。
五、代码实现
-
-
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <unistd.h> int main(int argc, char *argv[]) { //创建套接字 int sockfd = socket(AF_INET,SOCK_STREAM,0); if(-1 == socket) { perror("socket"); return -1; } else printf("创建成功\n"); //绑定 struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = inet_addr("192.168.5.123"); if(-1 == bind(sockfd, (struct sockaddr*)&saddr,sizeof(saddr))) { perror("bind"); return -1; } else printf("绑定成功\n"); //监听 if(-1 == listen(sockfd,8)) { perror("listen error\n"); return -1; } else printf("listen success\n"); //连接 struct sockaddr_in caddr; int addrlen = sizeof(caddr); int connfd = accept(sockfd, (struct sockaddr*)&caddr, &addrlen); if(-1 == connfd) { perror("accept error"); return -1; } else printf("connfd success!!!!!!!\n"); printf("ip:%s port:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); char buf[64] = {0}; int ret = read(connfd, buf, sizeof(buf)-1); printf("ret: %d buf: %s\n" ,ret ,buf); return 0; }
相关参考以及代码:
-
TCP服务器客户端的搭建,以及客户端连接服务器发送信息
-
UDP客户端和服务端的搭建
-
TCP客户端从服务器进行图片下载
网络编程UDP客户端、服务端的搭建-CSDN博客https://blog.csdn.net/only_write_bug/article/details/134783207?spm=1001.2014.3001.5501Linux网络编程客户端从服务端下载图片-CSDN博客
https://blog.csdn.net/only_write_bug/article/details/134736467?spm=1001.2014.3001.5501