16_网络IPC6-报式套接字传输

需要掌握:

socket(),获取SOCKET
bind(), 绑定SOCKET到本地地址
inet_pton(), IP地址格式转换:点分式 --> 大整数
recvfrom(),用于报式套接字,从 SOCKET上接收信息
recv(), 用于流式套接字,从 SOCKET上接收信息
inet_ntop(),IP地址格式转换:大整数格式 --> 点分式格式
send(), 用于流式套接字 向SOCKET发送数据
sendto(),用于报式套接字,向SOCKET发送数据

跨主机通信注意:

被动端(先运行,等待被访问)

1 取得SOCKET  :socket()
2 给SOCKET取得地址,即绑定本地地址(包含端口,ip等信息): bind()
3 收/发消息  :recvfrom()
4 关闭SOCKET: close()

主动端

1 取得SOCKET
2 给SOCKET取得地址(可省略)
3 发/收消息:sendto()
4 关闭SOCKET

为什么 主动端的 bind()即绑定本地地址操作 可以省略呢?

bind() 是给SOCKET取得地址,即绑定本地地址。这个操作是和本机的约定。
发送端如果不和本机约定地址,即省略bind()操作。当前SOCKET建立成功后。系统会为我们分配一个可用的空闲的地址给我们用,在进程结束之前 该端口一直给我们用。


socket()

NAME
创建通信端点,取得SOCKET
       socket - create an endpoint for communication

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

/* 用协议族domain 中的某个协议protocol 来完成type类型的传输

注意:如果目标协议族中有一个或多个协议可以支持 目标传输类型的话,那么协议protocol 可以写0,表示用协议族中默认支持目标传输类型的协议 

如用 :soccket(AF_INET,SOCK_DGRAM,0); 表示用协议族 AF_INET协议族 默认支持报式套接字的协议 来完成 报式SOCK_DGRAM套接字传输,

domain : 域,如协议族
type :上层怎么实现
protocol:协议
*/

   int socket(int domain, int type, int protocol);

RETURN VALUE 返回值为文件描述符
On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errno is set appropriately.

domain :
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   Local communication 本地协议              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7)
       AF_IPX              IPX - Novell protocols
       AF_NETLINK          Kernel user interface device     netlink(7)
       AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
       AF_AX25             Amateur radio AX.25 protocol 无线电 短波通信
       AF_ATMPVC           Access to raw ATM PVCs
       AF_APPLETALK        AppleTalk                        ddp(7)
       AF_PACKET           Low level packet interface       packet(7)
       AF_ALG              Interface to kernel crypto API

type:

SOCK_STREAM     流式套接字,Provides sequenced, reliable, two-way, connection-based byte streams.
有序可靠:只要接收方能够接到数据,就可以保证 当前数据包中的内容是正确的。不能保证不丢包,网络传输中会有丢包
双工
基于连接:点对点,一对一
字节流传输

       SOCK_DGRAM      报式
数据分组
无连接的
不可靠的

SOCK_SEQPACKET 有序可靠的报式
有序可靠的报式

...

bind()

NAME
给SOCKET绑定一个地址
       bind - bind a name to a socket

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

/*
sockfd: 获取 SOCKET 返回的文件描述符

addr :协议地址,依赖于当前用到的协议族中的地址信息,AF_INET 协议族中的 协议地址类型为 struct sockaddr_in 

addrlen :协议地址空间大小

*/

   int bind(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);



sockaddr 依赖于当前用到的协议族中的地址信息 The sockaddr structure is defined as something like:

       struct sockaddr {
           sa_family_t sa_family;
           char        sa_data[14];
       }

不同的协议族 来 绑定自己这端的地址 所用的结构体是不一样的。所以是不存在 struct sockaddr 类型的。所以我们的处理方式是:我们用的是哪一个协议族,就把该协议族地址作为addr ,然后再把地址长度写到addrlen

AF_INET see ip(7)

man 7 ip

/*
注意 :IP地址和端口,是需跟着网络一起发送的。代表自己的身份
*/

           struct sockaddr_in {
				//协议族 address family: AF_INET
               sa_family_t    sin_family; 

 	   		    //需要的端口
               in_port_t      sin_port;  

//IP地址 并非点分式,而是大整数internet address ,用的时候需要格式转换:inet_pton()
               struct in_addr sin_addr;   
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };

所以 AF_INET 协议族中的 协议地址类型为 struct sockaddr_in

RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.


inet_pton() IP地址格式转换:点分式 --> 大整数

NAME
将  IPv4 and IPv6 addresses 转换为 二进制格式,即将点分式 转换为 二进制格式
       inet_pton - convert IPv4 and IPv6 addresses from text to binary form

SYNOPSIS
       #include <arpa/inet.h>

/*
af : 协议族,只能是 AF_INET or AF_INET6,即IPV4或者IPV6
src:IP地址
dst :转换之后的存储空间
*/

   int inet_pton(int af, const char *src, void *dst);

0.0.0.0:能够匹配任何的IP地址,表示在当前绑定这个阶段,我们自己的IP地址是多少,0.0.0.0 就会替换成我们当前的IP地址


recvfrom() 从 SOCKET上接收信息

NAME
从 SOCKET上接收信息
       recv, recvfrom, recvmsg - receive a message from a socket

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

/* 用于流式套接字
sockfd :目标SOCKET
buf :接收信息存储地址
len:接收信息存储空间 大小 
flags :特殊要求
*/

   ssize_t recv(int sockfd, void *buf, size_t len, int flags);

/* recvfrom 用于报式套接字
sockfd :目标SOCKET
buf :接收信息存储地址
len:接收信息存储空间 大小 
flags :特殊要求
src_addr : 用于保存发送端的地址,即保存对端地址,接收到消息的时候保存
addrlen :发送端地址空间大小,即对端地址代表空间的大小
*/

   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);

recv()应用于流式套接字,只需要指定目标SOCKET,和存储信息的位置即大小,以及有无特殊要求即可。因为是提前建立好链接的,一对一,点对点的连接方式,所以不用记录 对端是谁。

recvfrom() 用于报式套接字通信,接收的每一个消息来源可能不一致,所以除了需要指定目标SOCKET,和存储信息的位置即大小,以及有无特殊要求之外。还需要记录对方是谁,即发送端身份。


inet_ntop() 大整数格式 --> 点分式格式

NAME 
转换IPv4 and IPv6 addresses 地址格式, 大整数格式 --> 点分式格式
       inet_ntop - convert IPv4 and IPv6 addresses from binary to text form

SYNOPSIS
       #include <arpa/inet.h>

/*
af: 协议族
src :待转换的IP地址
dst: 将目标IP 准换到 该地址
size:dst指向的空间大小

*/

   const char *inet_ntop(int af, const void *src,
                         char *dst, socklen_t size);

send()、sendto()

NAME
       send, sendto, sendmsg - send a message on a socket

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

/* 用于流式套接字 发送数据
sockfd:目标SOCKET
buf :发送数据信息存储地址
len:发送数据信息存储空间 大小 
*/

   ssize_t send(int sockfd, const void *buf, size_t len, int flags);


/*  用于报式套接字 发送数据
sockfd:目标SOCKET
buf :发送数据信息存储地址
len:发送数据信息存储空间 大小 
flags :特殊要求
dest_addr : 接收端的地址,即对端地址
addrlen :接收端地址空间大小
*/

   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);

RETURN VALUE
On success, these calls return the number of bytes sent. On error, -1 is returned, and errno is set appropriately.


实验 :报式套接字传输数据

proto.h

#ifndef PROTO_H_
#define PROTO_H_

#define RCVPORT "1989"
#define NAMESIZE 11
#define IPSTRSIZE 40
struct msg_st
{
	char name[NAMESIZE];
	uint32_t math;
	uint32_t chinese;
}__attribute__((pack));
//UDP数据包 结构体一定不能考虑对齐,因为每个平台的上面的对齐方式可能会有差异,所以告诉gcc,该结构体 不需要对齐。

#endif

rcver.c 接收端

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include "proto.h"

int main()
{
	int sd;
	struct sockaddr_in laddr,raddr;
	struct msg_st rbuf;
	socklen_t raddr_len;
	char ipstr[IPSTRSIZE];

//取得SOCKET, 用 AF_INET协议族中 默认支持报式套接字的协议 来完成 报式SOCK_DGRAM套接字传输,
	sd = socket(AF_INET,SOCK_DGRAM,0);//IPPROTO_UDP
	if(sd < 0)
	{
		perror("soccket()");
		exit(1);
	}

//设置  AF_INET 协议族地址信息结构体,AF_INET 协议族中的 协议地址类型为 struct sockaddr_in 
 // 协议族为 AF_INET 
 laddr.sin_family = AF_INET;
// 设置端口为1989,因为需要将自己的地址信息(包括端口信息)发出去,所以需要注意字节序问题,即 从主机发向网络,htons
	laddr.sin_port = htons(atoi(RCVPORT)); 

	inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);//0.0.0.0:any address

// 给SOCKET绑定一个地址,关联到目标协议族地址信息结构体
	if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
	{
		perror("bind");
		exit(1);
	}

	/* !!!! *///一定要注意初始化对端地址空间大小信息
	raddr_len = sizeof(raddr);

	while(1)
	{
//以报式套接字方式 从 SOCKET上接收信息,raddr用于保存发送端的地址,
//即保存对端地址,接收到消息的时候保存,remote端地址信息。接收到的信息存储到rbuf
		recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len);
		
// 转换IPv4 and IPv6 addresses 地址格式, 大整数格式 --> 点分式格式 存储到ipstr
		inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);

// raddr.sin_port) 是从socket接收到的信息,不是单字节信息,需要字节序转换 ntohs
		printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));

		printf("---NAME = %s\n",rbuf.name);//单字节信息,不需要字节序转换
		printf("---MATH = %d\n",ntohl(rbuf.math));//需要字节序转换
		printf("---CHINESE = %d\n",ntohl(rbuf.chinese));//需要字节序转换
	}	

	close(sd);

	exit(0);

}

发送端:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "proto.h"

int main(int argc,char *argv[])
{
	int sd;
	struct sockaddr_in raddr;
	struct msg_st sbuf;

	if(argc < 2)
	{
		fprintf(stderr,"Usage ...\n");
		exit(1);
	}


	sd = socket(AF_INET,SOCK_DGRAM,0);//IPPROTO_UDP
	if(sd < 0)
	{
		perror("soccket()");
		exit(1);
	}

	//bind()

	strcpy(sbuf.name,"MHR");
	sbuf.math = htonl(rand()%100);
	sbuf.chinese = htonl(rand()%100);
	
	raddr.sin_family = AF_INET;
	raddr.sin_port = htons(atoi(RCVPORT));
	inet_pton(AF_INET,argv[1],&raddr.sin_addr);


	if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
	{
		perror("sendto()");
		exit(1);
	}

	puts("OK");

	close(sd);

	exit(0);

}

实验结果可以看到,数据传输成功。并且 由于主动端没有bind() 实验中的几次传输 系统给主动端分配到端口分别是 45499,56709, 51977。被动端 打印了 主动端的IP 端口 数据信息等 信息。

在这里插入图片描述
查看网络状态

netstat -anu  //u:udp 报式套接字
netstat -ant   // t:tcp 流式套接字

mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ netstat -anu
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
udp        0      0 0.0.0.0:1989            0.0.0.0:*       //可以看到 1989端口 已经打开     
udp        0      0 127.0.1.1:53            0.0.0.0:*                          
udp        0      0 0.0.0.0:68              0.0.0.0:*                          
udp        0      0 0.0.0.0:631             0.0.0.0:*                          
udp        0      0 0.0.0.0:51842           0.0.0.0:*                          
udp        0      0 0.0.0.0:5353            0.0.0.0:*                          
udp6       0      0 :::59961                :::*                               
udp6       0      0 :::5353                 :::*                               
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ 


 netstat -nap |grep address number 查看程序运行的pid
如:
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ netstat -nap |grep 0.0.0.0 //代码中被动端地址 是0.0.0.0
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      -               
udp        0      0 0.0.0.0:1989            0.0.0.0:*                           2739/a.out    //进程IP 2739   
udp        0      0 127.0.1.1:53            0.0.0.0:*                           -               
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -               
udp        0      0 0.0.0.0:631             0.0.0.0:*                           -               
udp        0      0 0.0.0.0:51842           0.0.0.0:*                           -               
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           -               
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ kill 2739
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ netstat -nap |grep 0.0.0.0
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      -               
udp        0      0 127.0.1.1:53            0.0.0.0:*                           -               
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -               
udp        0      0 0.0.0.0:631             0.0.0.0:*                           -               
udp        0      0 0.0.0.0:51842           0.0.0.0:*                           -               
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           -               
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值