點分十進制字符串IP 轉 32位網絡序列IP
分析:1)IP轉成4字節? ? ? ? 2)4字節轉成網絡序列
思路:
- "192.168.1.1" 進行字符串劃分,以 "." 為分割符,分割出"192","168","1","1"
- 將這些字符串轉成8位的整型(字符),192,168,1,1
- 網絡字節序是大端,高權值位放在低地址,定義一個32位整型(變量內字節地址是依次增大,取地址是最小地址),192放取地址的第一個字節,168第二個字節,以此類推。
或者用 inet_addr(),將ip點分十進制字符串傳入,返回值4字節網絡字節序。
接口使用
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
- sockfd:創建套接字打開的文件fd
- buf:接受緩沖區
- len:接受緩沖區大小
- flags:0,表示阻塞式IO
- src_addr:輸出型參數,用來獲取客戶端的套接字信息(IP和端口)
- addrlen:先是輸入型參數,src_addr指向結構體大小,后是輸出型參數,實際讀到信息大小
- 返回值:成功,返回接受到數據的字節數;失敗-1返回
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
- sockfd:創建套接字打開的文件fd
- buf:發送緩沖區
- len:發送緩沖區大小
- flags:0,表示阻塞式IO
- src_addr:輸入型參數,指定發給哪一個客戶端
- addrlen:src_addr指向的結構體大小
- 返回值:成功,發送的字節數;失敗-1返回,錯誤碼被設置
客戶端如何知道服務端IP和端口
客戶端和服務端是一家公司寫的,內置了服務端的IP和端口
本地環回(localhost, 127.0.0.1)
????????本地環回:要求客戶端和服務端必須在一臺機器上,表明我們是本地通信,client發送的數據,不會被推送到網絡,而是在OS內部,轉一圈直接交給對應的服務器端(經常用來進行網絡代碼的測試)
服務端bind時不顯式綁定IP
bind公網IP,不行的原因:公網IP沒有配置到IP上,同一主機綁定時,無法直接bind。
如果我們顯式進行地址綁定,client未來訪問時,就必須使用server綁定的信息。
為什么不建議手動綁定特定IP?
????????因為一臺主機可以有多個IP,綁定特定IP,只能接受該IP的數據,而發給其他本主機IP的數據接收不了。
做法:綁定IP時,值設置為?INADDR_ANY(任意IP地址bind,數值上為0)
客戶端不用顯式bind
- client需不需要bind?需要bind
- client需不需要顯式bind?不需要,首次發送消息OS會自動給client進行bind,OS知道IP,端口號采用隨機端口號的方式。(端口號是幾不重要,唯一就行了)
- 原因:
1. 一個端口號只能被一個進程所持有(端口號要找到唯一一個進程)
2.未來有很多應用,端口號固定可能會導致沖突,無法bind,必須由OS隨機分配
UdpSocket編程V1(EchoServer)
Linux-remote: linux遠程倉庫
UdpServer.hpp
#pragma once#include <iostream> #include <string> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <functional> #include "Log.hpp"using namespace LogModule;#define BUFF_SIZE 1024 const int defaultfd = -1;class UdpServer {using func_t = std::function<std::string(const std::string &)>; public:UdpServer(uint16_t port, func_t func): _sockfd(defaultfd), _port(port), _isrunning(false), _func(func){// 1.創建套接字,(網絡類型,UDP協議)獲取網絡文件描述符_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(1);}LOG(LogLevel::INFO) << "socket success, sockfd: " << _sockfd;// 2.bind,綁定(通信類型,端口號port,IP)// 2.1 創建結構sockaddr_in, (man inet_addr),填充字段struct sockaddr_in peer;peer.sin_family = AF_INET;peer.sin_port = htons(_port);// 2.2 ip地址是一個結構,結構存放一個32位無符號整數// ip是一個字符串,192.168.1.1, 1.轉成數字 2.轉成網絡序列 -> inet_addr// peer.sin_addr.s_addr = inet_addr(_ip.c_str());peer.sin_addr.s_addr = INADDR_ANY; // 任意IP// 2.3 綁定int n = bind(_sockfd, (struct sockaddr *)&peer, sizeof(peer));if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(2);}LOG(LogLevel::INFO) << "bind success, sockfd: " << _sockfd;}void Start(){_isrunning = true;while (_isrunning){// 3.服務端收數據char buff[BUFF_SIZE];struct sockaddr_in peer;socklen_t addrlen = sizeof(struct sockaddr_in);ssize_t n = recvfrom(_sockfd, buff, BUFF_SIZE, 0,(struct sockaddr *)&peer, &addrlen);// 4.消息處理buff[n] = 0;std::string data = buff;LOG(LogLevel::INFO) << "client Says: " << data;std::string result = _func(data);// 5.服務端發送數據回客戶端ssize_t ret = sendto(_sockfd, result.c_str(), result.size(), 0, (const struct sockaddr *)&peer, addrlen);(void)ret;}}~UdpServer(){}private:int _sockfd;uint16_t _port;// std::string _ip; //不需要,任意IPbool _isrunning;func_t _func; };
UdpClient.cc
#include <iostream> #include <string> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "Log.hpp"using namespace LogModule;// ./udpclient server_ip server_port int main(int argc, char *argv[]) {if (argc != 3){std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;return 1;}std::string ip = argv[1];uint16_t port = (uint16_t)(std::stoi(argv[2]));LOG(LogLevel::DEBUG) << "ip: " << ip << " port:" << port;// 1.創建socket,(網絡類型,UDP)int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(1);}LOG(LogLevel::INFO) << "socket success, sockfd: " << sockfd;// 2.綁定參數,bind,不用顯式綁定。// 首次發送消息時由OS自動綁定,OS知道IP,端口號由OS隨機分配struct sockaddr_in peer;peer.sin_family = AF_INET;peer.sin_port = htons(port);peer.sin_addr.s_addr = inet_addr(ip.c_str());while (true){// 3.客戶端發數據// 3.1 從標準輸入按行讀數據std::cout << "Please Enter# ";std::string data;std::getline(std::cin, data);// 3.2 發送數據給服務端ssize_t ret = sendto(sockfd, data.c_str(), data.size(), 0, (struct sockaddr *)&peer, sizeof(peer));(void)ret;// 4.接受服務端處理過的數據char buff[1024];struct sockaddr_in sin;socklen_t len = sizeof(struct sockaddr_in);ssize_t n = recvfrom(sockfd, buff, 1024, 0, (struct sockaddr *)&sin, &len);// 5.使用數據buff[n] = 0;std::cout << "Server handlered, data: " << buff << std::endl;}return 0; }
UdpServer.cc
#include "UdpServer.hpp" #include <iostream> #include <memory>std::string Hello(const std::string &data) {std::string hello("Hello, ");hello += data;return hello; }// ./udpserver port int main(int argc, char *argv[]) {if (argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;return 1;}uint16_t port = (uint16_t)std::stoi(argv[1]);std::unique_ptr<UdpServer> udpserver = std::make_unique<UdpServer>(port, Hello);udpserver->Start();return 0; }