一.tcp状态转移图
1.下图为TCP状态转移图,此图包含三次握手和四次挥手,其中三次握手比快。
①三次握手:由客户端主动打开,给服务端发送SYN,客户端变为SYN_SENT,服务端收到SYN为第一次握手。服务端给客户端发送SYN,ACK,客户端收到SYN,ACK为第二次握手。客户端给服务端发送确认号ACK,服务端收到ACK,为第三次握手。完成三次握手后,客户端与服务端都变成ESTABLISHED(已完成三次握手状态)
②四次挥手:
不论是客户端还是服务端,在进行close时进行挥手。当有一方先进行close操作就是主动关闭,另一方就是被动关闭。(这里假设客户端先关闭)客户端发送FIN,服务端接收FIN,为第一次挥手,此时服务端变为CLOSE_WAIT,客户端变为FIN_WAIT_1。当服务端发送ACK,客户端收到ACK为第二次挥手,此时客户端变为FIN_WAIT_2。当服务端发送FIN,客户端接收FIN为第三次挥手,此时服务端变为LAST_ACK。当客户端发送ACK,服务端接收ACK为第四次挥手,此时客户端变为TIME_WAIT,服务端消失。
三次挥手:
(这里假设客户端先关闭)客户端发送FIN,服务端接收FIN,为第一次挥手,此时服务端变为CLOSE_WAIT,客户端变为FIN_WAIT_1。当服务端发送ACK和FIN,客户端收到FIN和ACK,为第二次挥手,此时服务端变为LAST_ACK。当客户端发送ACK,服务端收到ACK,为第三次挥手,此时客户端变为TIME_WAIT,服务端消失。
客户端与服务端同时关闭:
双方同时给对方发送FIN,此时双方状态都变成FIN_WAIT_1。之后双方同时接收FIN和发送ACK,此时就是同时关闭,双方状态都变为CLOSING。之后双方同时接收ACK,状态都变成TIME_WAIT。
2.TIME_WAIT:存活时间是一个报文最大生存时间的两倍。(大概2min)
为什么要有TIME_WAIT状态:
注:①当有TIME_WAIT状态,当客户端发送ACK后,让服务端有足够的时间接收ACK。就可以顺利结束TCP连接。要是没有TIME_WAIT状态,服务端万一没有接收到ACK,根据超时重传机制,服务端重新发来的FIN,客户端早已经结束,就不认识这个FIN了,挥手就无法准确进行。
②若是没有TIME_WAIT状态,当连接关闭后,端口号被释放,此时可能还有一些数据在路上没有发送过来,当要是有其他进程连接这个端口号,可能会产生混乱。
3.怎么解决connect长时间阻塞:给connect设置一定次数循环,当循环结束还没有连接上就退出。或者设定一定的时间,若时间到了还没有连接上就退出等等。(正常TCP自己结束等待的时间较长)
二.传输层udp的编程流程(短信)
1.服务端
①socket(int domain(地址族ipv4/ipv6),int type(服务类型,tcp->流式服务,udp->数据报服务),int protocol(协议版本,目前恒为0))-->创建套接字
②bind(int sockfd(套接字描述符),const struct sockaddr *addr(使用哪个IP的哪个端口),socklen_t addrlen(指针指向空间的大小))-->指定IP与端口
③recvfrom(int sockfd(套接字描述符),void * buf(传到哪个buff中),size_t len(buff多大),int flags(标志位),struct sockaddr * src_addr(套接字的地址结构-->收到的数据存进去,记录了发送者的IP与端口),socklen_t *addrlen(套接字地址结构大小))-->接收数据
④sendto(int sockfd(监听套接字),const void * buf(待发送的数据),size_t len(发送数据的大小),int flags(标志位),const struct sockaddr * dest_addr(套接字的地址结构-->数据去往的ip与端口),socklen_t addrlen(套接字地址结构大小))-->发送数据
⑤close(int fd(套接字))-->关闭描述符
2.客户端
①socket(int domain(地址族ipv4/ipv6),int type(服务类型,tcp->流式服务,udp->数据报服务),int protocol(协议版本,目前恒为0))-->创建套接字
②sendto(int sockfd(监听套接字),const void * buf(待发送的数据),size_t len(发送数据的大小),int flags(标志位),const struct sockaddr * dest_addr(套接字的地址结构-->数据去往的ip与端口),socklen_t addrlen(套接字地址结构大小))-->发送数据
③recvfrom(int sockfd(套接字描述符),void * buf(传到哪个buff中),size_t len(buff多大),int flags(标志位),struct sockaddr * src_addr(套接字的地址结构-->收到的数据存进去,记录了发送者的IP与端口),socklen_t *addrlen(套接字地址结构大小))-->接收数据
④close(int fd(套接字))-->关闭连接
3.实现客户端与服务端的连接,客户端从键盘输入给服务端可以发送消息,服务端收到打印出来并返回ok,客户端收到后将ok也打印到屏幕上。
①服务端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()//udp循环的接收
{
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1)
{
printf("sockfd err\n");
exit(1);
}
struct sockaddr_in saddr,caddr;
menset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(6000);
saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res==-1)
{
printf("res err\n");
exit(1);
}
while(1)
{
int len=sizeof(caddr);
char buff[128]={0};
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
printf("n=&s\n",buff);
sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,len);
}
}
②客户端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
exit(1);
}
struct sockaddr_in saddr;//代表服务器的IP和端口
saddr.sin_family=AF_INET;
saddr.sin_port=htons(6000);
saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
while(1)
{
char buff[128]={0};
printf("input:\n");
fgets(buff,128,stdin);
if(strncmp(buff,"end",3) == 0)
{
break;
}
sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
menset(buff,0,sizeof(buff));
int len=sizeof(saddr);
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
printf("buff=&s\n",buff);
}
close(sockfd);
exit(0);
}
4.udp与tcp不同没有接收缓冲区,发送端发送的数据是直接交给udp数据报,再由数据报直接交给接收端,没来得及发送的数据直接就丢失了。即发送一次就接收一次,发送两次就接收两次。
如:
5.下载文件适合那种模式:TCP好,因为TCP有超时重发的机制,当文件下载到一半没成功,继续下载可以接着上次下载的地方下载,且文件下载要有可靠性。
视频聊天:udp好,因为视频一旦断开,信息的交互就直接结束,不再存储信息,下次视频就是新的开始。
6.udp占用了6000号端口,那tcp可不可以用6000号端口:可以,不同协议之间不相关。可以通过以下这个命令查看。
7.一个进程可不可以创建多个套接字:可以的(如两个套接字1,2进行listen,一个listen 1,一个listen 2)
一个进程可不可以使用两个及以上的端口:可以的
8.udp特点:无连接,不可靠的,数据报服务(客户端发几次服务端就收几次,反之也一样,不考虑丢包的问题)
三.http协议
1.http协议又叫超文本传输协议(可以传输视频,照片等等),一般用在浏览器和服务器通信的时候。
2.http协议是应用层的协议。使用的端口是80。https协议是更为安全的,使用的端口为443。它在传输层用的是tcp协议。
3.在命令提示符界面可以使用ping www.baidu.com可以拿到百度的Ip地址。
4.http回复状态码:
5.在网页中输入www.baidu.com会发生什么事:首先通过DNS解析拿到IP地址,并且端口号固定为80。有了IP与端口就可以执行connect,然后就会建立tcp连接。然后浏览器会发起请求。服务器会进行应答。(一次来回是短连接)(重复来回是长连接)
请求报文:
应答报文: