UDP协议的套接字
函数接口
发送数据接口
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
- sockdf:套接字的描述符
- buf:待发送的数据
- len:发送数据的长度
- flags:传入0表示阻塞发送
- dest_addr:目标主机的地址信息,封装在
struct sockaddr
结构体中 - addrlen:dest_addr表示的地址信息的长度
注意:
- UDP也是有发送缓冲区的,只是UDP协议的特点是面向数据报,他是整条信息直接发送的。
所以说当数据从应用层到达传输层的过程,也就是到发送缓冲区中,数据会被直接打上UDP协议的头部,然后直接提交到网络层,中间的阻塞时间可以说很短暂 - 阻塞发送,是指当发送缓冲区中有数据的时候,这个阶段如果再次调用sendto接口,会让数据发送的过程陷入阻塞等待。
直到上一条数据发送完毕,sendto接口才可以将数据发送到缓冲区中
接收数据接口
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
- sockfd:套接字描述符
- buf :把从接收缓冲区中拿到数据放到buf数组中
- len:buf数组的最大长度,也就是最大可以结束多少数据,在末尾需要预留一个’
\0'
的位置 - flags:0表示阻塞接收数据
- src_addr:源主机的地址信息,标识着这条信息从哪一个主机的哪一个进程来
- addrlen:src_addr地址信息的长度,是一个输入输出型参数,传的是长度的地址。
关闭套接字接口
#include <unistd.h>
int close(int fd);
因为socket函数的返回值也是一个文件描述符,所以就可以使用close接口来关闭。
UDP通信小程序
实现的功能
服务端 (server):
- 创建套接字
- 绑定地址信息
- 接收数据
- 发送数据
- 关闭套接字
客户端(client):
- 创建套接字
- 发送数据
- 接收数据
- 关闭套接字
流程
程序代码
udpClass.hpp,将重复的部分集中到一个类中
#pragma once
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
using std::cin;
using std::cout;
using std::endl;
using std::string;
class udpClass
{
public:
//构造函数、
udpClass()
{
_sock = -1;
}
//socket 创建套接字
bool CreateSocket()
{
//int socket(int domain, int type, int protocol);
//选择ipv4-->AF_INET 面向数据报--> SOCK_DGRAM UDP协议-->IPPROTO_UDP
_sock = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP);
if(_sock < 0)
{
//创建套接字失败
perror("socket error ");
return false;
}
return true;
}
// bind 绑定Ip地址 端口信息
bool Bind(string& ip,uint16_t port)
{
//int bind(int sockfd, const struct sockaddr *addr,
// socklen_t addrlen);
struct sockaddr_in addr;
addr.sin_family = AF_INET;//按照ipv4的格式进行解析
addr.sin_port = htons(port);//小端字节序转为大端字节序,16位
//inet_addr--> 点分十进制的ip转为uint32格式, 将主机字节序转为网络字节序
//ip.c_str --> string 类型转为 char* 类型
addr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = bind(_sock,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
if(ret < 0)
{
perror("bind error ");
return false;
}
return true;
}
//send 发送数据
bool Send(string& data, struct sockaddr_in* addr)
{
//ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
// const struct sockaddr *dest_addr, socklen_t addrlen);
int sendsize = sendto(_sock,data.c_str(),data.size(),0,
(struct sockaddr*)addr,sizeof(struct sockaddr_in));
if(sendsize < 0)
{
perror("send error ");
return false;
}
return true;
}
//recvfrom 接收数据
bool Recvfrom(string& buf,struct sockaddr_in* addr)
{
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
// struct sockaddr *src_addr, socklen_t *addrlen);
char data[1024] = {'\0'};
socklen_t addrlen = sizeof(struct sockaddr_in);
int recvsize = recvfrom(_sock,data,sizeof(data),0,
(struct sockaddr*)addr, &addrlen);
if(recvsize < 0)
{
perror("recvfrom error ");
return false;
}
//拷贝接收的数据i
//从data数组中截取前recvsize个字符
buf.assign(data,recvsize);
return true;
}
//close
void Close()
{
close(_sock);
_sock = -1;
}
private:
int _sock;//套接字描述符
};
- 注意:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
这种情况下,我们在用sizeof
计算sockaddr_in
结构体的大小的时候,不能直接对变量名使用sizeof,因为原变量的类型是指针类型,他的大小在32位机器下一直是4个字节,在64位机器下是8个字节。所以需要直接对原变量的结构体类型进行sizeof,这样才能求出准确的大小。
server.cpp ,服务端的程序
#include "udpClass.hpp"
int main(int argc, char* argv[])
{
//所需要的三个参数 ./server ip port
if(argc != 3)
{
cout<<"所输入的参数不正确,应该为:[./server] [ip] [port]"<<endl;
return 0;
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
udpClass udp;
//创建套接字
if(!udp.CreateSocket())
return 0;
//绑定ip和端口信息
if(!udp.Bind(ip,port))
return 0;
while(1)
{
//接收数据
string buf;
struct sockaddr_in cliaddr;//对端地址信息
udp.Recvfrom(buf,&cliaddr);
cout<<"client say : "<<buf<<endl;
//发送数据
cout<<"server respond : ";
fflush(stdout);//刷新缓冲区
cin>>buf;
udp.Send(buf,&cliaddr);
}
udp.Close();
return 0;
}
client.cpp ,客户端程序
#include "udpClass.hpp"
int main(int argc, char* argv[])
{
if(argc != 3)
{
//ip port 是服务端的ip port
cout<<"参数错误 [./client] [ip] [port]"<<endl;
return 0;
}
string ser_ip = argv[1];
uint16_t ser_port = atoi(argv[2]);
udpClass udp;
//创建套接字
if(!udp.CreateSocket())
return 0;
//服务端的地址信息
struct sockaddr_in ser_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(ser_port);
ser_addr.sin_addr.s_addr = inet_addr(ser_ip.c_str());
while(1)
{
//发送数据
cout<<"client say : ";
fflush(stdout);
string buf;
cin>>buf;
udp.Send(buf,&ser_addr);
//接收数据
struct sockaddr_in dataaddr;
udp.Recvfrom(buf,&dataaddr);
cout<<"server say : "<<buf<<endl;
}
udp.Close();
return 0;
}