前言
本章主要是对套接字网络编程的一个学习,目标是能够基本的进行套接字编程
一、UDP套接字
1、创建套接字
无论是服务端还是客户端,进行网络编程需要做的第一件事就是创建套接字
- socket函数函数原型:
int socket(int domain, int type, int protocol);
- 解释:
- domain:创建套接字的域或者叫做协议家族,也就是创建套接字的类型。填写
struct sockaddr
结构的前16位:本地通信设置为AF_UNIX
,网络通信设置为AF_INET
(IPv4)或AF_INET6
(IPv6) - type:套接字协议的传输类型:对于UDP的数据报式传输则填入
SOCK_DGRAM
,对于TCP的流式传输则填入SOCK_STREAM
- protocol:创建套接字的协议类别。可以指明为TCP或UDP,但该字段一般直接设置为0就可以了,即默认(会根据前两个参数自动推导)
- 返回值:套接字创建成功返回一个文件描述符,创建失败返回-1,同时错误码会被设置
- 示例:
//创建socket网络文件
int sock=socket(AF_INET,SOCK_DGRAM,0);//ipv4协议,数据报式套接(UDP),套接字协议(0:默认协议)
if(sock < 0)
{
std::cerr<<"socket"<<std::endl;
return 2;
}
std::cout<<"sock:"<<sock<<std::endl;
2、填写ip/port和绑定
对于服务端和客户端都要进行绑定ip及port,只有绑定后才能标识网络中唯一的主机中的进程服务,便于进程接下来的数据传输
struct sockaddr_in
成员:
sin_family:表示协议家族 sin_port:表示端口号,是一个16位的整数 sin_addr:表示IP地址,是一个32位的整数 sin_addr中的成员s_addr:表示IP地址,是一个32位的整数
- 注意:
- 对于服务端来说,服务端ip和port需要被多个客户端所熟知的,所以服务端的port是需要进行固定化的,也就是说一个服务端的port是该服务端所私有的,不能随意更换
- 对于云服务器上的服务端,不建议绑定明确的ip,建议使用INADDR_ANY绑定该主机所有设备,以此接收向该主机发送的所有数据
- 对于客户端来说,客户端是不提供服务的,ip和port不用被其他主机熟知,并且为了启动客户端的顺利(固定的port被占用会使得进程启动不了),所以不需要我们主动去进行绑定ip和port,当进行数据的发送时,系统会自动绑定ip以及随机的port
- 对于客户端虽然不用主动填写自己的ip和port,但是需要的是明确数据传输的主机中的进程,即需要填写服务端的ip和port
- IP格式转化:
- 对于进行绑定的网络信息字段是需要我们主动进行网络字节序的转化的,系统提供了相应的接口(上面介绍了),而发送的数据系统会在底层进行网络字节序的转化
- 在ip的转化时,我们习惯用的是点分十进制的字符串ip,例如192.168.233.123,但是需要填入的ip形式是四字节整数ip
- inet_addr函数的函数原型:
in_addr_t inet_addr(const char *cp);
- 解释:
- 功能:将点分十进制的字符串IP转换成四字节整数IP
- 传入待转换的字符串IP,该函数返回的就是转换后的整数IP
- inet_ntoa函数原型:
char *inet_ntoa(struct in_addr in);
- 解释:
- 将四字节整数IP转换成点分十进制字符串IP
- 传入inet_ntoa函数的参数类型是
in_addr
,不需要选中in_addr
结构当中的32位的成员传入,直接传入in_addr
结构体即可
注:上述函数在转化ip格式时同时也会自动进行网络字节序的转化
服务端创建套接字,即底层打开了对应的网络套接字文件,想进行网络通信还需要绑定对应的网络信息,即将套接字文件与网络进行强相关
- bind函数函数原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 解释:
- sockfd:绑定的文件的文件描述符。也就是我们创建套接字时获取到的文件描述符
- addr:网络相关的属性信息,包括协议家族、IP地址、端口号等
- addrlen:传入的addr结构体的长度
- 返回值说明:绑定成功返回0,绑定失败返回-1,同时错误码会被设置
- 注意:
- 在绑定时需要将网络相关的属性信息填充到struct sockaddr_in结构体当中,然后将该结构体地址作为bind函数的第二个参数进行传入(这里需要强转为struct sockaddr *addr类型)
- UDP是数据报式套接字,并不会管对端的接收转态,只要绑定后就可以向对端进行接收消息了,但是这样的传输实际中是存有风险的
- 示例:服务端
//创建套接字结构体-填入ip及port
struct sockaddr_in local;
memset(&local,0,sizeof(local));//初始化结构体
local.sin_family=AF_INET;//通信协议-ipv4
local.sin_port=htons(atoi(argv[1]));//使用命令行参数+网络字节序转化接口
local.sin_addr.s_addr=htons(INADDR_ANY);//云服务器不建议绑定明确的ip,建议使用INADDR_ANY绑定该主机所有设备
//将网络文件与套接字进行绑定(强相关)
if(bind(sock,(struct sockaddr*)&local,sizeof(local))==-1)
{
std::cout<<"bind"<<std::endl;
return 3;
}
- 示例:客户端
//填入目标套接字的信息-确定传输数据的对象
struct sockaddr_in desc;
memset(&desc,sizeof(desc),0);
desc.sin_family=AF_INET;//通信的ip协议-ipv4
desc.sin_port=htons(atoi(argv[2]));//字符串转整数+网络字节序转化
desc.sin_addr.s_addr=inet_addr(argv[1]);//点分十进制字符串ip转四字节整数ip-自动转化为网络字节序
//客户端不用主动绑定ip和port,当向远端发送消息是会自动绑定-服务端才需要固定的ip及port
3、数据发送和接收
- 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:对端网络相关的属性信息,包括协议家族、IP地址、端口号等
- addrlen:传入dest_addr结构体的长度
- 返回值:入成功返回实际写入的字节数,写入失败返回-1,同时错误码会被设置
注:由于UDP不是面向连接的,所以传输数据时需要指明对端网络相关的信息,即