Linux中用socket實現UDP網路程式
阿新 • • 發佈:2018-12-11
這篇部落格的目的是想實現一個簡單的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 }
執行結果: