博客
关于我
Linux系统编程67 网络编程1 - 报式套接字传输
阅读量:337 次
发布时间:2019-03-04

本文共 11049 字,大约阅读时间需要 36 分钟。

需要掌握:

socket(),获取SOCKETbind(), 绑定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 取得SOCKET2 给SOCKET取得地址(可省略)3 发/收消息:sendto()4 关闭SOCKET

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

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


socket()

NAME创建通信端点,取得SOCKET       socket - create an endpoint for communicationSYNOPSIS       #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 APItype:SOCK_STREAM     流式套接字,Provides sequenced, reliable, two-way, connection-based byte streams.有序可靠:只要接收方能够接到数据,就可以保证 当前数据包中的内容是正确的。不能保证不丢包,网络传输中会有丢包双工基于连接:点对点,一对一字节流传输       SOCK_DGRAM      报式数据分组无连接的不可靠的SOCK_SEQPACKET 有序可靠的报式有序可靠的报式...

bind()

NAME给SOCKET绑定一个地址       bind - bind a name to a socketSYNOPSIS       #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 formSYNOPSIS       #include <arpa/inet.h>/*af : 协议族,只能是 AF_INET or AF_INET6,即IPV4或者IPV6src: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 socketSYNOPSIS       #include <sys/types.h>       #include <sys/socket.h>/* 用于流式套接字sockfd :目标SOCKETbuf :接收信息存储地址len:接收信息存储空间 大小 flags :特殊要求*/   ssize_t recv(int sockfd, void *buf, size_t len, int flags);/* recvfrom 用于报式套接字sockfd :目标SOCKETbuf :接收信息存储地址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 formSYNOPSIS       #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 socketSYNOPSIS       #include <sys/types.h>       #include <sys/socket.h>/* 用于流式套接字 发送数据sockfd:目标SOCKETbuf :发送数据信息存储地址len:发送数据信息存储空间 大小 */   ssize_t send(int sockfd, const void *buf, size_t len, int flags);/*  用于报式套接字 发送数据sockfd:目标SOCKETbuf :发送数据信息存储地址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 40struct 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 -anuActive 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 2739mhr@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$ 

转载地址:http://hxme.baihongyu.com/

你可能感兴趣的文章
窄带随机过程的产生
查看>>
随机四则运算
查看>>
Maven
查看>>
zookeeper使用
查看>>
Java入门常见错误总结
查看>>
Java重载overload
查看>>
Java面向对象
查看>>
JAVA带标签的break和continue
查看>>
Java_File类的基本用法
查看>>
Java获取线程基本信息的方法
查看>>
JavaWeb用户信息管理系统-创建登录业的务持久层
查看>>
SpringIoC和DI注解开发
查看>>
Mybatis快速入门
查看>>
Java类和对象
查看>>
Java集合Collection
查看>>
Java基础知识日积月累(Tip of the Day08)
查看>>
SpringMVC入门-概述和基本配置
查看>>
SpringBoot快速入门
查看>>
医疗管理系统-手机快速登录和SpringSecurity权限控制
查看>>
SpringCloud微服务简介
查看>>