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