本文章主要为博主在学习网络通信的笔记+一个Udp_echo_server,和client的代码实现
1,网络发展,网络协议,意识到网络通信——不同主机的进程间通信,
2,学习如何在应用层调用系统提供的接口进行通信,echo_Udp_server, Udp_client,
目录
一,学的时候找别人的资源过一遍这些概念就好
以下个人理解,未必和发展历史相关,因果正确,博主建议概念性的知识你找别人看,博主的笔记仅仅为了给自己后期复习提醒一下,有些抽象和忽略,
发展/背景:局域网->广域网,主机数量变多,距离变远,各个要连接起来的局域网的协议不同,但却要组织起来进行通信,所以再次分层/包装->网络层,
下面列些概念性的,懒得往这里搬了,
1,纵向分层,横向模块
2,port端口号——主机在网络通信这块儿区别进程的标识符,
Ip地址——主机在网络里的标识符,
Mac地址——主机在局域网的标识符,
3,
二,贴点代码,
博主在写的时候遇见最大的坑是ip转了两次(一次点分十进制转网络(inet_pton函数)一次在自己包装的inetaddr里也处理了一次(htonl函数)),导致出错, ,用到的系统调用的函数较少,代码量少,就这样了,跨过了/写过了,就觉得这echo_server和client也就那样,
InetAddr.hpp
#pragma once
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "common.hpp"
// 就把这个class当包装好的sockaddr_in用
// 主要是ip,port 本地转网络填socket, 网络转本地已经转好
class InetAddr{
private:
void PortNet2Host(){ _port = ntohs(_addr.sin_port); }
void IpNet2Host(){
char buf[64] = { 0 };
const char* ip = inet_ntop(AF_INET, &_addr.sin_addr.s_addr, buf, sizeof(buf));
_ip = ip;
}
public:
InetAddr(){
memset(&_addr, 0, sizeof(_addr));
}
InetAddr(struct sockaddr_in& addr)
{
memset(&_addr, 0, sizeof(_addr));
_addr.sin_family = addr.sin_family;
_addr.sin_addr.s_addr = addr.sin_addr.s_addr;
_addr.sin_port = addr.sin_port;
Init();
}
InetAddr(uint32_t ip, uint16_t port)
{
// ip = htonl(ip);
port = htons(port);
memset(&_addr, 0, sizeof(_addr));
_addr.sin_family = AF_INET;
_addr.sin_addr.s_addr = ip;
_addr.sin_port = port;
// 由_addr去设置_ip和_port
Init();
}
InetAddr(uint16_t port){
port = htons(port);
memset(&_addr, 0, sizeof(_addr));
_addr.sin_family = AF_INET;
_addr.sin_addr.s_addr = INADDR_ANY;
_addr.sin_port = port;
Init();
}
void Init(){
PortNet2Host();
IpNet2Host();
}
struct sockaddr* Sockaddr_in(){ return CONV(&_addr); }
socklen_t Socklen() { return sizeof(_addr); }
std::string Ip(){ return _ip; }
uint16_t Port(){ return _port; }
~InetAddr(){
}
private:
struct sockaddr_in _addr;
std::string _ip = "";
uint16_t _port = 0;
};
common.hpp
#pragma once
#include <iostream>
#define Die(code) \
do \
{ \
exit(code); \
} while (0)
#define CONV(v) (struct sockaddr *)(v)
enum
{
USAGE_ERR = 1,
SOCKET_ERR,
BIND_ERR,
RECVFORM_ERR,
SENDTO_ERR
};
Server.hpp
#pragma once
#include <iostream>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
#include "common.hpp"
// ipv4 Udp EchoServer
// 服务器需要,自己设置的port,ip写INADDR_ANY,
// 1 socket,
// 2 bind
// 3 调用recvfrom()与sendto()开始通信
// 我需要设计一个class server, 给它指定的port,它能自己socket并bind, 要有start函数去使用recvfrom,sendto去通信
// 我还要一个class, 给它sockaddr_in, 它让我直接看到ip的点分十进制, 给它port, ip = INADDR_ANY,能吐出sockaddr_in.
// server class == 一个运行的server,
using func_t = std::function<std::string(const std::string&)>;
#define INIT_PORT 8080
class UdpServer{
public:
UdpServer(uint16_t port = INIT_PORT, func_t func = [](const std::string& s1){
return s1;
})
:_addr(INADDR_ANY, port)
{
_func = func;
}
void Init(){
// 建文件描述符
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(_sockfd < 0){
Die(SOCKET_ERR);
}
if(bind(_sockfd, _addr.Sockaddr_in(), _addr.Socklen())){
Die(BIND_ERR);
}
}
void start(){
_isrunning = true;
std::cout << "start_server ~\n" << "wait client send messsge!" << std::endl;
while(1){
InetAddr client_sockaddr;
char buf[1024] = { 0 };
socklen_t size = client_sockaddr.Socklen();
int n = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, client_sockaddr.Sockaddr_in(), &size);
if(n < 0){
Die(RECVFORM_ERR);
}
std::string s(buf);
client_sockaddr.Init();
std::cout << client_sockaddr.Ip() << ":" << client_sockaddr.Port() << ": " << s << std::endl;
// s = _func(s);
//
n = sendto(_sockfd, s.c_str(), s.size(), 0, client_sockaddr.Sockaddr_in(), client_sockaddr.Socklen());
if(n < 0){
Die(SENDTO_ERR);
}
}
_isrunning = false;
}
private:
int _sockfd = -1;
InetAddr _addr; // 只存个自己的socket
bool _isrunning = false; // 服务器运行状态
// 业务
func_t _func;
};
Server_main.cpp
#include "Server.hpp"
#include <functional>
#include <memory>
int main(int argc, char* argv[]){
if(argc != 2){
std::cout << "die~\n";
Die(-1);
}
uint16_t port = atoi(argv[1]);
std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(port);
svr_uptr->Init();
std::cout << "Init_server ~\n";
svr_uptr->start();
return 0;
}
Client_main.hpp
#include <iostream>
#include "common.hpp"
#include "InetAddr.hpp"
int main(int argc, char* argv[]){
if(argc != 3){
exit(-1);
}
uint32_t ip = 0;
inet_pton(AF_INET, argv[1], &ip);
uint16_t port = atoi(argv[2]);
InetAddr aim_server_addr(ip, port);
int _fd = socket(AF_INET, SOCK_DGRAM, 0);
while(1){
std::cout << "Please Enter# " << std::endl;
std::string s;
socklen_t size = aim_server_addr.Socklen();
std::getline(std::cin, s);
int n = sendto(_fd, s.c_str(), s.size(), 0, aim_server_addr.Sockaddr_in(), aim_server_addr.Socklen());
if(n < 0){
Die(SENDTO_ERR);
}
std::cout << "sendto ed" << std::endl;
std::cout << "sendto ed" << std::endl;
std::cout << "sendto ed" << std::endl;
std::cout << "sendto ed" << std::endl;
std::cout << "sendto ed" << std::endl;
std::cout << "sendto ed" << std::endl;
char buf[1024] = { 0 };
n = recvfrom(_fd, buf, sizeof(buf) - 1, 0, aim_server_addr.Sockaddr_in(), &size);
if(n > 0){
buf[n] = 0;
std::cout << buf << std::endl;
}
}
return 0;
}
makefile
.PHONY:all
all:server_udp client_udp
server_udp:Server_main.cpp
g++ -o $@ $^ -std=c++17
client_udp:Client_main.cpp
g++ -o $@ $^ -std=c++17
.PHONY:clean
clean:
rm -f server_udp client_udp