【Linux网络编程】Socket编程UDP
- UDP网络编程
-
- [V1版本 - EchoServer](#V1版本 - EchoServer)
- [V2版本 - DictServer](#V2版本 - DictServer)
- [V3版本 - 简单聊天室](#V3版本 - 简单聊天室)
UDP网络编程
V1版本 - EchoServer
EchoServer.hpp
cpp
#ifndef __ECHO_SERVER_HPP
#define __ECHO_SERVER_HPP
#include <iostream>
#include <cstdlib>
#include <string>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Logger.hpp"
using namespace NS_LOG_MODULE;
const int default_fd = -1;
const int default_port = 6666;
enum
{
SUCCESS = 0,
SOCKET_ERR,
USAGE_ERR,
BIND_ERR
};
class UdpServer
{
public:
// UdpServer(const std::string ip, uint16_t port = default_port)
UdpServer(uint16_t port = default_port)
: _sockfd(default_fd)
, _port(port)
{}
~UdpServer()
{
close(_sockfd);
}
void Init()
{
// 第一步:创建socket,本质:打开网卡 -- 系统特性
_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 这里的AF_INET说明是要创建一个网络套接字
if(_sockfd < 0)
{
LOG(LogLevel::FATAL) << "create socket error";
exit(SOCKET_ERR);
}
LOG(LogLevel::INFO) << "create socket success, sockfd: " << _sockfd;
// 第二步:填充网络信息
struct sockaddr_in local;
bzero(&local, sizeof(local)); // 将local清零
local.sin_family = AF_INET; // 这里的AF_INET说明这个结构体子类的类型是网络通信的类型
local.sin_port = htons(_port); // h->n 主机到网络的转化
// local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. 字符串IP转4字节IP 2. h->n
local.sin_addr.s_addr = INADDR_ANY; // 任意IP地址绑定
// 第三步:bind socket 信息 绑定套接字信息
int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
if(n < 0)
{
LOG(LogLevel::FATAL) << "bind socket error";
exit(BIND_ERR);
}
LOG(LogLevel::INFO) << "bind socket success, port: " << _port;
}
void Start()
{
// 传递的是字符串,echo server
char inbuffer[1024];
while(true)
{
// 读消息
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &len);
// inbuffer:用户提供,保存收到的数据
// flag = 0 表示阻塞读取
// peer:接收发送方(客户端)的socket信息,即ip和port
// len:输入输出型参数,输入时必须设置成传入struct sockaddr_in结构体的大小,输出时返回实际结构体的大小,往往两值相等
// 返回值:成功返回读取字节数,失败-1
if(n < 0)
{
LOG(LogLevel::ERROR) << "recvfrom error";
}
else
{
uint16_t client_port = ntohs(peer.sin_port);
std::string client_ip = inet_ntoa(peer.sin_addr);
std::string client_address = "[" + client_ip + ":" + std::to_string(client_port) + "] say# ";
inbuffer[n] = 0;
LOG(LogLevel::DEBUG) << client_address << inbuffer;
std::string echo_string = "server say# ";
echo_string += inbuffer;
// 把收到的字符串再返回给客户端
sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&peer, len);
}
}
}
private:
int _sockfd;
// std::string _ip; // "192.168.2.2"(字符串风格的点分十进制IP地址,让人看的) && 4字节IP
uint16_t _port; // 用户设置好的,server port必须是固定的
};
#endif
cpp
#include "EchoServer.hpp"
#include <memory>
static void Usage(const std::string process)
{
std::cerr << "Usage:\n\t" << process << " local_port" << std::endl;
}
// ./server_udp server_port
int main(int argc, char* argv[])
{
if(argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
ENABLE_CONSOLE_LOG_STRATEGY();
uint32_t server_port = std::stoi(argv[1]);
std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(server_port);
usvr->Init();
usvr->Start();
return 0;
}
cpp
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
static void Usage(const std::string& process)
{
std::cout << "Usage:\n\t" << process << " server_ip server_port" << std::endl;
}
// client怎么知道server的IP和Port?server被内置到了client
// ./client_udp server_ip server_port
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string server_ip = argv[1];
uint16_t server_port = std::stoi(argv[2]);
// 1. 创建socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
std::cerr << "create socket error" << std::endl;
exit(2);
}
// 2. 构建server端socket信息
// client 需要有自己的IP和Port吗?需要
// 需要显式的bind自己的IP和Port吗?不要显式bind
// 1) 为什么不让client显式bind?client bind port 可能会出现冲突,client port只需要具有唯一性即可,具体是多少不重要
// 2) 如何设置自己的IP和Port呢?client 一般会采用随机端口的方式,由OS自主选择
// udp client 首次发送数据的时候,OS底层会隐式自动帮client进行获取随机端口,然后 bind + Port + IP
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(server_ip.c_str());
server.sin_port = htons(server_port);
while(true)
{
std::string message;
// 1. 获取用户输入
std::cout << "Please Enter# ";
std::getline(std::cin, message);
// 2. client 发送数据给 server,首次发送即自动绑定
ssize_t n = sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
if(n > 0)
{
// recvfrom
char inbuffer[1024] = {0};
struct sockaddr_in temp;
socklen_t len = sizeof(temp);
ssize_t m = recvfrom(sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&temp, &len);
if(m > 0)
{
inbuffer[m] = 0;
std::cout << inbuffer << std::endl;
}
}
}
return 0;
}
V2版本 - DictServer
dict.txt
cpp
apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天
Dict.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>
#include "Logger.hpp"
using namespace NS_LOG_MODULE;
static const std::string default_path = "./dict.txt";
static const std::string sep = ": ";
class Dict
{
public:
Dict(const std::string& dict_path = default_path)
:_dict_path(dict_path)
{
LoadDict();
}
~Dict(){}
void LoadDict()
{
std::fstream in(_dict_path);
if(!in.is_open())
{
LOG(LogLevel::FATAL) << "open " << _dict_path << " error";
exit(1);
}
std::string line;
while(std::getline(in, line))
{
LOG(LogLevel::DEBUG) << "load: " << line << " success";
// apple: 苹果
auto pos = line.find(sep);
if(pos == std::string::npos)
{
LOG(LogLevel::WARNING) << "Format: " << line << " error";
continue;
}
std::string k = line.substr(0, pos);
std::string v = line.substr(pos + sep.size());
_dict.insert(std::make_pair(k, v));
}
in.close();
LOG(LogLevel::INFO) << "load done...";
}
std::string Translate(std::string word)
{
auto iter = _dict.find(word);
if(iter != _dict.end())
{
return iter->second;
}
else
{
return "None";
}
}
private:
std::string _dict_path;
std::unordered_map<std::string, std::string> _dict;
};
DictServer.hpp
cpp
#ifndef __DICT_SERVER_HPP
#define __DICT_SERVER_HPP
#include <iostream>
#include <cstdlib>
#include <string>
#include <strings.h>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Logger.hpp"
using namespace NS_LOG_MODULE;
const int default_fd = -1;
const int default_port = 6666;
using callback_t = std::function<std::string(std::string)>;
enum
{
SUCCESS = 0,
SOCKET_ERR,
USAGE_ERR,
BIND_ERR
};
class UdpServer
{
public:
// UdpServer(const std::string ip, uint16_t port = default_port)
UdpServer(callback_t cb, uint16_t port = default_port)
: _cb(cb)
, _sockfd(default_fd)
, _port(port)
{}
~UdpServer()
{
close(_sockfd);
}
void Init()
{
// 第一步:创建socket,本质:打开网卡 -- 系统特性
_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 这里的AF_INET说明是要创建一个网络套接字
if(_sockfd < 0)
{
LOG(LogLevel::FATAL) << "create socket error";
exit(SOCKET_ERR);
}
LOG(LogLevel::INFO) << "create socket success, sockfd: " << _sockfd;
// 第二步:填充网络信息
struct sockaddr_in local;
bzero(&local, sizeof(local)); // 将local清零
local.sin_family = AF_INET; // 这里的AF_INET说明这个结构体子类的类型是网络通信的类型
local.sin_port = htons(_port); // h->n 主机到网络的转化
// local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. 字符串IP转4字节IP 2. h->n
local.sin_addr.s_addr = INADDR_ANY; // 任意IP地址绑定
// 第三步:bind socket 信息 绑定套接字信息
int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
if(n < 0)
{
LOG(LogLevel::FATAL) << "bind socket error";
exit(BIND_ERR);
}
LOG(LogLevel::INFO) << "bind socket success, port: " << _port;
}
void Start()
{
// 传递的是字符串,echo server
char inbuffer[1024];
while(true)
{
// 读消息
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &len);
// inbuffer:用户提供,保存收到的数据
// flag = 0 表示阻塞读取
// peer:接收发送方(客户端)的socket信息,即ip和port
// len:输入输出型参数,输入时必须设置成传入struct sockaddr_in结构体的大小,输出时返回实际结构体的大小,往往两值相等
// 返回值:成功返回读取字节数,失败-1
if(n < 0)
{
LOG(LogLevel::ERROR) << "recvfrom error";
}
else
{
uint16_t client_port = ntohs(peer.sin_port);
std::string client_ip = inet_ntoa(peer.sin_addr);
std::string client_address = "[" + client_ip + ":" + std::to_string(client_port) + "] say# ";
inbuffer[n] = 0;
LOG(LogLevel::DEBUG) << client_address << inbuffer;
// std::string echo_string = "server say# ";
// echo_string += inbuffer;
std::string result = _cb(inbuffer); // 单词
// 把收到的字符串再返回给客户端
// sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&peer, len);
sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);
}
}
}
private:
int _sockfd;
// std::string _ip; // "192.168.2.2"(字符串风格的点分十进制IP地址,让人看的) && 4字节IP
uint16_t _port; // 用户设置好的,server port必须是固定的
callback_t _cb; // 用回调的方式进行数据加工
};
#endif
cpp
#include "DictServer.hpp"
#include "Dict.hpp"
#include <memory>
static void Usage(const std::string process)
{
std::cerr << "Usage:\n\t" << process << " local_port" << std::endl;
}
// ./server_udp server_port
int main(int argc, char* argv[])
{
if(argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
ENABLE_CONSOLE_LOG_STRATEGY();
// 1. 定义字典
Dict dict;
// 2. 构建网络服务,处理IO问题
uint32_t server_port = std::stoi(argv[1]);
// 3. 绑定上下两层
std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>([&dict](std::string word)->std::string{
return dict.Translate(word);
}, server_port);
usvr->Init();
usvr->Start();
return 0;
}
cpp
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
static void Usage(const std::string& process)
{
std::cout << "Usage:\n\t" << process << " server_ip server_port" << std::endl;
}
// client怎么知道server的IP和Port?server被内置到了client
// ./client_udp server_ip server_port
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string server_ip = argv[1];
uint16_t server_port = std::stoi(argv[2]);
// 1. 创建socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
std::cerr << "create socket error" << std::endl;
exit(2);
}
// 2. 构建server端socket信息
// client 需要有自己的IP和Port吗?需要
// 需要显式的bind自己的IP和Port吗?不要显式bind
// 1) 为什么不让client显式bind?client bind port 可能会出现冲突,client port只需要具有唯一性即可,具体是多少不重要
// 2) 如何设置自己的IP和Port呢?client 一般会采用随机端口的方式,由OS自主选择
// udp client 首次发送数据的时候,OS底层会隐式自动帮client进行获取随机端口,然后 bind + Port + IP
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(server_ip.c_str());
server.sin_port = htons(server_port);
while(true)
{
std::string message;
// 1. 获取用户输入
std::cout << "Please Enter# ";
std::getline(std::cin, message);
// 2. client 发送数据给 server,首次发送即自动绑定
ssize_t n = sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
if(n > 0)
{
// recvfrom
char inbuffer[1024] = {0};
struct sockaddr_in temp;
socklen_t len = sizeof(temp);
ssize_t m = recvfrom(sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&temp, &len);
if(m > 0)
{
inbuffer[m] = 0;
std::cout << inbuffer << std::endl;
}
}
}
return 0;
}
V3版本 - 简单聊天室
UdpServer.hpp
cpp
#ifndef __UDP_SERVER_HPP
#define __UDP_SERVER_HPP
#include <iostream>
#include <cstdlib>
#include <string>
#include <strings.h>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
#include "Logger.hpp"
using namespace NS_LOG_MODULE;
const int default_fd = -1;
const int default_port = 6666;
using handler_addr_t = std::function<void(const InetAddr &)>;
using handler_msg_t = std::function<void(int sockfd, std::string msg)>;
enum
{
SUCCESS = 0,
SOCKET_ERR,
USAGE_ERR,
BIND_ERR
};
class UdpServer
{
public:
UdpServer(uint16_t port = default_port)
: _sockfd(default_fd), _port(port)
{
}
~UdpServer()
{
close(_sockfd);
}
void Init()
{
// 第一步:创建socket,本质:打开网卡 -- 系统特性
_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 这里的AF_INET说明是要创建一个网络套接字
if (_sockfd < 0)
{
LOG(LogLevel::FATAL) << "create socket error";
exit(SOCKET_ERR);
}
LOG(LogLevel::INFO) << "create socket success, sockfd: " << _sockfd;
// 第二步:填充网络信息
InetAddr local(_port);
// 第三步:bind socket 信息 绑定套接字信息
int n = bind(_sockfd, (struct sockaddr *)(local.GetNetAddress()), local.Len());
if (n < 0)
{
LOG(LogLevel::FATAL) << "bind socket error";
exit(BIND_ERR);
}
LOG(LogLevel::INFO) << "bind socket success, port: " << _port;
}
void RegisterService(handler_addr_t handler_addr, handler_msg_t handler_msg)
{
_handler_addr = handler_addr;
_handler_msg = handler_msg;
}
void Start()
{
// 传递的是字符串,echo server
char inbuffer[1024];
while (true)
{
// 读消息
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&peer, &len);
// inbuffer:用户提供,保存收到的数据
// flag = 0 表示阻塞读取
// peer:接收发送方(客户端)的socket信息,即ip和port
// len:输入输出型参数,输入时必须设置成传入struct sockaddr_in结构体的大小,输出时返回实际结构体的大小,往往两值相等
// 返回值:成功返回读取字节数,失败-1
if (n < 0)
{
LOG(LogLevel::ERROR) << "recvfrom error";
}
else
{
inbuffer[n] = 0;
// 1. 检测新用户
InetAddr clientaddress(peer);
std::string tips = clientaddress.ToString();
std::string message = tips + inbuffer;
LOG(LogLevel::DEBUG) << message;
_handler_addr(clientaddress);
// 2. 转发消息
_handler_msg(_sockfd, message);
}
}
}
private:
int _sockfd;
// std::string _ip; // "192.168.2.2"(字符串风格的点分十进制IP地址,让人看的) && 4字节IP
uint16_t _port; // 用户设置好的,server port必须是固定的
handler_addr_t _handler_addr; // 处理地址
handler_msg_t _handler_msg; // 处理消息
};
#endif
InetAddr.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// 对客户端进行先描述
class InetAddr
{
public:
InetAddr(const struct sockaddr_in& address):_address(address), _len(sizeof(address))
{
_port = ntohs(_address.sin_port);
_ip = inet_ntoa(_address.sin_addr);
}
InetAddr(uint16_t port, const std::string& ip = "0.0.0.0"):_port(port), _ip(ip)
{
bzero(&_address, sizeof(_address)); // 将_address清零
_address.sin_family = AF_INET; // 这里的AF_INET说明这个结构体子类的类型是网络通信的类型
_address.sin_port = htons(_port); // h->n 主机到网络的转化
_address.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. 字符串IP转4字节IP 2. h->n
_len = sizeof(_address);
}
InetAddr()
{}
bool operator==(const InetAddr& addr)
{
return (this->_ip == addr._ip) && (this->_port == addr._port);
}
std::string ToString()
{
return "[" + this->_ip + ":" + std::to_string(this->_port) + "]";
}
struct sockaddr_in* GetNetAddress()
{
return &_address;
}
socklen_t Len()
{
return _len;
}
~InetAddr()
{}
private:
// net address
struct sockaddr_in _address;
socklen_t _len;
// host address
std::string _ip;
uint16_t _port;
};
UserManager.hpp
cpp
#pragma once
#include <iostream>
#include <vector>
#include "InetAddr.hpp"
#include "Logger.hpp"
using namespace NS_LOG_MODULE;
// 管理用户,增删查改
class UserManager
{
public:
UserManager(){}
void AddUser(const InetAddr& addr)
{
if(SearchUser(addr))
return;
_users.push_back(addr);
}
void DelUser(const InetAddr& addr)
{
for(auto iter = _users.begin(); iter != _users.end(); iter++)
{
if(*iter == addr)
{
_users.erase(iter);
break;
}
}
}
bool SearchUser(const InetAddr& addr)
{
for(auto& user: _users)
{
if(user == addr)
{
return true;
}
}
return false;
}
void ModUser(const InetAddr& addr)
{
DelUser(addr);
AddUser(addr);
}
std::vector<InetAddr>& Users()
{
return _users;
}
~UserManager(){}
private:
std::vector<InetAddr> _users;
};
Route.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <sys/socket.h>
#include "UserManager.hpp"
#include "Mutex.hpp"
// 路由:完成转发功能
class Route
{
public:
Route():_uma(std::make_unique<UserManager>())
{}
void CheckUser(const InetAddr& addr)
{
LockGuard lockguard(_lock);
_uma->AddUser(addr);
}
void OfflineUser(const InetAddr& addr)
{
LockGuard lockguard(_lock);
_uma->DelUser(addr);
}
void Broadcast(int sockfd, std::string message)
{
LockGuard lockguard(_lock);
auto& users = _uma->Users();
for(auto& user : users)
{
sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)user.GetNetAddress(), user.Len());
}
}
~Route(){}
private:
std::unique_ptr<UserManager> _uma;
Mutex _lock;
};
cpp
#include "ThreadPool.hpp" // 执行处理事件
#include "Route.hpp" // 任务
#include "UdpServer.hpp" // 获取事件
#include <memory>
static void Usage(const std::string process)
{
std::cerr << "Usage:\n\t" << process << " local_port" << std::endl;
}
using namespace NS_THREAD_POOL_MODULE;
using task_t = std::function<void()>;
// ./server_udp server_port
int main(int argc, char* argv[])
{
if(argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
ENABLE_CONSOLE_LOG_STRATEGY();
uint16_t server_port = std::stoi(argv[1]);
ThreadPool<task_t>* thread_pool = ThreadPool<task_t>::Instance(); // 线程池模块
Route r; // 路由模块
UdpServer usvr(server_port); // 网络模块
usvr.Init();
usvr.RegisterService(
[&r](const InetAddr& addr){
r.CheckUser(addr);
},
[&r, thread_pool](int sockfd, std::string msg){
auto t = std::bind(&Route::Broadcast, &r, sockfd, msg);
thread_pool->Enqueue(t);
});
usvr.Start();
return 0;
}
cpp
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Thread.hpp"
#include "InetAddr.hpp"
using namespace NS_THREAD_MODULE;
int sockfd;
std::string server_ip;
uint16_t server_port;
std::string nickname;
static void Usage(const std::string &process)
{
std::cout << "Usage:\n\t" << process << " server_ip server_port" << std::endl;
}
static void Online(InetAddr& serveraddr)
{
std::cout << "Plaease Set Your Nick Name# ";
std::getline(std::cin, nickname);
std::string online_message = nickname + " online!";
ssize_t n = sendto(sockfd, online_message.c_str(), online_message.size(), 0, (struct sockaddr *)serveraddr.GetNetAddress(), serveraddr.Len());
(void)n;
}
void RecvMessage()
{
while (true)
{
// recvfrom
char inbuffer[1024] = {0};
struct sockaddr_in temp;
socklen_t len = sizeof(temp);
ssize_t m = recvfrom(sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&temp, &len);
if (m > 0)
{
inbuffer[m] = 0;
std::cerr << inbuffer << std::endl; // 2
}
}
}
void SendMessage()
{
InetAddr serveraddr(server_port, server_ip);
Online(serveraddr);
while (true)
{
std::string message;
// 1. 获取用户输入
std::cout << "Please Enter# "; // 1
std::getline(std::cin, message);
message = nickname + "# " + message;
// 2. client 发送数据给 server,首次发送即自动绑定
ssize_t n = sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)serveraddr.GetNetAddress(), serveraddr.Len());
(void)n;
}
}
// client怎么知道server的IP和Port?server被内置到了client
// ./client_udp server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
server_ip = argv[1];
server_port = std::stoi(argv[2]);
// 1. 创建socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
std::cerr << "create socket error" << std::endl;
exit(2);
}
Thread recver(RecvMessage);
Thread sender(SendMessage);
recver.Start();
sender.Start();
recver.Join();
sender.Join();
return 0;
}