1. 程式人生 > >Linux中用socket實現UDP網路程式

Linux中用socket實現UDP網路程式

這篇部落格的目的是想實現一個簡單的UDP伺服器程式,完成客戶端與伺服器端的通訊。 因為涉及的小知識點比較多,所以本篇部落格的篇幅較長,但是會講的很詳細。 在下一篇部落格裡,我會總結Linux中用socket實現TCP網路程式 在這裡插入圖片描述 1.程式的第一步是建立套接字(socket)

#include<sys/socket.h>	//標頭檔案
//建立套接字函式,socket
int socket(int domain, int type, int protocol);
//domain:地址域,常用的有以下兩種
//	AF_INET             IPv4 Internet protocols          ip(7)   
// 	AF_INET6            IPv6 Internet protocols          ipv6(7)
//type:套接字型別,常用的有以下兩種
//	 SOCK_STREAM    流式套接字   
//	 SOCK_DGRAM    資料報套接字
//protocol:協議型別
//	預設為0,流式套接字預設TCP協議,資料包套接字預設UDP協議
//	在這裡我們直接用UDP協議,即 IPPROTO_UDP	
//返回值:int型,成功返回套接字描述符,失敗返回-1

2.程式的第二步是為套接字繫結地址資訊,確定socket能夠操作緩衝區。 首先我們要提到一個網路地址結構體,sockaddr_in.

struct sockaddr_in {    
	sa_family_t    sin_family; /*協議型別: AF_INET */   
	 in_port_t      sin_port;   /* 網路位元組序 */    
 	struct in_addr sin_addr;   /*網路地址*/
};

然後就是為套接字繫結地址資訊的函式,bind.

#include<sys/socket.h>	//標頭檔案
 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);    
//	sockfd:套接字描述符   
//	addr:要繫結的地址資訊    
//	addrlen:地址資訊的長度
//	返回值:int型,成功返回0,失敗返回-1

3.程式的第三步是接收資料

#include<sys/type.h>		//標頭檔案
#include<sys/socket.h>
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen)
//	sockfd:套接字描述符
//	buf:用於儲存接受的資料,把資料拷貝到buf當中
//	len:想要接受的資料長度
//	flags:預設為0,如果緩衝區沒有資料,就阻塞等待直到有資料並拷貝完畢
//	src_addr:用於確定資料是哪一個客戶端傳送的,即確定傳送端的地址資訊
//	addrlen:傳送端地址資訊的長度,不能為0
//	返回值:ssize_t型別,	成功返回實際接收資料的長度,失敗返回-1

4.程式的第四步是傳送資料

#include<sys/types.h>	//標頭檔案
#include<sys/socket.h>
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:地址資訊長度
//	返回值:ssize_t型別,返回實際傳送的資料長度,失敗返回-1

5.程式的第五步是關閉socket,即close(sockfd) 上面五個步驟是伺服器端的步驟,客戶端的步驟類似,我不做講解,下面我把程式碼分享給大家。 伺服器端:udp_client.c

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<errno.h>
  5 #include<string.h>
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 
 10 int main()
 11 {
 12         int sockfd=socket(AF_INET,SOCK_DGRAM,0);
 13         if(sockfd<0)
 14         {
 15                 perror("socket error\n");
 16                 return -1;
 17         }
 18         struct sockaddr_in server;
 19         server.sin_family = AF_INET;
 20         server.sin_port=htons(9000);
 21         server.sin_addr.s_addr=inet_addr("192.168.1.104");
 22 
 23         char buff[1024]={0};
 24         struct sockaddr_in peer;
 25         while(1)
 26         {
 27                 socklen_t len=sizeof(peer);
 28                 printf("please enter:");
 29                 fflush(stdout);
 30                 ssize_t s=read(0,buff,sizeof(buff)-1);
 31                 if(s>0)
 32                 {
 33                         buff[s-1]=0;
 34                         sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&server,sizeof(server));
 35                         ssize_t _s=recvfrom(sockfd,buff,sizeof(buff)-1,0,(struct sockaddr*)&peer,&len);
 36                         if(_s>0)
 37                         {
 38                                 buff[_s]=0;
 39                                 printf("Server echo#%s\n",buff);
 40                         }
 41                 }
 42         }
 43 
 44 
 45         return 0;
 46 }

客戶端:udp_server.c

 1 //這是一個非常簡單的udp服務端程式
  2 //功能是:客戶端與服務端的聊天程式
  3 
  4 #include<stdio.h>
  5 #include<unistd.h>
  6 #include <sys/socket.h>
  7 #include<errno.h>
  8 #include<string.h>
  9 #include<netinet/in.h>
 10 #include<arpa/inet.h>
 11 #include<stdlib.h>
 12 
 13 int main()
 14 {
 15         //1.建立套接字
 16         //int socket(int domain, int type, int protocol);
 17         int sockfd =socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
 18         if(sockfd <0)
 19         {
 20                 perror("socket error\n");
 21                 return -1;
 22         }
 23         //2.為套接字繫結地址資訊
 24         //  int bind(int sockfd, const struct sockaddr *addr,
 25         //                  socklen_t addrlen)
 26         struct sockaddr_in addr;
 27         addr.sin_family=AF_INET;
 28         addr.sin_port=htons(9000);
 29         addr.sin_addr.s_addr=inet_addr("192.168.1.104");
 30         socklen_t addrlen =sizeof(struct sockaddr_in);
 31         int ret=bind(sockfd,(struct sockaddr*)&addr,addrlen);
 32         if(ret<0)
 33         {
 34                 perror("bind error\n");
 35                 close(sockfd);
 36                 return -1;
 37         }
 38         //3.接收資料
 39         // ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
 40         //                         struct sockaddr *src_addr, socklen_t *addrlen);
 41         //      sockfd: socket描述符
 42         //      buf:   用於將儲存接收的資料
 43         //      len:   想要接收的資料長度
 44         //      flags: 0-預設是說如果緩衝區沒有資料,那麼我就阻塞等待
 45         //      src_addr:  用於確定資料的傳送端地址資訊
 46         //      addrlen:   地址資訊的長度
 47         //      返回值:實際接收的資料長度 ,-1:失敗
 48         while(1)
 49         {
 50                 //接收資料
 51                 char buff[1024]={0};
 52                 struct sockaddr_in cli_addr;
 53                 addrlen=sizeof(struct sockaddr_in);
 54                 ssize_t rlen=recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&cli_addr,&addrlen);
 55                 if(rlen<0)
 56                 {
 57                         perror("recvfrom error\n");
 58                         close(sockfd);
 59                         return -1;
 60                 }
61                 //inet_ntoa:將網路地址轉換成“.”點隔的字串格式
 62                 //ntohs:將網路位元組序轉成埠位元組序。
 63                 printf("client[%s:%d] say:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buff);
 64                 //傳送資料
 65                 //ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
 66                 //                      const struct sockaddr *dest_addr, socklen_t addrlen);
 67                 //sockfd:socket描述符,傳送資料的時候就是通過這個socket所繫結的地址來發送。
 68                 //buf:要傳送的資料
 69                 //len:要傳送資料的長度
 70                 //flag:0-預設阻塞式傳送
 71                 //dest_addr:資料要傳送到的對端地址,目的地址
 72                 //addrlen:地址資訊長度
 73                 //返回值:返回時記得傳送長度,失敗返回-1/ 
 74                 memset(buff,0x00,1024);//將陣列初始化為0
 75                 printf("server say:");
 76                 scanf("%s",buff);
 77                 sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&cli_addr,addrlen);
 78 
 79         }
 80         close(sockfd);
 81         return 0;
 82 }

執行結果: 在這裡插入圖片描述 在這裡插入圖片描述