1. UDP協議介紹
UDP協議 相對TCP協議來講屬于不可靠協議,UDP協議是廣播方式發送數據,沒有服務器和客戶端的概念。
在Linux下使用socket創建UDP的套接字時,屬性要選擇數據報類型SOCK_DGRAM
。
sockfd=socket(AF_INET,SOCK_DGRAM,0);
復制代碼
2. UDP協議發送和接收數據的函數
2.1 recvfrom函數
UDP使用recvfrom()函數接收數據,他類似于標準的read(),但是在recvfrom()函數中要指明數據的目的地址。
#include
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * from, size_t *addrlen);
復制代碼
返回值
成功返回接收到數據的長度,負數失敗
前三個參數等同于函數read()的前三個參數,flags參數是傳輸控制標志。最后兩個參數類似于accept的最后兩個參數(接收客戶端的IP地址)。
2.2 sendto函數
UDP使用sendto()函數發送數據,他類似于標準的write(),但是在sendto()函數中要指明目的地址。
#include
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen);
復制代碼
返回值
成功返回發送數據的長度,失敗返回-1
前三個參數等同于函數read()的前三個參數,flags參數是傳輸控制標志。
參數to指明數據將發往的協議地址,他的大小由addrlen參數來指定。
2.3 設置套接字屬性
#include /* See NOTES */
#include
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
復制代碼
setsockopt()函數用于任意類型、任意狀態套接口的設置選項值。盡管在不同協議層上存在選項,但本函數僅定義了最高的“套接口”層次上的選項。選項影響套接口的操作,諸如加急數據是否在普通數據流中接收,廣播數據是否可以從套接口發送等等。
參數介紹:
sockfd:標識一個套接口的描述字。
level:選項定義的層次;目前僅支持SOL_SOCKET和IPPROTO_TCP層次。
optname:需設置的選項。
optval:指針,指向存放選項值的緩沖區。
optlen:optval緩沖區的長度。
UDP協議發送數據時,設置具有廣播特性: 默認情況下socket不支持廣播特性
char bBroadcast=1;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(char));
復制代碼
3.案例: UDP協議完成數據收發
3.1 接收數據示例
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
/* According to POSIX.1-2001 */
#include
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("參數: ./tcp_server <端口號>\n");
return 0;
}
/*1. 創建socket套接字*/
int sockfd;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
printf("服務器:套接字創建失敗.\n");
return 0;
}
/*2. 綁定端口號*/
struct sockaddr_in addr;
addr.sin_family=AF_INET; //IPV4
addr.sin_port=htons(atoi(argv[1])); //65535
//addr.sin_addr.s_addr=inet_addr("192.168.2.16");
addr.sin_addr.s_addr=INADDR_ANY; //本地所有IP地址 "0.0.0.0"
if(bind(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr)))
{
printf("服務器:端口號綁定失敗.\n");
return 0;
}
/*3. 接收數據*/
char buff[100];
struct sockaddr_in from;
size_t addrlen=sizeof(struct sockaddr);
ssize_t len;
while(1)
{
//帶阻塞功能,收到數據才會返回
len=recvfrom(sockfd,buff,sizeof(buff)-1,0,(struct sockaddr*)&from,&addrlen);
buff[len]='\0';
printf("接收到數據:%s,長度=%d,數據來自于:%s:%d\n",buff,len,inet_ntoa(from.sin_addr),ntohs(from.sin_port));
}
}
復制代碼
3.2 發送數據示例
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
/* According to POSIX.1-2001 */
#include
#include
int main(int argc,char **argv)
{
if(argc!=4)
{
printf("參數: ./tcp_client <端口號> <發送的數據>\n");
return 0;
}
/*1. 創建socket套接字*/
int sockfd;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
printf("服務器:套接字創建失敗.\n");
return 0;
}
const int opt = 1;
//設置該套接字為廣播類型,
int nb = 0;
nb = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
if(nb == -1)
{
printf("設置廣播類型錯誤.\n");
}
/*2. 開始發送數據*/
struct sockaddr_in addr;
addr.sin_family=AF_INET; //IPV4
addr.sin_port=htons(atoi(argv[2])); //65535 服務器的端口號
addr.sin_addr.s_addr=inet_addr(argv[1]); //服務器IP地址
ssize_t len;
while(1)
{
len=sendto(sockfd,argv[3],strlen(argv[3]),0,(const struct sockaddr*)&addr,sizeof(struct sockaddr));
printf("成功發送:%d\n",len);
sleep(1);
}
}
復制代碼地址>
4. 案例: 使用UDP協議探測在線好友
前面幾篇文章介紹了Linux下TCP協議設計的群聊天室的一個程序,如果想要知道同一個網絡下有多少好友在線,就可以使用UDP協議進行廣播探測。 大家的端口號是固定的,也就是只要在這個網絡范圍內,大家都跑這個同一個聊天室程序,就可以互相探測,得到對方IP地址之后,再完成TCP協議建立,完成點對點聊天通信。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
?
?
#define SEND_MSG "1314520" //發送的數據包
#define PORT 8888 //固定的端口號
?
int sockfd;
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("./app <廣播地址> 當前程序固定的端口號是8888\n");
return 0;
}
/*1. 創建socket套接字*/
sockfd=socket(AF_INET,SOCK_DGRAM,0);
//設置端口號的復用功能
int on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
/*2. 綁定端口號與IP地址*/
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT); // 端口號0~65535
addr.sin_addr.s_addr=INADDR_ANY; //inet_addr("0.0.0.0"); //IP地址
if(bind(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr))!=0)
{
printf("UDP服務器:端口號綁定失敗.\n");
return 0;
}
/*3. 接收數據*/
unsigned char buff[1024+1];
int cnt;
struct sockaddr_in client_addr;
socklen_t addrlen=sizeof(struct sockaddr_in);
struct pollfd fds;
fds.fd=sockfd;
fds.events=POLLIN;
while(1)
{
cnt=poll(&fds,1,1000);
if(cnt>0)
{
cnt=recvfrom(sockfd,buff,1024,0,(struct sockaddr *)&client_addr,&addrlen);
buff[cnt]='\0';
//判斷是不是探測包數據
if(strcmp(buff,SEND_MSG)==0)
{
printf("在線好友:%s,%d-->%s:%d\n",buff,cnt,inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
cnt=sendto(sockfd,SEND_MSG,strlen(SEND_MSG),0,(const struct sockaddr *)&client_addr,sizeof(struct sockaddr));
printf("回應探測包:%d字節.\n",cnt);
?
//這里可以繼續寫代碼,將存在的好友保存在鏈表,并記錄在線好友數量
}
}
else
{
ssize_t cnt;
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT); // 端口號0~65535
addr.sin_addr.s_addr=inet_addr(argv[1]); //IP地址
?
cnt=sendto(sockfd,SEND_MSG,strlen(SEND_MSG),0,(const struct sockaddr *)&addr,sizeof(struct sockaddr));
printf("探測包發送:%d字節.\n",cnt);
}
}
return 0;
}
審核編輯:湯梓紅
-
Linux
+關注
關注
87文章
11292瀏覽量
209334 -
UDP
+關注
關注
0文章
325瀏覽量
33931 -
網絡編碼
+關注
關注
0文章
38瀏覽量
11599
發布評論請先 登錄
相關推薦
評論