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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jiangxt211/article/details/58676326

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    1400

void 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 0
typedef 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_INET
struct 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_INET6
struct 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_LOCAL
struct 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 */
#define	LISTENQ		1024	/* 2nd argument to listen() */

/* Miscellaneous constants */
#define	MAXLINE		4096	/* max text line length */
#define	BUFFSIZE	8192	/* buffer size for reads and writes */

/* Define some port number that can be used for our examples */
#define	SERV_PORT		 9877			/* TCP and UDP */
#define	SERV_PORT_STR	"9877"			/* TCP and UDP */
#define	UNIXSTR_PATH	"/tmp/unix.str"	/* Unix domain stream */
#define	UNIXDG_PATH		"/tmp/unix.dg"	/* Unix domain datagram */

/* Following shortens all the typecasts of pointer arguments: */
#define	SA	struct 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网络编程》

展开阅读全文

SOCKET编程UDP通信问题

06-21

rn在同一台机子上运行测试客户端和服务器端可以通信成功,但两台机器上就不行了...rn弄不明白是怎么回事,求教!rnrn服务器端:rnrn#include rn#include rnrnrnint main()rnrn WORD wVersionRequested;rn WSADATA wsaData;rn int err;rnrn wVersionRequested = MAKEWORD(1, 1);rnrn err = WSAStartup(wVersionRequested, &wsaData);rn if(err != 0)rn rn WSACleanup();rn return FALSE;rn rnrn if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)rn rn WSACleanup();rn return FALSE;rn rnrn SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);rnrn SOCKADDR_IN addSrv;rn addSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);rn addSrv.sin_family = AF_INET;rn addSrv.sin_port = htons(6000);rnrn bind(sockSrv, (SOCKADDR*)&addSrv, sizeof(SOCKADDR));rnrn char speech[100];rnrn SOCKADDR_IN addrClient;rn int len = sizeof(SOCKADDR);rnrn while(1)rn rn recvfrom(sockSrv, speech, 100, 0, (SOCKADDR*)&addrClient, &len);rn if(!strcmp(speech ,"Exit"))rn rn sendto(sockSrv, "Exit", strlen("Exit")+1, 0, (SOCKADDR*)&addrClient, len);rn printf("Chat end!\n");rn break;rn rn char s[100];rn sprintf(s, "%s: %s", inet_ntoa(addrClient.sin_addr), speech);rnrn printf("%s\n", s);rn strcpy(speech, "\0");rnrn printf("Please input data: ");rn gets(speech);rn sendto(sockSrv, speech, strlen(speech)+1, 0, (SOCKADDR*)&addrClient, len);rn strcpy(speech, "\0");rn rn closesocket(sockSrv);rn WSACleanup();rnrn return 0;rnrnrn客户端:rn#include rn#include rnrnrnint main()rnrn WORD wVersionRequested;rn WSADATA wsaData;rn int err;rnrn wVersionRequested = MAKEWORD(1, 1);rnrn err = WSAStartup(wVersionRequested, &wsaData);rn if(err != 0)rn rn WSACleanup();rn return FALSE;rn rnrn if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)rn rn WSACleanup();rn return FALSE;rn rn SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);rnrn SOCKADDR_IN addSrv;rn addSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.122");//更改IPrn addSrv.sin_family = AF_INET;rn addSrv.sin_port = htons(6000);rnrn char speech[100];rn int len = sizeof(SOCKADDR_IN);rnrn while(1)rn rn printf("Please input data: ");rn gets(speech);rn sendto(sockClient, speech, strlen(speech) + 1, 0, (SOCKADDR*)&addSrv, len);rnrn strcpy(speech, "\0");rn recvfrom(sockClient, speech, 100, 0, (SOCKADDR*)&addSrv, &len);rn if(!strcmp(speech, "Exit"))rn rn sendto(sockClient, "Exit", strlen("Exit")+1, 0, (SOCKADDR*)&addSrv, len);rn printf("Chat end!\n");rn break;rn rn char s[100];rn sprintf(s, "%s: %s", inet_ntoa(addSrv.sin_addr), speech);rn printf("%s\n", s);rn strcpy(speech, "\0");rn rn closesocket(sockClient);rn WSACleanup();rnrn return 0;rn 论坛

没有更多推荐了,返回首页