socket编程-UDP
socket主要类型
流套接字(SOCK_STREAM)
流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复送,并按顺序接收。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP协议。
数据报套接字(SOCK_DGRAM)
数据报套接字提供一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在应用层中做相应的处理。
原始套接字(SOCK_RAW)
原始套接字与标准套接字(标准套接字指的是前面介绍的流套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送的数据必须使用原始套接。
实际应用中,数据报socket的应用场景比较少
流套接字(SOCK_STREAM)
socket函数:socket函数用于创建一个新的socket,也就是向系统申请一个socket资源,socket函数用户客户端和服务端。socket()函数的返回值其本质是一个文件描述符,是一个整数。一般情况下,socket创建都会成功,除非系统资源耗尽,socket函数一般不会返回失败(-1)。其函数声明如下:
int socket(int domain, int type, int protocol);
-
domain
协议域,又称协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址 ,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。 -
type
指定socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式socket(SOCK_STREAM)是一种面向连接的socket,针对于面向连接的TCP服务应用。数据报式socket(SOCK_DGRAM)是一种无连接的socket,对应于无连接的UDP服务应用。 -
protocol
指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
示例代码如下:
//1.创建套接字
int ServerSockFd;
if((ServerSockFd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==-1)//socket创建失败
{
perror("socket 创建失败");
return -1;
}
sockaddr结构体与sockaddr_in结构体:
sockaddr在头文件#include <sys/socket.h>中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了,如下:
struct sockaddr {
sa_family_t sin_family;//地址族
char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息
};
sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr)
- __SOCKADDR_COMMON_SIZE
- sizeof (in_port_t)
- sizeof (struct in_addr)];
};
- 二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
- sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。
- sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。
两个函数 htons() 和 inet_addr()。
- htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)
- inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。
- inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。
- htonl()将u_long从主机转换为 TCP/IP 网络字节顺序 (,这是大端)
- 已服务端为例进行演示
//2.进行绑定,指定用于通信的ip地址和端口
struct sockaddr_in ServerAddr;//服务端地址信息数据结构
//将协议簇指定为ipv4
ServerAddr.sin_family=AF_INET;
//服务端程序绑定本主机的任意IP地址
ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);
服务端程序绑定指定ip
//ServerAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
ServerAddr.sin_port=htons(9000);
bind函数:服务端把用于通信的地址和端口绑定到socket上。如果绑定的地址错误,或端口已被占用,bind函数一定会报错,否则一般不会返回错误。如无错误发生,则bind()返回0。否则的话,将返回-1。其函数声明如下:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
- 参数sockfd,需要绑定的socket,是一个文件描述符。
- 参数addr,存放了服务端用于通信的地址和端口,用结构体sockaddr_in强制转换得到。
- 参数addrlen,表示addr结构体的大小。
- 返回值:成功则返回0,失败返回-1,错误原因存于errno 中。
示例代码如下:
if(bind(ServerSockFd,(sockaddr*)&ServerAddr,sizeof(ServerAddr))!=0)
{
perror("bind failed");
close(ServerSockFd);
return -1;
}
sendto函数:指向一指定目的地发送数据,SendTo()适用于发送未建立连接的UDP数据包 (参数为SOCK_DGRAM)。如无错误发生,则返回发送数据的字符数。否则的话,将返回-1。其函数声明如下:
ssize_t sendto(int sockfd,const void *buf,
size_t nbytes,int flags,
const struct sockaddr *to,
socklen_t addrlen);
- 参数sockfd,需要绑定的socket,是一个文件描述符。
- buf: 发送数据缓冲区
- nbytes: 发送数据缓冲区的大小
- flags:一般为0
- to: 指向目的主机地址结构体的指针
- addrlen:to所指向内容的长度
示例代码如下:
if((ret=sendto(ClientSockFd, sen, sizeof(sen), 0, (sockaddr*)&ServerAddr, sizeof(ServerAddr)))<=0)
{
std::cout<<"send ret="<<ret<<std::endl;
break;
}
std::cout<<"客户端发送:"<<rec<<std::endl;
std::cout<<"send ret="<<ret<<std::endl;
recvfrom 函数:接收数据报,并存储源地址。如无错误发生,则返回接收到的字符数。否则的话,将返回-1。其函数声明如下:
ssize_t recvfrom(int sockfd, void *buf,
size_t nbytes,int flags,
struct sockaddr *from,
socklen_t *addrlen);
- 参数sockfd,需要绑定的socket,是一个文件描述符。
- buf: 发送数据缓冲区
- nbytes: 发送数据缓冲区的大小
- flags:一般为0
- from: 源地址结构体指针,用来保存数据的来源
- addrlen: from所指内容的长度
示例代码如下:
//recv接收函数,失败返回-1
if((ret=recvfrom(ClientSockFd, rec, sizeof(rec), 0, (sockaddr*)&ClientAddr, (socklen_t*)&len))<=0)
{
std::cout<<"recv ret="<<ret<<std::endl;
break;
}
std::cout<<"客户端接收:"<<rec<<std::endl;
std::cout<<"recv ret="<<ret<<std::endl;
最后完整简易服务端代码:https://blog.csdn.net/weixin_45256307/article/details/129458811
最后完整简易客户端代码:https://blog.csdn.net/weixin_45256307/article/details/129458814