通信流程:
Server:
1.创建套接字 — 在内核中创建一个socket结构体
2.为套接字绑定地址信息
在创建套接字创建的socket结构体中加入IP+PORT信息
1.告诉操作系统主机收到的哪些数据应该交给当前的这个socket
2.确定发送数据的源端地址信息
3.接收数据
当前进程从指定的socket接收缓冲区中取出数据
4.发送数据
将要发送的数据放到socket发送缓存区中,内核选择合适时候封装发送
5.关闭套接字
Client:
1.创建套接字
2.为套接字绑定地址信息 (大多数情况下会忽略第二步,在发送数据时,若socket没有绑定地址,则系统会选择合适的地址进行绑定)
3. 发送数据
4. 接收数据
5.关闭套接字
udp_srv.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h> //字节序转换接口头文件
#include<netinet/in.h> //地址结构头,协议类型文件
#include<sys/socket.h> //套接字头文件
int main(int argc,char *argv[])
{
//1.创建套接字---在内核中创建一个socket结构体
//int socket(地址类型,套接字类型,协议类型)
int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sockfd<0){
perror("socket error\n");
return -1;
}
//2.为套接字绑定地址信息
//int bind(操作句柄,当前地址信息,地址信息长度)
struct sockaddr_in addr; //定义IPv4d的地址结构
addr.sin_family=AF_INET; //定义地址类型为IPv4
addr.sin_port=htons(9000); //设置地址端口
addr.sin_addr.s_addr=inet_addr("172.18.0.4 "); //将点分10进制主机字节序转换为网络字节序
int len=sizeof(addr);
int ret=bind(sockfd,(struct sockaddr*)&addr,len);
if(ret<0){
perror("bind error\n");
return -1;
}
while(1){
//3.接收数据
//recvfrom(操作句柄,空间地址,数据长度,标志,对端地址,对端地址长度)
char buf[1024] = {0};
struct sockaddr_in paddr; //定义对端地址信息结构体
int len = sizeof(struct sockaddr_in);
ret = recvfrom(sockfd, buf, 1023, 0,
(struct sockaddr*)&paddr, &len);
if (ret < 0) {
perror("recvfrom error");
return -1;
}
uint16_t cport = ntohs(paddr.sin_port);
char *cip = inet_ntoa(paddr.sin_addr);
printf("client-[%s:%d] say: %s\n", cip, cport, buf);
//4. 回复数据
memset(buf, 0x00, 1024);//初始化内存空间
printf("server say: ");
fflush(stdout);
fgets(buf, 1023, stdin);
//ssize_t sendto()
ret = sendto(sockfd, buf, strlen(buf), 0,
(struct sockaddr*)&paddr, len);
if (ret < 0 ) {
perror("sendto error");
return -1;
}
}
//5.关闭套接字
close(sockfd);
return 0;
}
updsocket.hpp
/*
* 封装实现一个udpsocket类
* 通过实例化的对象调用对应的成员接口
* 可以实现udp客户端或服务端的搭建
*/
#include<cstdio>
#include<iostream>
#include<string>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
class UdpSocket{
private:
int _sockfd; //套接字描述符
public:
//构造函数
UdpSocket()
:_sockfd(-1){}
//1.创建套接字
bool Socket(){
//int socket(地址域类型,套接字类型,通信协议)
_sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);//创建socket
if(_sockfd<0){
perror("socket error");
return false;
}
return true;
}
//2.为套接字绑定地址信息
bool Bind(std::string &ip,uint16_t port){
struct sockaddr_in addr; //定义地址信息结构体
addr.sin_family=AF_INET; //地址域类型IPv4
addr.sin_port=htons(port); //主机字节序端口号转换为网络字节序端口号
addr.sin_addr.s_addr=inet_addr(ip.c_str()); //将字符串点分十进制IP地址转换为整型网络字节序IP地址
socklen_t len=sizeof(struct sockaddr_in);
int ret;
//int bind(套接字操作句柄,地址信息,地址信息长度)
ret=bind(_sockfd,(struct sockaddr*)&addr,len);
if(ret<0){
perror("bind error");
return false;
}
return true;
}
//3.接收数据
bool Recv(std::string *buf,std::string *ip=NULL,int *port=NULL){
struct sockaddr_in addr;
socklen_t len=sizeof(struct sockaddr_in);
char tmp[4096]={0};
//recvfrom(操作句柄,发送空间首地址,发送数据长度,标识符,对端地址信息,地址结构长度)
int ret=recvfrom(_sockfd,tmp,4096,0,(sockaddr*)&addr,&len);
if(ret<0){
perror("recvfrom error");
return false;
}
buf->assign(tmp,ret); //自带申请空间拷贝数据
if(ip!=NULL){
*ip=inet_ntoa(addr.sin_addr);//将网络字节序IP地址转换为字节串点分十进制IP地址
}
if(port!=NULL){
*port=ntohs(addr.sin_port); //将网络端口号转换为主机端口号
}
return true;
}
//4.发送数据
bool Send(std::string &data,const std::string &ip,const int port){
struct sockaddr_in addr;
addr.sin_family=AF_INET; //地址域类型为IPv4
addr.sin_port=htons(port); //主机端口号转换为网络端口号
addr.sin_addr.s_addr=inet_addr(ip.c_str()); //将网络字节序IP地址转换为字节序串点分十进制IP地址
socklen_t len=sizeof(struct sockaddr_in);
//ssize_t snedto(操作句柄,空间首地址,发送数据长度,标识符,对端地址信息,地址结构长度)
int ret=sendto(_sockfd,data.c_str(),data.size(),0,(sockaddr*)&addr,len);
if(ret<0){
perror("sendto error");
return false;
}
return true;
}
//5.关闭套接字
bool Close(){
if(_sockfd!=-1){
close(_sockfd);
}
return true;
}
};
udp_cli.cpp
#include"udpsocket.hpp"
#define CHECK_RET(q) if((q)==false){return -1;}
int main(){
UdpSocket sock;
//1.创建套接字
CHECK_RET(sock.Socket());
//2.绑定地址信息(不推荐)
while(1){
//3.发送数据
std::cout<<"client say: ";
std::string buf;
std::cin>>buf;
CHECK_RET(sock.Send(buf,"172.17.0.4",9000));
//4.接收数据
buf.clear();
CHECK_RET(sock.Recv(&buf));
std::cout<<"server say:"<< buf<< "\n";
}
//5.关闭套接字
sock.Close();
return 0;
}