ip:标记网络中的一台主机的地址
port:标记一台主机(某个系统)上的一个进程
socket = ip + port :用来表示全网的唯一一个进程
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6以及UNIX Domain Socket。然而,各种网络协议的地址格式并不相同,如下图所示,常用的是sockaddr_in结构体。
传输层协议、无连接、不可靠传输、面向数据报
ip地址:127.0.0.1成为本地环回,通常用来进行网络通信代码的本地测试,一般使用该ip地址如果能通信的话,就代表本地环境及代码基本没有大问题。
使用recvfrom函数接受信息,sendto函数发送信息,返回值类型都为ssize_t。
class udpServer{
private:
std::string ip; //ip地址
int port; //端口号
int sock; //socket文件描述符
public:
udpServer(int _port, std::string _ip="127.0.0.1")
:ip(_ip), port(_port){ //构造函数(带参)
}
void InitServer(){
//创建套接字-->绑定
sock = socket(AF_INET, SOCK_DGRAM, 0); //创建套接字:成功返回3
//tcp:SOCK_STREAM udp:SOCK_DGRAM
if(socket < 0){
std::cout << "socket create error..." << std::endl;
}
std::cout << "sock: " << sock << std::endl;
//创建sockaddr_in结构体:表示服务器端的网络协议地址
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port); //主机端口号转换为16位的网络端口号
local.sin_addr.s_addr = inet_addr(ip.c_str()); //首先转换为char类型,然后再转换为网络地址类型
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0){ //绑定套接字:成功返回0,否则为-1
std::cerr << "bind error!\n" << std::endl;
exit(1);
}
}
void Start(){
//服务器端:接受请求-->发送应答
char msg[64]; //接受客户端发送的信息
for( ; ; ){
msg[0] = '\0';
struct sockaddr_in end_point;
socklen_t len = sizeof(end_point); //网络协议地址的长度,类型为socklen_t
ssize_t s = recvfrom(sock, msg, sizeof(msg)-1, 0, (struct sockaddr*)&end_point, &len);
//从远端读数据,成功返回值>0,返回类型为ssize_t
if(s > 0){
//读取成功,发送应答
msg[s] = '\0';
char buf[16];
sprintf(buf, "%d", ntohs(end_point.sin_port)); //整型转字符串类型
std::string client = (inet_ntoa(end_point.sin_addr));
client += ":";
client += buf;
std::cout << client << "#" << msg << std::endl; //输出客户端信息及读到的数据
std::string echo_string = msg;
echo_string += " [server echo!] ";
sendto(sock, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&end_point, len);
}
}
}
~udpServer(){ //析构函数
close(sock);
}
};
以命令行参数的形式传入创建udp服务器所需要的端口号参数。
void Usage(std::string proc){ //显示当前执行程序的基本信息
std::cout << "Usage:" << proc << "local_port" << std::endl;
}
int main(int argc, char *argv[]){
if(argc != 2){
Usage(argv[0]);
exit(1);
}
udpServer *us = new udpServer(atoi(argv[1])); //传入端口号
us->InitServer();
us->Start();
delete us;
}
udp客户端中使用的ip和端口号,不是链接,而是标记了服务器在哪,用于向服务器发送数据。
class udpClient{
private:
std::string ip;
int port;
int sock;
public:
//这里的ip和port对应的是Server的ip、port!!!
udpClient(std::string _ip, int _port)
:ip(_ip), port(_port){
}
void InitClient(){
//创建套接字
sock = socket(AF_INET, SOCK_DGRAM, 0); //创建套接字:成功返回3
std::cout << "sock: " <<sock << std::endl;
}
void Start(){
//客户端:发送请求-->接受应答
struct sockaddr_in peer;
peer.sin_family = AF_INET;
peer.sin_port = htons(port);
peer.sin_addr.s_addr = inet_addr(ip.c_str());
std::string msg;
for(; ; ){
std::cout << "Please Enter# ";
std::cin >> msg;
if(msg == "quit"){
break; //当前客户端退出
}
sendto(sock, msg.c_str(), msg.size(), 0, (struct sockaddr*)&peer, sizeof(peer));
char echo[128];
ssize_t s = recvfrom(sock, echo, sizeof(echo)-1, 0, nullptr, nullptr);
if(s > 0){
echo[s] = 0;
std::cout << "server# " << echo << std::endl;
}
}
}
~udpClient(){ //析构函数
close(sock);
}
};
以命令行参数的形式传入创建udp客户端所需要的ip地址参数和端口号参数。
void Usage(std::string proc){
std::cout << "Usage:" << proc << "server_ip, server_port" << std::endl;
}
int main(int argc, char *argv[]){
if(argc != 3){
Usage(argv[0]);
exit(1);
}
udpClient uc(argv[1], atoi(argv[2]));
uc.InitClient();
uc.Start();
return 0;
}
主机端口号转换为16位的网络端口号----htons()
16位的网络端口号转换为主机端口号----ntohs()
char类型转换为网络地址类型----inet_addr()
网络地址类型转为字符串----inet_ntoa()----ip输出
整型转字符串类型输出----sprintf()----端口号输出