网站设计师网站,wordpress视频加密,wordpress 4.5下载,广州网站建设 推广公司哪家好文章目录 端口号TCP/UDP网络字节序socket的常见APIUDP实现服务器与客户机通信服务器客户机运行效果如下 端口号
我们说即便是计算机网络#xff0c;他们之间的通信也仍然是进程间通信
那么要如何在这么多计算机中#xff0c;找到你想要的那个进程呢
在网络中标识的唯一的计… 文章目录 端口号TCP/UDP网络字节序socket的常见APIUDP实现服务器与客户机通信服务器客户机运行效果如下 端口号
我们说即便是计算机网络他们之间的通信也仍然是进程间通信
那么要如何在这么多计算机中找到你想要的那个进程呢
在网络中标识的唯一的计算机使用的是ip地址
在同一台计算机的进程是通过进程id区分的而要在对方的计算机中按照进程id来找恐怕不是一共好的想法因为你也不知道对方进程的id是多少于是就商量传输层协议使用port端口来确定进程
因此ip加上port就能确定这个世界上的唯一一台计算机中的唯一一个进程
ip地址是用四个八位二进制数来表示
而port端口号是一共2字节16位整数
端口号用来标识进程告诉操作系统数据要交给哪一个进程
都已经有了进程id了为什么还要有个端口号呢
这是因为一个端口号只能被一个进程占用但是一个进程是可以拥有多个端口号的
他们之间并不是完美的1对1关系
TCP/UDP
我们先简单重新认识一下这两个协议
这两个都是传输层的协议
TCP面向的是有连接意思是在正式的传递信息之前需要建立连接确保是能收到的就像对暗号一样土豆土豆我是地瓜
而UDP则是无连接的相当于直接把数据扔出去 由此可见TCP是可靠传输他规定了一些措施来保证传输的可靠性例如三次握手四次挥手 “嗨我想听一个TCP的笑话。” “你好你想听 TCP 的笑话么” “嗯我想听一个 TCP 的笑话。” “好的我会给你讲一个TCP 的笑话。” “好的我会听一个TCP 的笑话。” “你准备好听一个TCP 的笑话么” “嗯我准备好听一个TCP 的笑话” “OK那我要发 TCP 笑话了。大概有 10 秒20 个字。” “嗯我准备收你那个 10 秒时长20 个字的笑话了。” “抱歉你的链接超时了。你好你想听 TCP 的笑话么” 而UDP是完全没有的因此他是不可靠的 我给你们讲个UDP 的笑话吧 哈哈哈哈哈哈哈哈哈是不是很好笑 除此之外TCP由于建立了连接就可以像水流一样传输数据是面向字节流的而UDP则没有所以UDP是面向数据包的
网络字节序
计算机内存分为大端存储和小端存储大端存储是低地址存高位数据小端存储是低地址存低位数据 至今这两个流派也没有分出胜负但是计算机网络不知道啊他不知道两个计算机之间是如何存储的只能硬性规定网络数据流是按照低地址存高位数据也就是大端存储
发送方的主机发送时是按照地址从低到高发送的
如果发送方是小端则先将数据传换成大端
我们可以使用系统调用将大小端字节序进行交换 也就是说小端机器发送时调用hton小端机器接收时调用ntoh
大端直接就原封不动返回了因此不确定大小端时调用就对了
socket的常见API
socket有一个媲美鲁棒性的翻译套接字让人看得云里雾里简直是完美的反自学机制 套接字的本质就是一个文件描述符这个东西非常非常非常重要 socket的第一个参数是标识套接字的类型AF_INET表示IPv4也是最常用的
第二个参数表示通信的类型是使用UDPSOCK_DGRAM还是使用TCPSOCK_STREAM
bind的作用是将这个进程提供的服务绑定到操作系统中当外部访问时就能知道这个服务的端口号当信息发出时也能携带自身的ip和port
listen和accept是TCP通信中需要用到的listen用于开始监听是否有请求accept用于将拿到的请求解析connect则是连接的请求
接下来我们会使用UDP进行主机和服务器之间的通信
UDP实现服务器与客户机通信
首先我们需要知道的是客户机和服务器需要的程序是不一样的因此需要分别实现服务器和客户机的代码
我们需要知道所谓的服务器和客户机不过都是进程
服务器
服务器的代码相对复杂我们分不同文件来说明
// nocopy.hpp
#pragma once
#include iostream
class nocopy
{
public:nocopy() {}nocopy(const nocopy ) delete;const nocopy operator(const nocopy ) delete;~nocopy() {}
};这个类主要是实现一个最简单的单例模式防止服务器进程被拷贝等内容
#pragma once
#include iostream
#include string
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.hclass InetAddr // 将接收到的网络信息格式化
{
public:InetAddr(struct sockaddr_in addr): _addr(addr){_port ntohs(_addr.sin_port); // 将网络字节序转换为主机字节序_ip inet_ntoa(_addr.sin_addr); // 将网络字节序的IP转换为点分十进制的字符串}std::string Ip(){return _ip;}uint16_t Port(){return _port;}std::string PrintDebug() // 输出调试信息{std::string info _ip;info :;info std::to_string(_port);return info;}~InetAddr() {}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};这个类主要是给客户机提供一个存储客户机信息的类用于返回给客户机的请求
#pragma once
#include iostream
#include string
#include cstring
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include nocopy.hpp
#include InetAddr.hppconst static uint16_t defaultport 1141; // 默认端口号
const static uint16_t defaultfd -1; // 默认socket
const static uint16_t defaultsize 1024; // 默认缓冲区大小class UdpServer : public nocopy // 服务器不允许被拷贝简化的单例模式
{
public:UdpServer(uint16_t port 11451): _port(port){}void Init(){// 创建socket文件描述符对其进行初始化_sockfd socket(AF_INET, SOCK_DGRAM, 0); // 使用IPv4UDP协议if (_sockfd 0){perror(socket创建失败);exit(errno);}std::cout socket创建成功,文件描述符为: _sockfd std::endl;// 初始化网络信息struct sockaddr_in local;bzero(local, sizeof(local)); // memsetlocal.sin_family AF_INET; // 协议簇local.sin_port htons(_port); // 端口号local.sin_addr.s_addr INADDR_ANY; // ip地址// 绑定到系统内核int n bind(_sockfd, (struct sockaddr *)local, sizeof(local));if (n ! 0){perror(bind出错);exit(errno);}}void Start() // 服务器不退出{char buffer[1024];for (;;){struct sockaddr_in peer; // 远程地址信息因为需要返回请求socklen_t len sizeof(peer);ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)peer, len); // 获取从从外部接收的数据// 第一个参数是套接字文件描述符标识接收数据的套接字// 第二个参数是接收数据的缓冲区if (n 0) // 正确接收{InetAddr addr(peer); // 格式化网络信息buffer[n] \0; // 手动添加结束std::cout [ addr.PrintDebug() ]# buffer std::endl;sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)peer, len);}}}~UdpServer(){}private:uint16_t _port; // 端口号int _sockfd; // socket文件描述符
};这里就是客户机的主体了里面的代码和注释写的非常详细主要思路就是初始化先听后回复
#include udpserver.hpp
#include memoryvoid Usage(std::string proc) // 使用提示
{std::cout Usage:\n\t proc local_port\n std::endl;
}int main(int argc, char *argv[])
{if (argc ! 2){Usage(argv[0]);return 1;}uint16_t port std::stoi(argv[1]);std::unique_ptrUdpServer usvr std::make_uniqueUdpServer(port);usvr-Init();usvr-Start();return 0;
}这里就是主程序负责判断用户输入是否正确调用服务器
客户机
#include iostream
#include cerrno
#include cstring
#include string
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h
#include netinet/in.hvoid Usage (const std::string proc)
{std::coutUsage:\n\tprocserver_ip server_port std::endl;
}int main(int argc, char* argv[])
{if(argc!3){Usage(argv[0]);return 1; }std::string serverip argv[1];uint16_t serverport std::stoi(argv[2]);// 创建socketint sockfd socket(AF_INET, SOCK_DGRAM, 0);if(sockfd0){perror(socket创建失败);exit(errno);}std::coutsocket创建成功,sockfd:sockfdstd::endl;// 客户机也需要绑定但是不需要主动调用客户机会在第一次发送数据时自动绑定数据// 填充服务器信息struct sockaddr_in server;memset(server, 0,sizeof(server));server.sin_family AF_INET;server.sin_port htons(serverport);server.sin_addr.s_addr inet_addr(serverip.c_str());while(true){// 发送的数据std::string inbuffer;std::coutPlease Enter# ;std::getline(std::cin, inbuffer);// 发送消息请求ssize_t n sendto(sockfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr*)server, sizeof(server));// 收消息if(n0){char buffer[1024];struct sockaddr_in tmp;socklen_t len sizeof(tmp);ssize_t m recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)tmp, len);if(m0){buffer[m] \0;std::coutserver echo# bufferstd::endl;}elsebreak;}elsebreak;} close(sockfd);return 0;
}客户机是先发出请求再接收请求
运行效果如下 使用netstat -tuln可以查到服务器的ip地址和端口当然这里只适合使用本机调试