Linux C编程--网络编程3--面向无连接的网络编程

时间:2021-01-08 11:02:12

数据报套接字操作

由于底层的协议不同,数据报套接字数据流套接字有一些基本的不同。数据报套接字是UDP协议,UDP是无连接、不可靠的数据报协议。在这种通信方式中,客户不与服务建立连接,它只是通过sendto向服务程序发送数据报,sendto函数本身要求一个地址参数给出服务程序的地址。

   同样,服务程序也不接收来自客户的连接,它只是调用recvfrom函数,这个函数等待来自某个客户的数据,并随接收到的数据报一起返回客户的地址,服务程序由此可以回应客户。

   使用数据报套接字,可以将数据集中为一个包,为每个包单独地指定目的地址,并且每个包独立地进行通信。


sendto和recvfrom函数

数据报套接字上发送和接收数据的正常方法是使用sendto和recvfrom函数。sendto向数据报套接字发送数据包,recvfrom从数据报套接字读数据包,同时报告该包从何而来。它们的原型如下:

int recvfrom(int socket, void *buffer,size_tsize, int flags,

                      structsockaddr *from,size_t *addrlen);

int sendto(int socket, void *buffer,size_tsize, int flags,

                         structsockaddr *to,size_taddrlen);

    这两个函数的前3个参数socket、buffer、size与read和write的参数相同,它们分别为套接字描述符、指向读写缓冲区的指针以及读写的字节数。


说明:

recvfrom的参数from和addrlen类似于accept的最后两个参数:在函数返回时,它们给出的套接字地址结构告诉我们是谁发送的数据报。如果对这一信息不感兴趣,可指定from为一空指针,不过要注意此时参数addrlen也必须为空指针。

   sendto的最后两个参数类似于connect;当发送数据报时,要在此套接字地址结构中填入协议地址以指明数据报发送给谁。

   注意sendto的最后一个参数是整数值,而recvfrom的最后一个参数是指向整数的指针。

   这两个函数的返回值与错误条件也与send和recv的相同,函数的返回值是实际读写的字节数。

   这两个函数也可用于数据流套接字,但很少这样使用。另外,如果不必检查数据报是由谁发送的,可以使用普通的recv代替recvfrom,当不想指定flags时甚至还可用read。

Linux C编程--网络编程3--面向无连接的网络编程

上图是无连接通信的模型。


下面给出一个具体的实例说明数据报通信。

数据报通信服务器程序

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>

#define MAXMSG 1024

int main()
{
	int sockfd,nbytes;
	struct sockaddr_in addr;
	socklen_t size;
	char message[MAXMSG];
	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0)
	{
		printf("Socket created failed.\n");
		return -1;
	}
	bzero(&addr,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(6666);
	addr.sin_addr.s_addr=htonl(INADDR_ANY);
	if(bind(sockfd, (struct sockaddr *)&addr, sizeof(addr))<0)
	{
		printf("bind failed.\n");
		return -1;
	}
	while(1)
	{
		size=sizeof(addr);
		nbytes=recvfrom(sockfd,message,MAXMSG,0,(struct sockaddr *)&addr,&size);
		if(nbytes<0)
		{
		printf("recvfrom(server) failed.\n");
		return -1;
		}
		printf("Server got message %s \n",message);
		nbytes=sendto(sockfd,message,nbytes,0,(struct sockaddr*)&addr,size);
		if(nbytes<0)
		{
		printf("sendto(server) failed.\n");
		return -1;
		}
	}
}

数据报通信客户机程序

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define MAXMSG 512

int main()
{
	int sockfd,n;
	char recvbuff[MAXMSG],sndbuff[MAXMSG];
	struct sockaddr_in	servaddr;
	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0)
	{
		printf("Socket created failed.\n");
		return -1;
	}

	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(6666);
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	while(fgets(sndbuff,MAXMSG,stdin)!=NULL)
	{
		if(sendto(sockfd,sndbuff,sizeof(sndbuff),0,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
		{
			printf("(client)sending error.\n");
			return -1;
		}
		if((n=recvfrom(sockfd,recvbuff,MAXMSG,0,NULL,NULL))<0)
		{
			printf("(client)receiving error.\n");
			return -1;
		}
		recvbuff[n]=0;
		printf("Client received message: %s",recvbuff);
	}
	close(sockfd);
	return 0;
}

最后注意不要忘记用kill命令杀死服务程序,因为这个程序自己不会终止。




下图是有连接的模型和Linux网络层结构

Linux C编程--网络编程3--面向无连接的网络编程Linux C编程--网络编程3--面向无连接的网络编程

最后给出TCP/IP协议族示意:

Linux C编程--网络编程3--面向无连接的网络编程