[Linux] 基于UDP协议的socket网络套接字编程详解


在这里插入图片描述


服务端

  1. 创建套接字
    (通过套接字使进程与网卡建立联系。在内核中创建了struct_socket结构体,其中包含了实质为缓冲区的发送队列与接收队列,每个端口都有自己独立的一个缓冲区用来保存等待发送的数据包
  2. 为套接字绑定地址信息。(ip + port
  3. 接收数据
    (服务端一定不会先发送数据,因为不知道客户端地址)
  4. 发送数据
  5. 关闭套接字

缓冲区讲解】:
客户端发送数据到服务端,根据冯诺依曼体系结构,网卡拿到数据,数据处理之前要放入内存(缓冲区)。那么多个端口数据都到达网卡(mysql数据、ssh数据等)。
此时如果是一个缓冲区,那么其中会有很多繁杂的数据,如果再去读取数据就要进行筛选,速度性能就下降了。
此时这里存在空间换时间的思想,每创建一个socket都会有一个对应的缓冲区。为socket绑定地址信息的过程,实际上就是为socket标记,标记的是其存放的哪个端口的数据。当网卡接受到数据,根据这个标记放到相应端口的缓冲区。取出数据处理时,每个端口也是只取出自己端口的数据。


客户端

  1. 创建套接字
  2. 为套接字绑定地址信息
    (通常客户端不推荐用户手动绑定地址信息,因为有可能此时端口被占用,一般在下一步选择地址与端口,但是手动绑定也是可以的~)
  3. 发送数据
    (如果用户socket还没有绑定地址,此时操作系统会选择一个合适的地址端口进行绑定)
  4. 接收数据
  5. 关闭套接字

常见端口号介绍:

  • ssh:22
  • mysql:3306
  • http:80
  • https:443

接口介绍

创建套接字:socket()

int socket(int domain, int type, int protocol);
  • domain:地址域。
    选项:
 	AF_LOCAL   		Local communication(本地地址域)             		  unix(7)
 	AF_INET 		IPv4 Internet protocols(IPV4 网络协议地址域)        ip(7)
 	AF_INET6        IPv6 Internet protocols(IPV6 网络协议地址域)        ipv6(7)
 	AF_PACKET       Low level packet interface(低级包接口(原始数据包))   packet(7)
...
  • type:套接字类型。
    选项:
 	SOCK_STREAM   	流式套接字(二进制数据流)	默认协议TCP,不可以支持UDPSOCK_DGRAM 		数据报套接字				默认协议UDP,不可以支持TCP...
  • protocol:协议类型。
    选项:
	0		使用套接字默认协议
	6 / IPPROTO_TCP() 	tcp协议
	17 / IPPROTO_UDP()	udp协议
...
  • 返回值:套接字操作句柄(文件描述符)。
    失败返回-1

绑定地址:bind()

int bind(
	int sockfd,
	struct sockaddr *my_addr, 
	socklen_t addrlen
);
  • sockfd:创建套接字返回的文件描述符。
  • my_addr:地址信息,他是一个(struct sockaddr *)类型的结构数据。给定这么一个结构体是为了通用,既可以使用IPV4方式,也可以使用IPV6方式存储数据。
  • addrlen:地址信息长度。
  • 返回值:成功返回0,失败返回-1

[注] sockaddr结构体

需要包含头文件:< netinet/in.h >

sockaddr结构体的定义如下:

struct sockaddr {
	unsigned short sa_family;		 /* address family, AF_xxx */
	char sa_data[14];				 /* 14 bytes of protocol address */
};

说明:
sa_family :是2字节的地址家族,地址域。一般都是“AF_xxx”的形式,它的值包括三种:AF_INETAF_INET6AF_UNSPEC只需要取出前两个字节,就可以知道到底是IPV4的地址还是IPV6的地址,从而判定到底通过sockaddr_in还是sockaddr_in6进行解析~

  • 如果指定AF_INET,那么函数就不能返回任何IPV6相关的地址信息;
  • 如果仅指定了AF_INET6,则就不能返回任何IPV4地址信息。
  • AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。如果某个主机既有AAAA记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA记录将作为sockaddr_in6结构返回,而A记录则作为sockaddr_in结构返回
  • 通常用的都是AF_INET

sockaddr_in结构体的定义如下:

struct sockaddr_in {
  short int sin_family; 			/* Address family */
  unsigned short int sin_port; 	/* Port number */
  struct in_addr sin_addr; 		/* Internet address */
  unsigned char sin_zero[8];	    /* Same size as struct sockaddr */
};
  • sin_family:指代协议族,在socket编程中只能是AF_INET
  • sin_port:存储端口号(使用网络字节顺序)。
  • sin_addr:存储IP地址,使用in_addr这个数据结构。
  • sin_zero:是为了让sockaddrsockaddr_in两个数据结构保持大小相同而保留的空字节。

而其中in_addr结构的定义如下:

typedef struct in_addr {
  union {
      struct{ unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b;
      struct{ unsigned short s_w1, s_w2;} S_un_w;
      unsigned long S_addr;
  } S_un;
} IN_ADDR;

阐述下in_addr的含义,很显然它是一个存储ip地址的共用体有三种表达方式:

  1. 第一种用四个字节来表示IP地址的四个数字(点分十进制);
  2. 第二种用两个双字节来表示IP地址;
  3. 第三种用一个长整型来表示IP地址。

in_addr赋值的一种最简单方法是使用inet_addr()函数,它可以把一个代表IP地址的字符串赋值转换为in_addr类型,如addrto.sin_addr.s_addr=inet_addr("192.168.0.2");
其反函数是inet_ntoa(),可以把一个in_addr类型转换为一个字符串


接收数据:recvfrom()

ssize_t recvfrom(
	int sockfd,
	void *buf, 
	size_t len,
	int flags,
	struct sockaddr *src_addr,
	socklen_t *addrlen
);
  • sockfd:操作句柄,套接字描述符。
  • buf:缓冲区,想要接收的数据。
  • len:想要接收的数据长度。
  • flags:0(默认阻塞接收):没有数据接收就等待,直到有数据再读取。
  • src_addr发送端的地址信息。
  • addrlen:地址信息长度。(输入输出型参数)
    (不但要指定想要接受的数据长度,还要保存实际接收数据的长度)
  • 返回值:实际接收到的数据长度。
    失败返回-1

发送数据:sendto()

ssize_t sendto(
	int sockfd, 
	const void *buf, 
	size_t len, 
	int flags,
	const struct sockaddr *dest_addr, 
	socklen_t addrlen
);
  • sockfd:操作句柄,套接字描述符。
  • buf:缓冲区,想要发送的数据。
  • len:想要发送的数据长度。
  • flags:0(默认阻塞接收)。
  • dest_addr目的端的地址信息。
  • addrlen:地址信息长度。
  • 返回值:实际接受到的数据长度
    失败返回-1

关闭套接字:close()

int close(int fd);

close()接口为:关闭文件描述符。
所以socket在内核创建结构、与网卡创建关联等,最终返回的是一个文件描述符。(操作句柄)


用C++封装一个UDP的socket类,实现客户端与服务端程序的udp通信

https://blog.csdn.net/qq_42351880/article/details/90302109

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

giturtle

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

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

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

打赏作者

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

抵扣说明:

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

余额充值