简单的回显服务器和客户端代码
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
参数说明:
int domain:指定协议族
AF_INET: IPv4 协议
AF_INET6: IPv6 协议
AF_UNIX: 本地通信(也称为 UNIX 域套接字)

int type:指定套接字的类型
SOCK_STREAM: 提供可靠的、面向连接的字节流(TCP)
SOCK_DGRAM: 提供不可靠的、无连接的数据报(UDP)
SOCK_RAW: 提供原始套接字,允许直接访问网络层(通常用于网络监测或自定义协议)

int protocol:指定所需的协议

在UDP通信中,将前两个参数设置好之后,最后一个参数设置成0即可。
任何一个UDP服务通信中,都需要有一个int sockfd的文件描述符,按照系统编程中所说,这里打印出来的文件描述符应该是3,因为0,1,2已经被占用了。
创建套接字代码:
void InitServer()
{
//1.创建套接字
_sockfd=::socket(AF_INET,SOCK_DGRAM,0); //调用系统级的方法
if(_sockfd<0)
{
//通信不可能实现,直接退出
LOG(FATAL,"socket error\n");
exit(SOCKET_ERROR);
}
LOG(DEBUG,"socket creat success, _sockfd:%d\n",_sockfd); //_socked=3
}
网络通信中,客户端和服务器需要有自己的IP地址和端口号,因此需要将套接字和IP地址、端口号绑定。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); void InitServer()
{
//1.创建套接字(文件)
_sockfd=::socket(AF_INET,SOCK_DGRAM,0); //调用系统级的方法
if(_sockfd<0)
{
//通信不可能实现,直接退出
LOG(FATAL,"socket error\n");
exit(SOCKET_ERROR);
}
LOG(DEBUG,"socket creat success, _sockfd:%d\n",_sockfd); //_socked=3
//2.bind
//(1)先填充本地信息
struct sockaddr_in local;
memset(&local,0,sizeof(local));
local.sin_family=AF_INET;
local.sin_port=htons(_localport); //端口号需要从主机转换成网络序列
local.sin_addr.s_addr=inet_addr(_localip.c_str()); //用户习惯的是字符串,比如"192.xxx.xxx.xxx"
//但是网络中需要4字节ip,需要的是网络序列ip
//也就是说这里需要将字符串转换成4字节和网络序列
//(2)绑定
int n=::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
if(n<0)
{
//绑定失败,不会网络通信
LOG(FATAL,"bind error\n");
exit(BAND_ERROR);
}
//绑定成功
LOG(DEBUG,"socket bind success\n");
}
struct sockaddr_in local;结构体用于存储本地地址信息,该对象中有四个字段,如下:

需要对前三个字段进行设置,sin_family 的值和 socket 函数中的 domain 参数保持一致;sin_por是端口信息,由于是在网络中通信,需要将主机转换成网络序列; local.sin_addr.s_addr=inet_addr(_localip.c_str())是将ip地址从主机序列转换成网络序列,但是ip地址用户习惯于字符串形式,即“192.xxx.xxx.xxx”,需要转换成4字节,这里直接使用inet_addr()函数即可。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);参数解释:
sockfd套接字描述符buf: 指向存储接收到数据的缓冲区的指针len: 要接收的字节数,表示缓冲区的大小flags: 接收选项的标志src_addr: 可选参数,指向 sockaddr 结构体的指针,用于存储发送方的地址信息。如果不需要该信息,可以传入 NULLaddrlen: 可选参数,指向一个 socklen_t 类型的变量,表示 src_addr 指向的结构的大小。调用后,该变量将被更新为实际的地址长度。#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);dest_addr:指向目标地址的指针,通常是 sockaddr 结构体的指针,表示数据将要发送到的地址。如果目标是 UDP 套接字,必须指定目标地址。addrlen:指向一个 socklen_t 类型的变量,表示 dest_addr 指向的结构的大小。这个参数在调用时需要正确设置,调用后该变量会被更新为实际的地址长度。void Start()
{
_isrunning=true;
char inbuffer[1024];
while (_isrunning)
{
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
ssize_t n=recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&peer,&len);
if(n>0)
{
inbuffer[n]=0;
std::string echo_string="[udp_server echo] #";
echo_string+=inbuffer;
sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,(struct sockaddr*)&peer,len);
}
}
}云服务器上禁止绑定自己的公网:

可以绑定内网,但是都不到信息,因为不会在公网公布:

在云服务上,绑定IP地址一般绑定为0,这样云服务器绑定了任意IP:

服务器端进程任意IP地址绑定:
local.sin_addr.s_addr=INADDR_ANY;
和服务器有所不同, 客户端的进程很多,但是端口号只能和一个进程绑定,可能出现两个进程绑定同一个端口号,会出现冲突无法运行。为了解决这一问题,客户端的端口号一般不让用户设定,而是让客户端操作所在的操作系统随机选择一个端口号。客户端的端口号具体是多少不重要,只要能标记和别的进程不一样即可。
客户端需要绑定自己的IP地址和端口,但是不需要显示绑定自己的IP地址和端口。客户端在首次向服务器发送数据的时候,系统会自动给客户端绑定它自己的IP和端口。
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// 客户端需要先知道服务器ip地址和端口号
int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;
exit(0);
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
std::cerr << "create socket error" << std::endl;
exit(1);
}
// client 不需要显示绑定自己的IP和端口,但是需要绑定自己的IP和端口
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
server.sin_addr.s_addr = inet_addr(serverip.c_str());
while (1)
{
std::string line;
std::cout << "Please Enter# ";
std::getline(std::cin, line);
int n = sendto(sockfd, line.c_str(), line.size(), 0, (sockaddr *)&server, sizeof(server));
if (n > 0)
{
struct sockaddr_in temp;
socklen_t len = sizeof(temp);
char buffer[1024];
int m = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&temp, &len);
if (m > 0)
{
buffer[m] = 0;
std::cout << buffer << std::endl;
}
else
{
break;
}
}
else
{
break;
}
}
::close(sockfd);
return 0;
}UdpServer.hpp
#pragma once
#include<iostream>
#include<cstring>
#include<unistd.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"nocopy.hpp"
#include"Log.hpp"
using namespace log_ns;
static const int gsockfd=-1;
static const uint16_t glocalport=8888;
enum
{
SOCKET_ERROR=1,
BAND_ERROR
};
class UdpServer:public nocopy
{
public:
UdpServer(uint16_t localport=glocalport)
:_sockfd(gsockfd)
,_localport(localport)
,_isrunning(false)
{}
void InitServer()
{
//1.创建套接字(文件)
_sockfd=::socket(AF_INET,SOCK_DGRAM,0); //调用系统级的方法
if(_sockfd<0)
{
//通信不可能实现,直接退出
LOG(FATAL,"socket error\n");
exit(SOCKET_ERROR);
}
LOG(DEBUG,"socket creat success, _sockfd:%d\n",_sockfd); //_socked=3
//2.bind
//(1)先填充本地信息
struct sockaddr_in local;
memset(&local,0,sizeof(local));
local.sin_family=AF_INET;
local.sin_port=htons(_localport); //端口号需要从主机转换成网络序列
/*
local.sin_addr.s_addr=inet_addr(_localip.c_str()); //用户习惯的是字符串,比如"192.xxx.xxx.xxx"
//但是网络中需要4字节ip,需要的是网络序列ip
//也就是说这里需要将字符串转换成4字节和网络序列
*/
local.sin_addr.s_addr=INADDR_ANY;
//(2)绑定
int n=::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
if(n<0)
{
//绑定失败,不会网络通信
LOG(FATAL,"bind error\n");
exit(BAND_ERROR);
}
//绑定成功
LOG(DEBUG,"socket bind success\n");
}
void Start()
{
_isrunning=true;
char inbuffer[1024];
while (_isrunning)
{
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
ssize_t n=recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&peer,&len);
if(n>0)
{
inbuffer[n]=0;
std::cout<<"client say# "<<inbuffer<<std::endl;
std::string echo_string="[udp_server echo] #";
echo_string+=inbuffer;
sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,(struct sockaddr*)&peer,len);
}
else
{
std::cout<<"recvfrom : error "<<std::endl;
}
}
}
~UdpServer()
{
if(_sockfd>gsockfd)::close(_sockfd);
}
private:
int _sockfd;
uint16_t _localport; //服务器的端口号
bool _isrunning;
};UdpServerMain.cc
#include"UdpServer.hpp"
#include<memory>
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " local-port" << std::endl;
exit(0);
}
uint16_t port = std::stoi(argv[1]);
EnableScreen();
std::unique_ptr<UdpServer> usvr=std::make_unique<UdpServer>(port); //C++14标准
usvr->InitServer();
usvr->Start();
return 0;
}UdpClientMain.cc
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// 客户端需要先知道服务器ip地址和端口号
int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;
exit(0);
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
std::cerr << "create socket error" << std::endl;
exit(1);
}
// client 不需要显示绑定自己的IP和端口,但是需要绑定自己的IP和端口
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
server.sin_addr.s_addr = inet_addr(serverip.c_str());
while (1)
{
std::string line;
std::cout << "Please Enter# ";
std::getline(std::cin, line);
int n = sendto(sockfd, line.c_str(), line.size(), 0, (sockaddr *)&server, sizeof(server));
if (n > 0)
{
struct sockaddr_in temp;
socklen_t len = sizeof(temp);
char buffer[1024];
int m = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&temp, &len);
if (m > 0)
{
buffer[m] = 0;
std::cout << buffer << std::endl;
}
else
{
break;
}
}
else
{
break;
}
}
::close(sockfd);
return 0;
}