相关接口
cpp
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
这个是套接字函数,套接字实际上就是IP+端口,IP用于标识主机,端口用于标识主机上面的进程
返回值:成功返回一个文件描述符,失败返回-1
domain:一般为AF_INET,表示网络通信
type:取值一般为SOCK_STREAM和SOCK_DGRAM,分别表示TCP和UDP,字面意思,引文tcp面向的是字节流,UDP是数据报,字节流就像水流一样,数据想取多少就取多少,数据报类似于快递,发来的是一整块数据
protocol:一般为0,然系统自动匹配默认协议
cpp
struct sockaddr_in {
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
};
这个表示IPV4,其中用
sin_family:协议家族,一般设置成为AF_INET,表示要进行网络通信
sin_port:端口号
sin_addr:ip号
这个通常和bind函数一起使用,用来绑定自己的设置自己的IP号和端口号,但是使用的时候注意,当在设置的时候需要将端口号转换成为网络字节序,网络字节序是大端,我们自己的IP地址为string类型的,使用的时候也需要将其转化整数,主要用到下面的函数
cpp
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong); //主机转网络,且转为long long
uint16_t htons(uint16_t hostshort); //主机转网络,且转为short
uint32_t ntohl(uint32_t netlong); //网络转主机,且转为long long
uint16_t ntohs(uint16_t netshort); //网络转主机,且为short
上面的函数非常好记忆:
比如:htonl 是 host to net long long 的缩写
然后就是IP地址的转化
cpp
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
成功返回合法Ip,失败返回0
比如:将字符传的IP转化成为网络序号Ip存到addr中
iner_aton("127.0.0.1",&addr.sin_addr);
in_addr_t inet_addr(const char *cp);//传入字符串的IP,返回网络序列的IP
in_addr_t inet_network(const char *cp);
将字符串的IP转化成为主机序列的IP,所以后又需要化成为网络序列
下面介绍bind
cpp
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
这个就是将我们设置好的IP和端口号绑定到操作系统,成功返回0,失败返回-1
ssokfd:通过调用socket函数返回的文件描述符
addr:就是我们的sockaddr_in,使用的时候需要强转为const struct addr
addlen:addr的大小
接受消息的函数
cpp
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void buf[restrict .len], size_t len,
int flags,
struct sockaddr *_Nullable restrict src_addr,
socklen_t *_Nullable restrict addrlen);
成功:返回实际收到的信息的长度,失败:返回-1
sockfd:自己通过sockfd函数创建的
buf:输出型的参数,收到的消息放到这里面
len:buf的长度
flags:为0,表示阻塞
src_addr:输出型的参数,表示从那个主机收到的信息
srclen:src_addr的长度
发送消息的函数
cpp
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void buf[.len], size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:自己通过sockfd函数创建的
buf:输出型的参数,发送的消息放到这里面
len:buf的长度
flags:为0,表示阻塞
src_addr:输出型的参数,表示想发给哪个主机
srclen:src_addr的长度
代码
服务端
hpp文件
cpp
#pragma once
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "SockAddr.hpp"
uint16_t DefaultPort = 8080;
std::string DefaultIp = "127.0.0.1";
class Udp_Server
{
public:
Udp_Server(const uint16_t Port = DefaultPort, const std::string Ip = DefaultIp)
:_Port(Port)
,_Ip(Ip)
,_IsRuning(false)
{
}
void Init()
{
_SockFd = socket(AF_INET,SOCK_DGRAM,0); //初始化sockfd
if(_SockFd < 0)
{
std::cout << "sockrt error:" << strerror(errno) << std::endl;
exit(1);
}
std::cout << "socket create success" << std::endl;
//绑定
sockaddr_in Local;
bzero(&Local,sizeof(Local)); //先将里面的字段全部清零,保险
Local.sin_family = AF_INET; //类型必须和上面socket一样才能绑定成功
Local.sin_port = htons(_Port); //转成网络字节流
Local.sin_addr.s_addr = inet_addr(_Ip.c_str());
Local.sin_addr.s_addr = INADDR_ANY;
int n = bind(_SockFd,(const struct sockaddr*)&Local,sizeof(Local));//绑定到操作系统
if(n < 0)
{
std::cout << "bind error:" << strerror(errno) << std::endl;
exit(2);
}
std::cout << "bind sucess" << std::endl;
}
void Start()
{
_IsRuning = true;
while(true)
{
char Buffer[1024];
sockaddr_in src;
socklen_t len = sizeof(src);
int n = recvfrom(_SockFd,Buffer,sizeof(Buffer)-1,0,(sockaddr*)&src,&len); //接受消息
if(n > 0)
{
Buffer[n] = 0;
std::cout << "From Client: " << Buffer << std::endl; //接受成功发送消息
std::string SendMessage = "return#: ";
SendMessage += Buffer;
socklen_t Size = sendto(_SockFd,SendMessage.c_str(),SendMessage.size(),0,(sockaddr*)&src,sizeof(src));
}
}
}
private:
int _SockFd; //套接字文件描述符号
uint16_t _Port; //端口号
std::string _Ip; //ip地址
bool _IsRuning; //是否运行
};
测试文件
cpp
#include <memory>
#include "Udp_Server.hpp"
int main()
{
std::unique_ptr<Udp_Server> Server = std::make_unique<Udp_Server>();
Server->Init();
Server->Start();
return 0;
}
客户端
cpp
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Udp_Client.hpp"
#include "SockAddr.hpp"
int main(int argc,char *argv[])
{
if(argc!=3)
{
std::cout << "Usage:./.exe Ip port" << std::endl;
exit(1);
}
std::string Ip = argv[1];
int Port = std::stoi(argv[2]);
int SocketFd = socket(AF_INET,SOCK_DGRAM,0);
if(SocketFd < 0)
{
std::cout << "ScoketFd error" << std::endl;
exit(2);
}
//SockAddr SAddr(Ip,Port);
sockaddr_in SAddr;
SAddr.sin_family = AF_INET;
SAddr.sin_addr.s_addr = inet_addr(Ip.c_str());
SAddr.sin_port = htons(Port);
while(true)
{
std::cout << "Please send#";
std::string SendMessage;
std::getline(std::cin,SendMessage);
ssize_t n = sendto(SocketFd,SendMessage.c_str(),SendMessage.size(),0,(const struct sockaddr*)&SAddr,sizeof(SAddr));
char Buffer[1024];
sockaddr_in src;
socklen_t len = sizeof(src);
ssize_t Size = recvfrom(SocketFd,Buffer,sizeof(Buffer)-1,0,(sockaddr*)&src,&len);
if(Size > 0)
{
Buffer[Size] = 0;
std::cout << Buffer << std::endl;
}
}
return 0;
}
运行结果
