协议是指通信双方约定使用同一种解析信息的手段来进行有效的沟通
OSI参考模型(自下而上):物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
TCP/IP是一组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇.
TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。
不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报 (datagram),在链路层叫做帧(frame).
应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装
源IP地址和目的IP地址
源IP:src_ip——>标识数据从哪里来
目的IP:dest_ip——>表示数据往哪里去
理解 “端口号” 和 “进程ID”:
字节序:CPU对内存的访问顺序
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
主机字节序:当前机器的字节序。
字节序转换:
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络
字节序和主机字节序的转换。
uint32_t htonl(uint32_t hostlog);
uint32_t ntohl(uint32_t netlog);
uint16_t htons(uint16_t hostshort);
uint16_t ntohs(uint16_t netshort);
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器) int socket(int domain, int type, int protocol); // 绑定端口号 (TCP/UDP, 服务器) int bind(int socket, const struct sockaddr *address, socklen_t address_len); // 开始监听socket (TCP, 服务器) int listen(int socket, int backlog); // 接收请求 (TCP, 服务器) int accept(int socket, struct sockaddr* address, socklen_t* address_len); // 建立连接 (TCP, 客户端) int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址。
IPv4、 IPv6地址类型分别定义为常数AF_INET、 AF_INET6。
socket API可以都用struct sockaddr *
类型表示, 在使用的时候需要强制转化成sockaddr_in
; 这样的好处是统一接口(程序的通用性), 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。
查看soackaddr内部:
虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主
要有三部分信息: 地址类型, 端口号, IP地址。
实现一个简单的英译汉的功能。
Server端代码:
#pragma once #include <iostream> #include <cstdio> #include <string> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <map> class udpServer { private: //std::string ip; int port;//端口号 int sock; std::map<std::string, std::string> dict; public: udpServer(int _port = 8080) :port(_port) { dict.insert(std::pair<std::string, std::string>("apple", "苹果")); dict.insert(std::pair<std::string, std::string>("banana", "香蕉")); dict.insert(std::pair<std::string, std::string>("student", "学生")); } void initServer() { sock = socket(AF_INET, SOCK_DGRAM, 0); std::cout << "sock: " << sock << std::endl; struct sockaddr_in local;//内核层 local.sin_family = AF_INET;//协议家族IPV4 local.sin_port = htons(port); //local.sin_addr.s_addr = inet_addr(ip.c_str()); local.sin_addr.s_addr = INADDR_ANY; if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0) { std::cerr << "bind error!\n" << std::endl; exit(1); } } //echo server void start() { char msg[64]; for(;;) { msg[0] = '\0'; struct sockaddr_in end_point;//远端 socklen_t len = sizeof(end_point); ssize_t s = recvfrom(sock, msg, sizeof(msg)-1, 0,\ (struct sockaddr*)&end_point, &len); if(s > 0) { char buf[16]; sprintf(buf, "%d", ntohs(end_point.sin_port)); std::string cli = inet_ntoa(end_point.sin_addr); cli += ":"; cli += buf; msg[s] = '\0'; std::cout << cli << "# " << msg << std::endl; std::string echo = "unknow"; auto it = dict.find(msg); if(it != dict.end()) { echo = dict[msg]; } //echo_str += " [server echo!]"; sendto(sock, echo.c_str(), echo.size(), 0,\ (struct sockaddr*)&end_point, len); } } } ~udpServer() { close(sock); } };
Client端代码:
#pragma once #include <iostream> #include <string> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> class udpClient { private: std::string ip; int port;//端口号 int sock; public: //ip,port 填服务器的 udpClient(std::string _ip = "127.0.0.1", int _port = 8080) :ip(_ip),port(_port) { } void initClient() { sock = socket(AF_INET, SOCK_DGRAM, 0); std::cout << "sock: " << sock << std::endl; } //echo server void start() { //char msg[64]; std::string msg; struct sockaddr_in peer; peer.sin_family = AF_INET; peer.sin_port = htons(port); peer.sin_addr.s_addr = inet_addr(ip.c_str()); 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); } };