之前我们介绍过一次TCP和UDP的区别以及各自的优缺点,但是总感觉写得不太细致,仍有许多有待补充。因此,今天就来再归档一次。
UDP
UDP也叫做用户数据报,是不需要建立连接的协议,它的作用就是将需要进行网络传输的数据压成二进制的格式,再进行传输。 暴力是它典型的特点。 因为它减少了一些保证数据可靠的机制,比如数据包分组、排序,直接将每份消息段放在队列中,应用程序就会每次从这个队列读取。因此,减少了这些繁琐的步骤,它的速度也就哗啦啦上来了。在这里,它的传输速度只受传输的带宽、计算机的能力、数据生成的速度所影响。
也因为缺少了类似TCP这样繁琐的机制,UDP可以被攻击的漏洞也就少了一些。但这也是要付出代价的,就是失去了数据传输的可靠性,也就是我们无法得知传输的数据是否完整地、安全地到达对方。
优缺点:速度快并较为安全但是可靠性差。由于UDP的控制选项较少、在数据传输的过程的延迟小,就适合对可靠性要求不高的应用程序,比如视频、音频的传输。
建立流程以及代码实现:
UDP编程的服务器实现步骤:
1.使用socket()创建一个socket(SOCK_DGRAM);
2.设置socket的属性;
3.使用bind()绑定IP地址和端口地址到socket上;
4.使用recvform()循环接收数据;
5.关闭连接
服务端代码:
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
int n;
char recvline[1024] = {0};
int sockfd;
struct sockaddr_in servaddr;
/* 创建一个UDP连接的socket */
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
/* 变量servaddr清零 */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(50001);
/* 绑定servaddr到创建的socket上 */
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
/* 接收客户端发送的数据 */
recvfrom(sockfd, recvline, 1024, 0, NULL, NULL);
printf("%s\n", recvline);
/* 关闭socket连接 */
close(sockfd);
}
UDP编程的客户端实现步骤:
1.使用socket()创建一个socket;
2.设置socket的属性;
3.使用bind()设置对方的IP地址和端口地址到socket上;
4.使用sendto()循环发送数据;
5.关闭连接
客户端代码:
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2)
{
printf("usgae: ./client [ip]\n");
return -1;
}
/* 创建一个UDP的socket连接 */
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
/* 变量servaddr清零 */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(50001);
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
char sendline[100];
sprintf(sendline, "Hello, world!");
/* 发送数据 */
sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
/* 关闭socket连接 */
close(sockfd);
return 1;
}
TCP
TCP也叫做传输控制协议。在进行TCP传输交换前,必须在客户端和服务端都建立起一个连接。它是将数据流分割成若干长度的报文段之后进行传输的连接。
如何保证传输过程中的可靠性?
1.当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。当TCP收到发自TCP连接另一端的数据,它将发送一个确认。TCP有延迟确认的功能,在此功能没有打开,则是立即确认。功能打开,则由定时器触发确认时间点。
2.TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)
3.既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
4.TCP的接收端必须丢弃重复的数据
5.缓冲区控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。
优缺点:因为TCP在传输前必须建立连接,因此这不仅消耗时间也消耗资源,并且在数据传输时的机制三次握手、重传、拥塞控制、四次挥手这些都会消耗大量时间。和UDP一样,保证安全也是需要付出代价的,那就是因可攻击的点太多了,所以TCP更加容易受到攻击。当我们需要保证数据稳定地传输到对方那时,就得使用TCP,比如我们日常用的HTTP、SMTP、FTP都是使用TCP。
建立流程以及代码实现:
TCP编程的服务器端实现步骤:
1、使用socket()函数创建一个socket(SOCK_STREAM);
2、使用函数setsockopt()设置socket属性;
3、使用bind函数()绑定IP地址、端口等信息到socket上;
4、使用用函数listen()开启监听;
5、接收来自客户端上来的连接,使用用函数accept();
6、用函数send()和recv(),或者read()和write()进行收发数据;
7、关闭网络连接;
8、关闭监听;
服务端代码:
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main()
{
int sfp, nfp, num = 0;
struct sockaddr_in s_add,c_add;
int sin_size;
unsigned short portnum=0x8888;
char buffer[100] = {0};
printf("Hello,welcome to my server !\r\n");
sfp = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sfp)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=htonl(INADDR_ANY);
s_add.sin_port=htons(portnum);
if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("bind fail !\r\n");
return -1;
}
printf("bind ok !\r\n");
if(-1 == listen(sfp,5))
{
printf("listen fail !\r\n");
return -1;
}
printf("listen ok\r\n");
sin_size = sizeof(struct sockaddr_in);
nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
if(-1 == nfp)
{
printf("accept fail !\r\n");
return -1;
}
printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",
ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
while(1)
{
memset(buffer, 0, 100);
sprintf(buffer, "hello,welcome to my server(%d) \r\n", num++);
send(nfp, buffer, strlen(buffer), 0);
usleep(500000);
}
close(nfp);
close(sfp);
return 0;
}
TCP编程的客户端实现步骤:
1、使用socket()函数创建一个socket(SOCK_STREAM);
2、使用函数setsockopt()设置socket属性;
3、使用bind函数()绑定IP地址、端口等信息到socket上;
4、连接对方的IP地址以及端口号;
5、使用connect()连接服务器;
6、用函数send()和recv(),或者read()和write()进行收发数据;
7、关闭网络连接;
客户端代码:
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char **argv)
{
int cfd;
int recbyte;
int sin_size;
char buffer[1024] = {0};
struct sockaddr_in s_add, c_add;
unsigned short portnum = 0x8888;
printf("Hello,welcome to client!\r\n");
if(argc != 2)
{
printf("usage: echo ip\n");
return -1;
}
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cfd)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr= inet_addr(argv[1]);
s_add.sin_port=htons(portnum);
printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("connect fail !\r\n");
return -1;
}
printf("connect ok !\r\n");
while(1)
{
if(-1 == (recbyte = read(cfd, buffer, 1024)))
{
printf("read data fail !\r\n");
return -1;
}
printf("read ok\r\nREC:\r\n");
buffer[recbyte]='\0';
printf("%s\r\n",buffer);
}
close(cfd);
return 0;
}
小结如下
- TCP是面向连接、可靠、有序的连接,而UDP则是无连接、不可靠、无序的连接。 UDP速度较快而TCP速度较慢。
- 因TCP的保证可靠、有序的机制存在,它开销较大,在TCP头部需要20字节而UDP头部仅需要8个字节。
- TCP是通过字节流传输的,因此TCP无界,而UDP因为每一个包都是单独的,所以UDP有界。
- TCP有拥塞控制,主要靠三次握手、慢开始、快重传、快恢复、拥塞避免实现,而UDP没有
- 基于TCP的协议有:HTTP/HTTPS、Telnet、FTP、SMTP。
- 基于UDP的协议有:DHCP、DNS、SNMP、TFTP、BOOTP。