1. 程式人生 > >【Linux】UDP與TCP的對比並寫出TCP和UDP的服務端

【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