网络协议(三)Socket编程之UDP

来源:互联网 发布:mac ndk 编辑:程序博客网 时间:2024/05/18 03:03

UDP(User Datagram Protocol)是无连接不可靠的数据报协议。使用的UDP的常用应用有:DNS、NFS、SNMP、tftp等。

典型的UDP客户-服务器程序


#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
               const struct sockaddr *addr, socklen_t addrlen);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sendto的最后两个参数类似于connect的最后两个参数:调用时其中套接字地址结构被我们填入数据报将发往(UDP)或与之建立连接(TCP)的协议地址。

ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,
                 struct sockaddr *addr, socklen_t *addrlen);

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
recvfrom的最后两个参数类似于accept的最后两个参数:返回时其中套接字地址结构的内容告诉我们是谁发送了数据报(UDP)或是谁发起了连接(TCP)。

(1)UDP层中隐含有排队发生。事实上每个UDP套接字都有一个接收缓冲区,到达该套接字的每个数据报都进入这个套接字接收缓冲区。当进程调用recvfrom时,缓冲区中的下一个数据报以FIFO顺序返回给进程。然而这个缓冲区的大小是有限的。(可以通过SO_RCVBUF进行设置)
(2)知道客户临时端口号的任何进程(无论是与本客户进程相同的主机上还是不同的主机上)都可以向该客户发送数据报,而且这些数据报会与正常的服务器应答混杂。(参见例一)
(3)UDP没有流量控制并且是不可靠的。
一般来说,大多数TCP服务器是并发的,而大多数UDP服务器是迭代的。


例一:

/* 客户端代码 */#include "../myunp.h"void UdpClient(int sockfd, const struct sockaddr *pservaddr, socklen_t servlen){int n = 0;char sendline[MAXLINE] = {0};char recvline[MAXLINE + 1] = {0};socklen_t len = 0;struct sockaddr *preplyaddr = NULL;while (fgets(sendline, MAXLINE, stdin) != NULL) {if (sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen) < 0) {printf("sendto error: %s\n", strerror(errno));return;}len = servlen;if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, preplyaddr, &len)) < 0) {printf("recvfrom error: %s\n", strerror(errno));return;}recvline[n] = 0; /* null terminate */fputs(recvline, stdout);}}int main(int argc, char **argv){int sockfd;struct sockaddr_in servaddr;if (argc != 2) {printf("usage: udpcli <IP address>");return -1;}memset(&servaddr, '\0', sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);inet_aton(argv[1], &servaddr.sin_addr);if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {printf("sockfd error: %s\n", strerror(errno));return -1;}UdpClient(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));return 0;}
/* 服务端代码 */#include "../myunp.h"void UdpEcho(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen){int n = 0;socklen_t len = 0;char msg[MAXLINE] = {0};for ( ; ; ) {len = clilen;if ((n = recvfrom(sockfd, msg, MAXLINE, 0, pcliaddr, &len)) < 0) {printf("recvfrom error: %s\n", strerror(errno));return ;} if (sendto(sockfd, msg, n, 0, pcliaddr, len) < 0) {printf("sendto error: %s\n", strerror(errno));return ;}}}int main(int argc, int **argv){int sockfd;struct sockaddr_in servaddr;struct sockaddr_in cliaddr;if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {printf("socked error: %s\n", strerror(errno));return -1;}memset(&servaddr, '\0', sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {printf("bind error: %s\n", strerror(errno));return -1;}UdpEcho(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));}


例二:验证接受到的响应

/* 客户端代码 */#include "../myunp.h"char *sock_ntop(const struct sockaddr *sa, socklen_t len){char sport[8] = {0};char *paddr = NULL;static char str[128] = {0};struct sockaddr_in *sin = (struct sockaddr_in *)sa;paddr = inet_ntoa(sin->sin_addr);if (ntohs(sin->sin_port) != 0) {snprintf(sport, sizeof(sport), ": %d", ntohs(sin->sin_port));strcat(str, paddr);strcat(str, sport);}return str;}void UdpClient(int sockfd, const struct sockaddr *pservaddr, socklen_t servlen){int n = 0;char sendline[MAXLINE] = {0};char recvline[MAXLINE + 1] = {0};socklen_t len = 0;struct sockaddr *preplyaddr = NULL;while (fgets(sendline, MAXLINE, stdin) != NULL) {if (sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen) < 0) {printf("sendto error: %s\n", strerror(errno));return;}len = servlen;if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, preplyaddr, &len)) < 0) {printf("recvfrom error: %s\n", strerror(errno));return;}if (len != servlen || memcmp(pservaddr, preplyaddr, len) != 0) {printf("reply from %s (ingnore)\n", sock_ntop(preplyaddr, len));continue;}recvline[n] = 0; /* null terminate */fputs(recvline, stdout);}}int main(int argc, char **argv){int sockfd;struct sockaddr_in servaddr;if (argc != 2) {printf("usage: udpcli <IP address>");return -1;}memset(&servaddr, '\0', sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(7);inet_aton(argv[1], &servaddr.sin_addr);if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {printf("sockfd error: %s\n", strerror(errno));return -1;}UdpClient(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));return 0;}
例三:使用connect函数

/* 客户端代码 */#include "../myunp.h"char *sock_ntop(const struct sockaddr *sa, socklen_t len){char sport[8] = {0};char *paddr = NULL;static char str[128] = {0};struct sockaddr_in *sin = (struct sockaddr_in *)sa;paddr = inet_ntoa(sin->sin_addr);if (ntohs(sin->sin_port) != 0) {snprintf(sport, sizeof(sport), ": %d", ntohs(sin->sin_port));strcat(str, paddr);strcat(str, sport);}return str;}int main(int argc, char **argv){int sockfd;socklen_t len;struct sockaddr_in cliaddr;struct sockaddr_in servaddr;if (argc != 2) {printf("usage: udpcli <IP address>");return -1;}if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {printf("sockfd error: %s\n", strerror(errno));return -1;}memset(&servaddr, '\0', sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);inet_aton(argv[1], &servaddr.sin_addr);if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {printf("connect error: %s\n", strerror(errno));return -1;}len = sizeof(cliaddr);getsockname(sockfd, (struct sockaddr *)&cliaddr, &len);printf("local address %s\n", sock_ntop((struct sockaddr *)&cliaddr, len));return 0;}
UDP客户端或服务端进程只在使用自己的UDP套接字与确定的唯一对端进行通信时,才应该调用。



例四:UDP缺乏流量控制

/* 客户端代码 */#include "../myunp.h"#define SENDCNT  2000#define DGLEN    1400void UdpClient(int sockfd, const struct sockaddr *pservaddr, socklen_t servlen){int i = 0;char sendline[DGLEN];for (; i < SENDCNT; i++) {if (sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen) < 0) {printf("sendto error: %s\n", strerror(errno));return;}// printf("i: %d\n", i);}}int main(int argc, char **argv){int sockfd;struct sockaddr_in servaddr;if (argc != 2) {printf("usage: udpcli <IP address>");return -1;}memset(&servaddr, '\0', sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);inet_aton(argv[1], &servaddr.sin_addr);if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {printf("sockfd error: %s\n", strerror(errno));return -1;}UdpClient(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));return 0;}
/* 服务端代码 */#include "../myunp.h"static void RecvfromInt(int);static int count = 0;static void RecvfromInt(int signo)  /* Ctrl + C */{printf("\nreceived %d datagrams\n", count);exit(0);}void UdpEcho(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen){int n = 0;socklen_t len = 0;char msg[MAXLINE] = {0};signal(SIGINT, RecvfromInt);n = 220 * 1024;setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));for ( ; ; ) {len = clilen;if (recvfrom(sockfd, msg, MAXLINE, 0, pcliaddr, &len) < 0) {printf("recvfrom error: %s\n", strerror(errno));return ;} count++;}}int main(int argc, int **argv){int sockfd;struct sockaddr_in servaddr;struct sockaddr_in cliaddr;if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {printf("socked error: %s\n", strerror(errno));return -1;}memset(&servaddr, '\0', sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {printf("bind error: %s\n", strerror(errno));return -1;}UdpEcho(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));}


#ifndef _MY_UNP_H#define _MY_UNP_H#if 0typedef uint16_t sa_family_t;typedef uint16_t in_port_t;/* Generic socket address structure (for connect, bind, and accept) */struct sockaddr {sa_family_t sa_family;   /* Protocol family */char        sa_data[14]; /* Address data */};typedef uint32_t in_addr_t;// AF_INETstruct in_addr {      /* IPv4 4-byte address */in_addr_t s_addr; /* Unsigned 32-bit integer */};/* Internet-style socket address structure */struct sockaddr_in {sa_family_t    sin_family;  /* Address family (always AF_INET)*/in_port_t      sin_port;    /* Port number in network byte order */struct in_addr sin_addr;    /* IP address in network byte order */unsigned char  sin_zero[8]; /* Pad to sizeof(struct sockaddr) */};// AF_INET6struct int6_addr {uint8_t s6_addr[16];  /* IPv6 address */};struct sockaddr_in6 {sa_family_t     sin6_family;   /* address family */in_port_t       sin6_port;     /* port number */uint32_t        sin6_flowinfo; /* traffic class and flow info */struct in6_addr sin6_addr;     /* IPv6 address */uint32_t        sin6_scope_id; /* set of interfaces for scope */};// AF_LOCALstruct sockaddr_un {sa_family_t sun_family;     char        sum_path[104]; /* null-terminated pathname */}#endif#include<sys/types.h>/* basic system data types */#include<sys/socket.h>/* basic socket definitions */#include<sys/time.h>/* timeval{} for select() */#include<time.h>/* timespec{} for pselect() */#include<netinet/in.h>#include    <arpa/inet.h>#include<errno.h>#include<fcntl.h>/* for nonblocking */#include<signal.h>#include<sys/stat.h>/* for S_xxx file mode constants */#include<sys/uio.h>/* for iovec{} and readv/writev */#include<unistd.h>#include<sys/wait.h>#include    <sys/select.h>#include    <poll.h>#include    <sys/stropts.h>#include<stdio.h>#include<stdlib.h>#include<string.h>/* Read n bytes from a descriptor */ssize_t readn(int fd, void *buf, size_t n);/* Write n bytes to a descriptor */ssize_t writen(int fd, const void *buf, size_t n);/* painfully slow version */ssize_t readline(int fd, void *buf, size_t maxlen);/* Following could be derived from SOMAXCONN in <sys/socket.h>, but many   kernels still #define it as 5, while actually supporting many more */#defineLISTENQ1024/* 2nd argument to listen() *//* Miscellaneous constants */#defineMAXLINE4096/* max text line length */#defineBUFFSIZE8192/* buffer size for reads and writes *//* Define some port number that can be used for our examples */#defineSERV_PORT 9877/* TCP and UDP */#defineSERV_PORT_STR"9877"/* TCP and UDP */#defineUNIXSTR_PATH"/tmp/unix.str"/* Unix domain stream */#defineUNIXDG_PATH"/tmp/unix.dg"/* Unix domain datagram *//* Following shortens all the typecasts of pointer arguments: */#defineSAstruct sockaddr#define max(x, y) ((x) > (y) ? (x) : (y))#define min(x, y) ((x) > (y) ? (y) : (x))// char *sock_ntop(const struct sockaddr *sa, socklen_t salen);#endif
#include "myunp.h"/* 内核用于套接字的缓冲区可能已到了极限 */ /* Read n bytes from a descriptor */ssize_t readn(int fd, void *buf, size_t n){size_t nleft = n;ssize_t nread = 0;char *ptr = buf;while (nleft > 0) {if ((nread = read(fd, ptr, nleft)) < 0) {if (errno == EINTR) {nread = 0;       /* call read() again */} else {return -1;}} else if (nread == 0) { /* EOF */ break;}nleft -= nread;ptr   += nread;}return (n - nleft);}/* Write n bytes to a descriptor */ssize_t writen(int fd, const void *buf, size_t n){size_t nleft = n;ssize_t nwritten = 0;const char *ptr = buf;while (nleft > 0) {if ((nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR) {nwritten = 0; /* call write() again */} else {return -1;    /* error */}}nleft -= nwritten;ptr   += nwritten;}return n;  /* nleft - nwritten? */}/* painfully slow version */ssize_t readline(int fd, void *buf, size_t maxlen){ssize_t n, rc;char c;char *ptr = buf;for (n = 1; n < maxlen; n++) {again:if ((rc = read(fd, &c, 1)) == 1) {*ptr++ = c;if (c == '\n') {break;}} else if (rc == 0) { /* EOF, n - 1 bytes were read */*ptr = 0;return (n - 1);} else {if (errno == EINTR)goto again;return -1;}}*ptr = 0;return n;}
总结





参考:《UNXI网络编程》

1 0