【Linux】UDP与TCP的对比并写出TCP和UDP的服务端

  • UDP  

(1.)无连接

           UDP在传输数据的时候不需要建立连接,可以直接传输。(这一点在UDP服务端程序中可以看到),因此传输速度比较快,适用于传视频,音频。

(2.)传输层协议

(3.)不可靠传输

            a:因为UDP在传输时不需要建立连接,很容易出现错误

            b:UDP只有一个socket接收缓冲区,没有socket发送缓冲区,只要是有数据发送,不管对是否可以正确的接受。而在对方的就收缓冲区满了之后,新来的数据无法进入到socket的接收缓冲区中,会造成数据丢失,UDP是没有流量控制的,因此UDP的数据是不可靠的

           c:不能对发送的数据进行排序,不能保证包序。

(4.)面向数据报 

           a:应用层给UDP多少数据,UDP都会原样发送,既不会拆分,也不会合并(这里会有应用层给的数据很大或者很小的问题)

           b:因为UDP头部(8个字节)定义了UDP数据报的长度(最大64k),所以数据是一条一条的发送和接收。因此数据不能够灵活的控制发送数量和大小,但是因为UDP数据长度固定,所以不会出现沾包问题

  • TCP

(1.)有连接

            在发送数据之前需要服务端和客户端建立连接才可以发送数据(在TCP的服务端程序中可以很好的体现)

(2.)传输层协议

(3.)可靠传输(参考TCP的三次握手,四次挥手)

          a:确认应答机制-->发送的每条数据都需要确认回复一下

          b:超时重传机制-->发送方等待一段时间后要是没有收到接收方发来的回复,就认为发送失败,那么发送方将重新发送

其中这个超时是递增的,次数有限,超过了重传次数就会断开网络,避免浪费资源

          c:序号/确认序号-->保证数据包的有序传输

(4.)面向字节流

          收发字节比较灵活,但是数据没有明显的边界,发送和接收数据的时候很容易造成沾包

  • 总结:

TCP要保证可靠传输就需要付出额外的代价,这样就会使它的传输性能大大降低。它适用于文件传输等,数据安全性较高的场景

UDP数据传输实时性高,常用于传输音乐,音频等。对数据的完整性要求不高,适用于实时性高的场景,传输速度快

  • DUP服务端编写过程

创建socket----->绑定地址----->接收数据------>发送数据----->关闭socket

1.) 创建socket

	int socket(int domain, int type, int protocol); 
	//domain(地址域) : AF_INET
	//type套接字类型 : SOCK_STREAM---->字节流(TCP)
	//	          SOCK_DGRAM---->数据报(DUP)
    //protocol协议类型: 0 默认
	//              IPPROTO_TCP 
	//	        IPPROTO_UDP

	//返回值:-1 (创建失败)

2.)绑定地址

注意:客户端程序中不推介手动绑定地址,因为绑定有可能失败,但是客户端发送数据的时候,具体哪个端口和地址都行,只要成功发送就可以。所以不需要我们手动绑定地址

	int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
	//参数列表
    //sockfd:套接字描述符
	//addr : 要绑定的地址信息
    //addrlen : 地址信息长度
	//返回值:失败返回-1

a:转换网络字节序的接口函数:

#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort); 

b:将字符串地址转化为网络地址信息的接口函数


#include <arpa/inet.h>
	in_addr_t inet_addr(const char *cp); 
	//只能转化IPV4

3.)接收数据

	ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 
    //参数列表:
    //sockfd:socket描述符
    //buf:用于存储接收的数据
    //len: 想要接受的数据长度
    //flags : 0 --->阻塞,如果缓存区没有数据就一直挂起等待
    //src_addr : 用于确定数据是哪一个客户端发送的(地址信息)
    //addrlen : 地址信息的长度
    //返回值:失败返回-1

4.)发送数据

	ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 
	//参数列表:
	//sockfd:socket描述符,发送数据就是通过这个socket所绑定的地址发送
	//buf:发送的数据
	//len:要发送的数据长度
	//flags:0-->默认阻塞式发送
	//dest_addr:数据要发送的对端地址
	//addrlen:地址信息长度

5.)关闭socket

close( )

实现代码: 

  1 #include<stdio.h>
  2 #include<sfdafsys/socket.h>
  3 #include<stdlib.h>
  4 #include<unistd.h>
  5 #include<string.h>
  6 #include<errno.h>
  7 #include<arpa/inet.h>
  8 
  9 int main()
 10 {
 11 
 12         //创建socket
 13         int sockfd =  socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
 14 
 15         if(sockfd < 0)
 16         {
 17                 printf("perror sockfd");
 18                 return -1;
 19         }
 20 
 21         //绑定地址
 22         struct sockaddr_in addr;
 23         addr.sin_family = AF_INET;
 24         addr.sin_port = htons(9000);
 25         addr.sin_addr.s_addr = inet_addr("192.168.179.128");
 26 
 27         socklen_t len = sizeof(addr);
 28 
 29         int ret = bind(sockfd,(struct sockaddr*)&addr,len);
 30 
 31         if(ret < 0)
 32         {
 33                 printf("perror bind");
 34                 return -1;
 35         }
 36         while(1)
 37         {
 38                 //接收数据
 39                 //客户端地址
 40                 struct sockaddr_in cli_addr;
 41                 char buf[1024] = {0};
 42                 len = sizeof(cli_addr);
 43                 ssize_t r_ret = recvfrom(sockfd,buf,1023,0,(struct sockaddr*)&cli_addr,&len);
 44                 if(r_ret < 0)
 45                 {
 46                         printf("perror recvfrom");
 47                         return -1;
 48                 }
 49 
 50                 printf("client[%s:%d] say=> %s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buf);
 51                 //发送数据
 52 
 53                 memset(buf,0x00,1024);
 54                 printf("请输入=> ");
 55                 scanf("%s",buf);
 56                 ssize_t rret= sendto (sockfd,buf,strlen(buf),0,(struct sockaddr*)&cli_addr,len);
 57                 if(rret < 0)
 58                 {
 59                         printf("perror sendto");
 60                         return -1;
 61                 }
 62 
 63 
 64         }
 65         //关闭socket
 66         close(sockfd);
 67         return 0;
 68 }
  • TCP的服务端代码

创建socket---->绑定地址---->监听---->建立连接---->接收数据---->发送数据

1.),2)创建socket和UDP差不多,绑定地址和UDP也差不多

3.)监听

	int listen(int sockfd, int backlog); 
	//sockfd:socket描述符
	//backlog :最大的同时并发连接数(连接成功队列中的节点数)并不是tcp的最大连接数
	//返回值:失败返回 - 1

4.)建立连接

	int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 
	//sockfd: 监听socket描述符
	//addr:新建立的客户端地址信息
	//addrlen:地址信息长度
	//返回值:
	//成功:返回非负整数,新的socket连接描述符
        //失败: - 1

accept函数是一个阻塞型函数,连接成功队列中如果没有新的socket连接就会挂起等待

5.)接收数据

	ssize_t recv(int sockfd, void *buf, size_t len, int flags); 
	//sockfd:建立连接成功的socket描述符
	//buf:用于接收数据
	//len: 用于指定接收数据长度
	//flags : 默认 0 --->阻塞式接收
	//返回值:失败返回小于0返回值
	         //等于0---->表示对端关闭连接
		    //成功:返回实际接收的长度

6.)发送数据

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//sockfd:建立连接成功的socket描述符
//buf:用于接收数据
//len:用于指定发送数据长度
//flags:默认 0

7.)关闭socket

clsoe( );

代码展示:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<errno.h>
  6 #include<netinet/in.h>
  7 #include<arpa/inet.h>
  8 #include<sys/socket.h>
  9 int main(int argc,char* argv[])
 10 {
 11 
 12         //判断输入参数是否有误
 13 
 14         if(argc != 3)
 15         {
 16                 printf("input is wrong!!!");
 17                 return -1;
 18         }
 19 
 20         //创建socket
 21         int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 22         if(sockfd < 0)
 23         {
 24                 perror("socket error");
 25                 return -1;
 26         }
 27 
 28         //绑定地址
 29         struct sockaddr_in addr;
 30         addr.sin_family = AF_INET;
 31         addr.sin_port = htons(atoi(argv[2]));
 32         addr.sin_addr.s_addr = inet_addr(argv[1]);
 33         socklen_t len =  sizeof(addr);
 34         int ret = bind(sockfd,(struct sockaddr*)&addr,len);
 35         if(ret < 0)
 36         {
 37                 perror("bind error");
 38                 return -1;
 39         }
 40 
 41         //开始监视
 42         if(listen(sockfd,5) < 0)
 43         {
 44                 perror("listen error");
 45                 return -1;
 46         }
 47 
 48 
 49         while(1)
 50         {
 51                 //开始接收新建立的socket数据(发起连接请求)
 52                 struct sockaddr_in cli_addr;
 53                 len = sizeof(cli_addr);
 54                 int new_sockfd = accept(sockfd,(struct sockaddr*)&cli_addr,&len);
 55                 if(new_sockfd < 0)
 56                 {
 57                         perror("accept error");
 58                         continue;
 59                 }
 60 
 61                 while(1)
 62                  {      //接收数据
 63                         char buf[1024] = {0};
 64                         ssize_t rret = recv(new_sockfd,buf,1023,0);
 65                         if(rret < 0)
 66                         {
 67                                 perror("recv error");
 68                                 close(new_sockfd);
 69                                 continue;
 70                         }
 71 
 72                         if(rret == 0)
 73                         {
 74                                 perror("close port ");
 75                                 close(new_sockfd);
 76                                 continue;
 77                         }
 78 
 79                         printf("client say=》[ %s ]",buf);
 80                         //发送数据
 81                         //清空buf
 82                         memset(buf,0x00,1024);
 83                         printf("请输入=》 ");
 84                         scanf("%s",buf);
 85                         send(new_sockfd,buf,strlen(buf),0);
 86 
 87                 }
 88         }
 89 
 90         //关闭
 91         close(sockfd);
 92 
 93         return 0;
 94 }
 95 

猜你喜欢

转载自blog.csdn.net/alidada_blog/article/details/83048217