一、UDP
1.UDP
User Datagram Protocol 用户数据报协议,是OSI(Open System Interconnection开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。
我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。
这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效果高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。
2. UDP特点:
无连接:知道对方IP和端口号就可以直接连接进行传输,无需connect函数进行连接
不可靠:没有确认机制,没有重传机制;消息丢失对方也不会知道。
面向数据报:不能够灵活的控制读写数据的次数和数量。对于上层交付下的数据既不切分,也不合并,一次发送一个完整的报文。
3. 报文内容
4. UDP的缓冲区:
没有发送缓冲区,上层交付数据会调用sendto函数直接交给内核,由内核将数据进行网络协议传输。
有接收缓冲区,但不能保证报文的顺序性和一致性。如果缓冲区满了,再到达的数据就会被丢弃。
5. 简单的UDP程序(基于Linux环境)
说明:使用套接字基于UDP协议,服务端先bind一个ip和port,然后进行收消息并把消息返还给客户端。而客户端无需bind连接服务器,只需知道它的ip和port就可以给它发消息。
使用:main函数使用可变参数列表,接收程序名,IP和端口号。
执行步骤:
1. 服务器,执行服务端程序,IP地址(自己拥有的IP,ifconfig查看),端口号。
2. 客户端,执行客户端程序,IP地址,端口号(和服务端的IP和端口号一致)
3. 客户端发送消息,服务端接收显示并返回给客户端,客户端显示自己发送的内容。
client.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(int argc,char *argv[])
{
if(argc!=3)
{
perror("argc\n");
return 1;
}
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
perror("socket\n");
return 2;
}
char buf[128];
struct sockaddr_in server;
server.sin_family=AF_INET;
server.sin_port=htons(atoi(argv[2]));
server.sin_addr.s_addr=inet_addr(argv[1]);
while(1)
{
ssize_t r=read(0,buf,sizeof(buf));
if(r<0)
{
printf("read error\n");
continue;
}
buf[r-1]='\0';
sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&server,sizeof(server));
ssize_t recv=recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
buf[recv]=0;
printf("server > %s\n",buf);
}
close(sockfd);//use signal to delet
return 0;
}
server.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
if(argc!=3)
{
perror("argc\n");
return 1;
}
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
perror("socket\n");
return 2;
}
char buf[128];
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(atoi(argv[2]));
local.sin_addr.s_addr=inet_addr(argv[1]);
if(bind(sockfd,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
return 2;
}
struct sockaddr_in client;
while(1)
{
socklen_t len=sizeof(client);
size_t recv=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&len);
if(recv<0)
{
printf("recv error\n");
return 4;
}
buf[recv]='\0';
printf("[%s,%d] > %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client,sizeof(client));
}
close(sockfd);//use signal to delet
return 0;
}
6. 演示效果如图(第一张服务端,第二张客户端):
二、TCP
1. TCP
Transmission Control Protocol 传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来。
我们来看看这三次对话的简单过程:主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。
2. TCP特点
面向连接:双方必须先建立连接才能进行数据的读写,双方都必须为该链接分配必要的内核资源,以管理连接的状态和连接上的传输。
可靠传输:采用多种应答机制保证数据的可靠性
基于字节流:发送端执行多次写操作时,TCP模块必须先把这些数据放入TCP发送缓冲区中,当TCP模块真正发送数据时,才把TCP发送缓冲区等待发送的数据封装成一个或多个TCP报文段发出
3. 报文格式
4. 图中说明:
1.源端口号和目的端口号就不解释了。
2.序号和确认序号:因为TCP将每个字节的数据都进行了编号。发送方把数据编号发送后,接收方确认都收到后会发送一个确认序号给发送方,保证了数据的可靠性。
3.首部长度:就是指图中前20字节的长度。
4.保留:现在还没确定把这6位做什么用。
5.6位标志位:
URG:紧急指针是否有效
ACK:接收号是否有效
PSH:提示接收端立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接;携带RST标识称为复位报文段
SYN:请求建立连接;携带SYN标识称为同步报文段
FIN:通知对方,本端要关闭了;携带FIN标识的为结束报文段
6.16为窗口大小:填充自己接受缓冲区剩余空间
7.16位检验和:检验传输过程中可能产生的错误,接收端用同样算法检查,正确则接受;错误则丢弃
8.16为紧急指针:标识那部分是紧急数据
9.选项:包含了选项表结束、空操作、最大报文段长度、窗口扩大因子、选择确认、SACK实际工作、时间戳选项。
5. TCP为了确保可靠性做了哪些工作?
- 校验和:是指传输位数的累加,当传输结束时,接收者可以根据这个数值判断是否接到了所有的数据。如果数值匹配,那么说明传送已经完成。
- 序列号:发送方将数据进行排序然后发送
- 确认应答:响应方完整接收到消息后返回给对方一个确认号(下一个数据的编号)
- 连接管理:三次握手和四次挥手
- 超时重传:当数据丢包,发送方在一定时间内没有收到确认号就将该内容重新发送
- 流量控制:根据接收方接受能力的大小决定发送多少数据。
- 拥塞控制:使用拥塞窗口,表示发送数据的大小。
拥塞窗口初始为1,每收到一个ACK,拥塞窗口加1。
这样,第一次发一个,收到一个确认后拥塞窗口变为2。
然后拥塞窗口依次变为8,16……呈指数增长。
当拥塞窗口大小超过阈值(阈值为窗口的最大值)时采用线性增长。且一旦产生超时重传,就将阈值减半。
6. TCP为了保障性能做了哪些工作?
- 滑动窗口:接收方还有多大的缓冲区可以用于接收数据
- 快速重传:当数据丢包,发送方收到接收方三次相同确认序号,就将该内容重新发送
- 延迟应答:接收到N个包或一定时间才给对方确认
- 捎带应答:将确认报文粘在下一个要发送的报文上
7. 简单的TCP程序(Linux环境下,多线程)
说明:基于套接字的TCP协议协议编程。不同于UDP的是,客户端需和服务端建立连接,才能相互进行收发消息。使用线程处理多个用户的请求,使其收发数据。
client.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(int argc,char *argv[])
{
if(argc!=3)
{
perror("argc\n");
return 1;
}
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
perror("socket\n");
return 2;
}
char buf[128];
struct sockaddr_in server;
server.sin_family=AF_INET;
server.sin_port=htons(atoi(argv[2]));
server.sin_addr.s_addr=inet_addr(argv[1]);
if(connect(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
{
perror("connect\n");
return 3;
}
printf("connetc success\n");
while(1)
{
memset(buf,0,sizeof(buf));
ssize_t r=read(0,buf,sizeof(buf));
if(r<0)
{
printf("read error\n");
continue;
}
buf[r-1]=0;
if(send(sockfd,buf,strlen(buf),0)==0)
continue;
memset(buf,0,sizeof(buf));
ssize_t rec=recv(sockfd,buf,sizeof(buf),0);
printf("server > %s\n",buf);
}
close(sockfd);//use signal to delet
return 0;
}
server.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
if(argc!=3)
{
perror("argc\n");
return 1;
}
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
perror("socket\n");
return 2;
}
char buf[128];
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(atoi(argv[2]));
local.sin_addr.s_addr=inet_addr(argv[1]);
if(bind(sockfd,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
return 2;
}
if(listen(sockfd,5)<0)
{
perror("listen\n");
return 5;
}
while(1)
{
int new_sockfd=accept(sockfd,NULL,NULL);
if(new_sockfd<0)
{
perror("accept\n");
return 6;
}
pid_t pid=fork();
if(pid==0)
{
if(fork()==0)
{
struct sockaddr_in client;
while(1)
{
socklen_t len=sizeof(client);
size_t rec=recv(new_sockfd,buf,sizeof(buf),0);
if(rec<0)
{
printf("recv error\n");
return 4;
}
if(rec==0)
{
break;;
}
buf[rec]='\0';
printf("[%s,%d] > %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
send(new_sockfd,buf,strlen(buf),0);
}
close(new_sockfd);//use signal to delet
}
else
{
exit(1);
}
}
else
{
waitpid(pid);
}
}
close(sockfd);
return 0;
}
8. 演示效果:
三、最后,TCP和UDP的区别:
TCP提供的是面向连接的、可靠的数据流传输;
UDP提供的是非面向连接的、不可靠的数据流传输。TCP提供可靠的服务,通过TCP连接传送的数据,无差错、不丢失,不重复,按序到达;
UDP尽最大努力交付,即不保证可靠交付。TCP面向字节流;
UDP面向报文。TCP连接只能是点到点的;
UDP支持一对一、一对多、多对一和多对多的交互通信。TCP首部开销20字节;
UDP的首部开销小,只有8个字节。TCP的逻辑通信信道是可靠信道;
UDP的逻辑通信信道是不可靠信道。
欢迎各位提建议或指错给我,谢谢~