欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

网络编程--UDP

程序员文章站 2022-06-30 10:27:27
...

说明:只供学习交流,转载请注明出处

面向连接的套接字使用的通信协议是TCPTCP协议可以保证信息的可靠传输,但是在建立连接前需要进行3次握手过程。网络通信在很多时候只是传输很少的数据,并且对数据传输的可靠性没有很高的要求,在这种情况下,可以使用UDP协议。UDP协议是无连接的数据通信协议。由于UDP协议没有建立连接的过程,UDP协议通信效率要高于TCP协议。

一,工作流程

无连接套接字通信不需要服务器与客户机之间建立连接,因此也就没有使用listen函数实现监听和调用accept函数建立服务器与客户机之间连接的过程。要在服务器和客户机之间实现通信必须指定另一方的IP地址。

无连接的套接字通信具体工作流程如下图所示。首先服务器和客户机都需要建立用于通信的套接字,并将其地址信息进行绑定。在完成这些操作之后,就可以通过sendtorecvfrom函数进行通信了。

网络编程--UDP

二,recvfrom函数

recvfrom函数用于接收通过套接字发送来的消息,该函数的具体信息如下表:

头文件

#include <sys/types.h>

#include <sys/socket.h>

函数原型

ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t fromlen);

返回值

成功

失败

是否设置errno

接收到的字节数

-1

说明:recvfrom函数可用于无连接或面向连接的套接字通信中。参数s为套接字文件描述符。参数buf为接收缓冲区。参数len为接收缓冲区长度。flags为控制参数,用于控制是否接收带外数据,通常设为0,常用的值还有:

MSG_PEEK:返回的数据不会在系统系统内删除。

MSG_WAITALL:强迫接收到len大小的数据后才返回。

MSG_NOSIGNAL:不会被SIGPIPE信号中断。

from用于记录发送端的地址结构。fromlen为发送段地址结构的长度。

错误信息:

EAGAIN:套接字处于非阻塞状态。

EBADF:非法的文件描述符。

ECONNABORTED:远程主机拒绝网络连接。

EFAULT:指向接收数据的缓冲区指针指向了非法地址空间。

EINTR:系统调用被信号中断。

EINVAL:非法参数。

ENOTCONN:套接字使用了面向连接的协议,但是并没有建立连接。

ENOTSOCK:文件描述符为文件的文件描述符。

三,sendto函数

sendto函数用于发送信息给指定的主机,该函数的具体信息如下表:

头文件

#include <sys/types.h>

#include <sys/socket.h>

函数原型

ssize_t sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

返回值

成功

失败

是否设置errno

发送的字节数

-1

说明:sendto函数可用于无连接或面向连接的套接字通信中。当用于面向连接的通信(套接字类型为SOCK_STREAMSOCK_SEQPACKET),参数totolen将被忽略。

sendto函数中参数s为套接字文件描述符。参数buf为指向要发送信息地址空间的指针。参数len为要发送的字节数。参数flags为相关控制参数,用于控制是否接收数据以及是否预览报文。参数from为指向存放接收方地址信息的指针。fromlen为接收端的地址结构的长度。

错误信息:

EBADF:非法的文件描述符。

ECONNRESET:连接重置。

EDESTADDRREQ:在套接字操作中没有指定目标地址。

EFAULT:参数指向了非法的地址空间。

EINTR:数据发送前,捕获到信号。

EINVAL:非法参数。

ENOTSOCK:参数非套接字的文件描述符。

ENOMEM:内存不足。

UNIX domain中面向无连接的通信实例:

Server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define UNIX_DOMAIN "UNIX.domain"

int main(void)
{
	socklen_t addr_len;
	int listen_fd;
	int com_fd;
	int ret;
	int i;
	static char recv_buf[1024];
	int len;

	struct sockaddr_un clt_addr;
	struct sockaddr_un srv_addr;

	com_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
	if (com_fd < 0)
	{
		perror("Cannot create listening socket");
		return (1);
	}

	srv_addr.sun_family = AF_UNIX;
	strcpy(srv_addr.sun_path, UNIX_DOMAIN);
	unlink(UNIX_DOMAIN);

	ret = bind(com_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr.sun_family)+
								strlen(srv_addr.sun_path));
	if (ret == -1)
	{
		perror("Cannot bind server socket");
		close(com_fd);
		unlink(UNIX_DOMAIN);
		return (1);
	}

	for (i = 0; i < 4; i++)
	{
		memset(recv_buf, 0, 1024);
		int num = recvfrom(com_fd, recv_buf, 1024, 0, (struct sockaddr*)&clt_addr, &addr_len);
		printf("Message from client (%d) : %s\n", num, recv_buf);
	}

	close(com_fd);

	unlink(UNIX_DOMAIN);

	return (0);
}


Client.c

与服务器类似,程序也是首先创建通信的套接字,然后使用bind函数将文件信息与套接字绑定。值得注意的是,为了保证客户端的用于通信的文件的唯一性,使用了mkstemp函数。使用mkstemp函数将获得文件名唯一的临时文件。

之所以要保证文件名的唯一性,是因为服务器进程没有向客户端进程发送消息,而只是读取了客户端发送来的消息。对服务器而言,可以通过recvfrom中获得的值来判断是哪个客户端发送的消息。但是,如果客户端使用相同的文件的话,在多客户端的情况下,服务器就无法判断要将消息发送给哪个客户端了。通过文件名的唯一性,这一个问题得到了解决。

具体代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define UNIX_DOMAIN "UNIX.domain"

int main(void)
{
	int com_fd;
	int ret;
	char snd_buf[1024] = {'\0'};
	int i;

	static struct sockaddr_un srv_addr;
	static struct sockaddr_un clt_addr;
	char clt_file[] = "xxxx";

	srv_addr.sun_family = AF_UNIX;
	strcpy(srv_addr.sun_path, UNIX_DOMAIN);

	com_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
	if ( com_fd < 0 )
	{
		perror("Cannot create communication socket");
		return (1);
	}

	mkstemp(clt_file);
	clt_addr.sun_family = AF_UNIX;
	strcpy(clt_addr.sun_path, clt_file);
	unlink(clt_file);

	ret = bind(com_fd, (struct sockaddr*)&clt_addr, sizeof(clt_addr.sun_family)+strlen(clt_addr.sun_path));
	if (ret == -1)
	{
		perror("Cannot bind server socket");
		return (1);
	}

	memset(snd_buf, 0, 1024);
	sprintf(snd_buf, "%ld : message from client", (long)getpid());

	for (i = 0; i < 4; i++)
	{
		sleep(1);
		int num = sendto(com_fd, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&srv_addr,
							sizeof(struct sockaddr));
		printf("send %d characters\n", num);
	}

	close(com_fd);
	unlink(clt_file);

	return (0);
}


ternet domain中面向无连接通信实现实例:

Server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
	int sock;
	int length;
	int fromlen;
	int n;
	struct sockaddr_in server;
	struct sockaddr_in from;
	char buf[1024] = {'\0'};

	if (argc != 2)
	{
		printf("Usage: %s port_num\n", argv[0]);
		return (1);
	}

	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0)
	{
		perror("Cannot create communicating socket");
		return (1);
	}

	length = sizeof(server);
	bzero(&server, length);
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port=htons(atoi(argv[1]));

	if (bind(sock, (struct sockaddr*)&server, length) < 0)
	{
		perror("Cannot bind the socket");
		close(sock);
		return (1);
	}

	fromlen = sizeof(struct sockaddr_in);

	while ( 1 )
	{
		n = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen);
		if ( n < 0 )
		{
			perror("Cannot receive date from client");
			break;
		}

		write(STDOUT_FILENO, "server: Received a datagram: ", 29);
		write(STDOUT_FILENO, buf, n);

		n = sendto(sock, "send message to client\n", 22, 0, (struct sockaddr*)&from, fromlen);
		if (n < 0)
		{
			perror("Cannot send data to the client");
			break;
		}
	}

	close(sock);

	return (0);
}


Client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int sock;
	int length;
	int n;
	struct sockaddr_in server;
	struct sockaddr_in from;
	struct hostent *hp;
	char buffer[256] = {'\0'};

	if (argc != 3)
	{
		printf("Usage : %s server_ip port_num\n", argv[0]);
		return (1);
	}

	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0)
	{
		perror("Cannot create communicating socket");
		return (1);
	}

	server.sin_family = AF_INET;
	hp = gethostbyname(argv[1]);
	if (hp == 0)
	{
		perror("cannot get the server ip address");
		return (1);
	}

	bcopy((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length);
	server.sin_port = htons(atoi(argv[2]));
	length = sizeof(struct sockaddr_in);

	printf("(client) enter the message: ");
	bzero(buffer, 256);
	fgets(buffer, 255, stdin);

	n = sendto(sock, buffer, strlen(buffer), 0, &server, length);
	if (n < 0)
	{
		perror("Cannot send message to the server");
		return (1);
	}

	bzero(buffer, 256);
	n = recvfrom(sock, buffer, 256, 0, &from, &length);
	if ( n < 0 )
	{
		perror("Cannot get message from server");

	}

	printf("Client get message : %s\n", buffer);

	close(sock);

	return (0);
}