进程通信之Udp

目录

  • Ucp的概念
  • UDPclient.c, UDPserver.c–这里是客户端给服务器发数据,反过来自己改改
  • 输出
    注意:udp提供无连接服务的,所以没有像tcp的listen(),accept(),connect(),创建好套接字后,就直接通信:sendto(),recvfrom()

(一)udp的概念–User Datagram Protocol(用户数据报协议)

  • TCP协议和UDP协议是5层网络协议传输层最重要的协议
  • TCP是面向连接的传输控制协议
  • UDP提供了无连接的数据报服务
  • 熟知协议及端口号:DNS–53; TFTP----69; SNMP—161

(二)UDPclient.c

写在前面:建议参考一定要阅读man 7 ip

(1)建立socket服务

//creates an endpoint for communication and returns a descriptor
int socket(int domain, int type, int protocol);

参数分析:

  • domain:使用的协议,下面是所有取值
    在这里插入图片描述
  • type:套接口的类型描述,下面是所有取值
    在这里插入图片描述
  • 返回值: On success, a file descriptor for the new socket is returned. On error, -1 is returned

上面简单的阐释了下,下面重点看ipv4的实现。 man 7 ip中详细描述了ipv4的实现

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */

tcp_socket = socket(AF_INET, SOCK_STREAM, 0);				//TCP--这么写就对了,这个组合是固定的
udp_socket = socket(AF_INET, SOCK_DGRAM, 0);				//UDP--这么写就对了,这个组合是固定的
raw_socket = socket(AF_INET, SOCK_RAW, protocol);			//原始套接字

在这里插入图片描述

细心地同学会发现这里用的PF_INET,而不是AF_INET,其实两个在数值上是一样的。
  • AF = Address Family—地址族,用在sockaddr_in中指定address family时一般设置为AF_INET
  • PF = Protocol Family—协议族,socket使用的协议是PF_INET
  • 混用其实没问题的,数值是一样的,上面是标准写法,指定协议用PF_INET
    文件路径:/usr/include/bits/socket.h
    在这里插入图片描述

(2)sockaddr_in 结构体–*man 7 ip

struct sockaddr_in {
   sa_family_t    sin_family; //address family:AF_INET--always set to AF_INET. This is requiredz(讲的很清楚,必须是这个值,这是规定)
   in_port_t      sin_port;   //port in network byte order--要用honts转换
   struct in_addr sin_addr;   //internet address 
};

/* Internet address. */
struct in_addr {
   uint32_t       s_addr;     //address in network byte order
};
  • sin_family: ipv4的tcp/udp取AF_INET
  • sin_port:端口号—必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字
  • sin_addrs._addr:IP地址—必须要采用网络数据格式,用到了我再说
    在这里插入图片描述
    补充,涉及格式转换,通信内部不识我们平常的一些术语,需要转化成网络字节顺序
//一段内存置零
extern void bzero(void *s, int n);
  s 要置零的数据的起始地址;
  n 要置零的数据字节个数
  如:bzero(&s_add,sizeof(struct sockaddr_in));
  
//将一个点分十进制的IP转换成一个长整数型数(u_long类型)
in_addr_t inet_addr(const char *cp)
  如:inet_addr(argv[1])

//将主机的无符号短整形数转换成网络字节顺序  
uint16_t htons(uint16_t hostshort);
  hostshort:主机字节顺序表达的16位数	
  如:s_add.sin_port=htons(portnum);

(3)发送数据

char sendline[100];														//待发送的数组被写入 Hello, world! 了
sprintf(sendline, "Hello, world!");
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
  • sockfd :socket返回的套接字
  • buf :待发送数据的缓冲区
  • len :缓冲区长度
  • flags :标志位给0即可
  • dest_addr:目的套接口的地址
  • addrlen :所指地址的长度

(三)UDPserver.c

前面和客户端差不多一样的,获取本机ip地址是INADDR_ANY
也是先建立socket服务----初始化struct sockaddr_in结构体。这里需要注意的是:
INADDR_ANY:(0.0.0.0)可以监听的所有地址

  • 0.0.0.0/8可以表示本网络中的所有主机
  • 0.0.0.0/32可以用作本机的源地址
  • 0.0.0.0/0表示默认路由
  • 0.0.0.0/0已经不是一个真正意义上的IP地址了。它表示的是一个集合:所有未知的主机和目的网络。路由表中无法查询的包都将送到全零网络的路由中去。

INADDR_LOOPBACK: (127.0.0.1)环回地址,数据报不会发出去的地址
INADDR_BROADCAST:(255.255.255.255)广播地址
在这里插入图片描述
(1)捆绑(主机地址INADDR_ANY–自动填本机地址/本机上的一个端口号–不指定随机获取)

//bind a name to a socket--分配一个本地名字
//It is normally necessary to assign a local address using bind() before a SOCK_STREAM socket may receive connections (see accept(2)).
int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);

在这里插入图片描述
(2)接收客户端发送的数据

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
  • sockfd :socket返回的套接字
  • buf :待发送数据的缓冲区
  • len :缓冲区长度
  • flags :标志位给0即可
  • src_addr :套接口的地址
  • addrlen :所指地址的长度
    在这里插入图片描述
    代码:
    UDPclient.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>

/****************************************************************************************
**										Udp(客户端)
** socket():建立socket服务
**	 int socket(int domain, int type, int protocol);
** domain  :使用的协议
** type    :套接口的类型描述
** protocol:0
** RETURN VALUE
** 	 On success, a file descriptor for the new socket is returned. On error, -1 is returned

** sprintf():将字符串写进str所指向的数组指针
**	  int sprintf(char *str, const char *format, ...);
** str  :字符数数组

** sendto():发送数据
**   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
** sockfd   :socket返回的套接字
** buf      :待发送数据的缓冲区
** len      :缓冲区长度
** flags    :标志位给0即可
** dest_addr:目的套接口的地址
** addrlen  :所指地址的长度
  
** inet_addr():将一个点分十进制的IP转换成一个长整数型数(u_long类型)
** in_addr_t inet_addr(const char *cp)

** htons():将主机的无符号短整形数转换成网络字节顺序  
** uint16_t htons(uint16_t hostshort);
**   hostshort:主机字节顺序表达的16位数	
****************************************************************************************/

int main(int argc, char **argv)
{
	int i = 10;
	int sockfd;											//udp返回套接字描述符
	struct sockaddr_in servaddr;

	if(argc != 2){
		printf("usgae: ./client [ip]\n");
		return -1;
	}

	/* 创建一个UDP的socket连接 */
	sockfd = socket(PF_INET, SOCK_DGRAM, 0);			//#define AF_INET  PF_INET(数值是一样的)

	/* 变量servaddr清零 */
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(50001);					//在临时端口,给用户进程使用的49152-65535
	servaddr.sin_addr.s_addr = inet_addr(argv[1]);

	char sendline[100];
	sprintf(sendline, "Hello, world!");

	for(; i>0;--i){
		/*  发送数据 */
		sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
	}

	/* 关闭socket连接 */
	close(sockfd);

	return 1;
}

UDPserver.c

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

/****************************************************************************************
**										Udp(服务器)
** socket():建立socket服务
**	 int socket(int domain, int type, int protocol);
** domain  :使用的协议
** type    :套接口的类型描述
** protocol:0
** RETURN VALUE
** 	 On success, a file descriptor for the new socket is returned. On error, -1 is returned

** bind():捆绑(主机地址/端口号)
**	  int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);
** sockfd  :socket的返回值
** addr    :struct sockaddr结构体指针
** addrlen :结构体大小
** RETURN VALUE
** 	 If the connection or binding succeeds, zero is returned. On error, -1 is returned

** recvfrom():接收数据
**   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
** sockfd   :socket返回的套接字
** buf      :待发送数据的缓冲区
** len      :缓冲区长度
** flags    :标志位给0即可
** src_addr :源套接口的地址
** addrlen  :所指地址的长度
  
** inet_addr():将一个点分十进制的IP转换成一个长整数型数(u_long类型)
** in_addr_t inet_addr(const char *cp)

** htons():将主机的无符号短整形数转换成网络字节顺序  
** uint16_t htons(uint16_t hostshort);
**   hostshort:主机字节顺序表达的16位数	
****************************************************************************************/

int main(int argc, char **argv)
{
	int n;
	char recvline[1024] = {0};

	int sockfd;													//udp返回套接字描述符
	struct sockaddr_in servaddr;

	/* 创建一个UDP连接的socket */
	sockfd = socket(PF_INET, SOCK_DGRAM, 0);					//#define AF_INET  PF_INET(数值是一样的)

	/* 变量servaddr清零 */
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);	
	servaddr.sin_port = htons(50001);							//在临时端口,给用户进程使用的49152-65535

	/* 绑定servaddr到创建的socket上 */
	bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	while(1){
		/* 接收客户端发送的数据 */
		recvfrom(sockfd, recvline, 1024, 0, NULL, NULL);
		printf("%s\n", recvline);
	}
	/* 关闭socket连接 */
	close(sockfd);
}

(四)输出

  • 客户端是Arm开发板,用Arm的编译器
  • 服务器选择运行在X86的虚拟机(刚装的虚拟机,没有配置),其实其他的都可以,你要有对应编译器就可以了
  • 客户端给服务器发送10次数据。

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值