基本UDP套接字程式設計
- 概述
- recvfrom 和sendto函數
- UDP回射程式
概述
UDP是無連接配接不可靠的資料報協定,不同于TCP提供的面向連接配接的可靠位元組流。常用的應用包括:DNS(域名系統),NFS(網絡檔案系統),SNMP(簡單網絡管理協定)。
圖中給出了典型的UDP客戶/伺服器的函數調用。客戶不與伺服器建立連接配接,而是隻管使用sendto函數給伺服器發送資料報,其中必須指定目的地,(即伺服器)的位址作為參數。類似的,伺服器不接受來自用戶端的連接配接,而是隻管調用recvfrom函數,等待來自某個客戶的資料到達。recvform将所接收的資料報一道傳回客戶的協定位址,是以伺服器可以把響應發送給正确的客戶。
recvfrom 和sendto函數
這兩個函數類似于标準的read 和write函數,不過需要額外的三個參數
#include <sys/socket.h>
ssize_t recvform(int sockfd, void *buff , size_t nbytes, int flags ,
struct sockaddr *from , socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff , size_t nbytes, int flags ,
struct sockaddr *to , socklen_t addrlen);//若成功則傳回讀或寫的位元組大小,若出錯傳回-1
前三個參數等同于read和write的三個參數,分别為:描述符、指向讀入或寫出緩沖區的指針和讀寫位元組數。
flags會在後續讨論recv,send等函數時說明,目前暫時都設為0。
recvfrom的from參數指向一個将由該函數在傳回時填寫資料報發送者的協定位址的套接字位址結構(從哪裡接收),其大小由addrlen指定;
sendto的to參數指向一個含有資料報接收者的協定位址的套接字位址結構(發到哪裡去),其大小由addrlen指定。
注意:sendto的最後一個參數是一個整型值,而recvfrom則是一個指向整數值的指針。
不同于TCP在read傳回0時表示對端關閉連接配接,UDP可以寫一個長度為0的資料報,接受的資料報也可以長度為0,UDP沒有關閉一個連接配接的概念。
如果recvfrom的from參數是一個空指針,那麼相應的長度參數也必須是一個空指針,表示我們并不關心資料發送者的協定位址。
UDP回射程式
使用UDP來重寫之前TCP的回射程式,函數流程圖如下圖所示。
UDP回射伺服器程式如下所示:
#include "unp.h"
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);//UDP面向資料報,SOCK_DGRAM為資料報套接字
bzero(&servaddr, sizeof(servaddr));//初始化套接字
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));//将本地協定位址指派給套接字
dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));//調用回射程式
}
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE];
for ( ; ; ) {
len = clilen;
n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);//接受客戶的消息
Sendto(sockfd, mesg, n, 0, pcliaddr, len);//回射給用戶端
}
}
UDP回射客戶程式如下所示:
#include "unp.h"
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: udpcli <IPaddress>");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(7);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);//将點分十進制位址轉換成二進制位址
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);//建立套接字
dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));//調用回射程式
exit(0);
}
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1];
while (Fgets(sendline, MAXLINE, fp) != NULL) {//從标準輸入讀入資料
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);//将文本發送給伺服器
n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);//讀取回射後的文本
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);//标準輸出
}
}
測試結果
ubuntu15.10環境下,測試上述代碼:
@Inspiron-N4010:~/unp$ ./udpcli 127.0.0.1
hello
hello
回射伺服器正确傳回用戶端發送的資料
轉載自:https://zcheng.ren/posts/UnixNetworkProgrammingPart6/#disqus_thread