当前位置: 首页 > news >正文

网站建设要学哪些方面网站建设的设计与实现

网站建设要学哪些方面,网站建设的设计与实现,注册一个公司网站需要多少钱,甘肃省建设厅注册中心网站一、预备知识 1.1 理解源IP地址和目的IP地址 在网络编程中#xff0c;IP地址#xff08;Internet Protocol Address#xff09;是每个连接到互联网的设备的唯一标识符。IP地址可以分为IPv4和IPv6两种类型。IPv4地址是由32位二进制数表示#xff0c;通常分为四个八位组IP地址Internet Protocol Address是每个连接到互联网的设备的唯一标识符。IP地址可以分为IPv4和IPv6两种类型。IPv4地址是由32位二进制数表示通常分为四个八位组以十进制数表示例如192.168.0.1。IPv6地址是由128位二进制数表示以16进制数表示例如2001:0db8:85a3:0000:0000:8a2e:0370:7334。 在IP数据包的头部有两个IP地址分别是源IP地址和目的IP地址。源IP地址表示发送数据包的设备地址而目的IP地址则是接收数据包的设备地址。IP地址的存在使得我们能够在庞大的网络中找到特定的设备就像我们在现实世界中通过邮寄地址找到特定的人或公司一样。 然而光有IP地址并不能完成通信。想象一下发送QQ消息的场景有了IP地址我们可以将消息发送到对方的设备上但这还不足以确定消息应该由哪个程序来解析。这时我们就需要端口号的帮助。端口号和IP地址结合使用可以唯一标识网络中的某个进程从而确保数据包能够正确送达目标应用程序。 在现代网络通信中IP地址的管理和分配由互联网名称与数字地址分配机构ICANN进行。IP地址的分配遵循一定的规则以确保全球唯一性和合理使用。例如私有IP地址如192.168.x.x仅用于局域网内部不能在互联网上使用。公网IP地址则由ISPInternet Service Provider分配用于互联网上的设备通信。 理解IP地址的基础知识是网络编程的第一步。在实际开发中我们经常需要处理IP地址的转换、验证和解析等操作。Python提供了丰富的库函数来帮助我们完成这些任务例如socket库和ipaddress库等。通过这些工具我们可以方便地进行IP地址的处理和操作。 1.2 认识端口号 端口号是网络编程中另一个重要的概念。端口号是传输层协议的一部分用于标识网络上的进程。它是一个16位的整数范围从0到65535每个端口号都唯一对应一个进程从而告诉操作系统当前数据应交给哪个进程处理。 端口号的作用类似于现实生活中的房间号。如果IP地址是一个公寓楼的地址那么端口号就是具体的房间号。通过IP地址我们可以找到公寓楼而通过端口号我们可以找到具体的房间进而找到住在这个房间的人即进程。 端口号分为三类公认端口0-1023、注册端口1024-49151和动态/私有端口49152-65535。公认端口由IANAInternet Assigned Numbers Authority管理通常用于系统服务和知名应用程序例如HTTP使用端口80HTTPS使用端口443。注册端口用于用户应用程序和服务可以由用户自行申请和使用。动态/私有端口则用于临时或私有应用程序不需要注册。 在网络编程中我们常常需要绑定端口号以便服务器能够接收客户端的请求。例如在创建一个TCP服务器时我们需要调用bind函数将套接字绑定到一个特定的IP地址和端口号上。这样当客户端发送请求到这个IP地址和端口号时服务器就能够接收到请求并进行处理。 需要注意的是端口号的使用需要遵循一些基本的原则。首先避免使用已知的公认端口以免与系统服务发生冲突。其次确保端口号唯一性一个端口号只能被一个进程占用。最后注意端口号的范围避免使用保留端口和特殊用途端口。 1.3 理解源端口号和目的端口号 在传输层协议TCP和UDP的数据段中有两个端口号分别是源端口号和目的端口号。源端口号表示发送数据的进程的端口号而目的端口号则表示接收数据的进程的端口号。这两个端口号描述了“数据是谁发的要发给谁”。 例如我们在发送快递时会标明寄件人和收件人的信息。寄件人的信息相当于源端口号而收件人的信息相当于目的端口号。通过源端口号和目的端口号的结合数据包可以在网络中准确地传输到目标进程。 在TCP协议中连接的建立和断开都涉及到源端口号和目的端口号。在三次握手过程中客户端通过源端口号向服务器的目的端口号发送SYN请求服务器通过目的端口号向客户端的源端口号发送SYN-ACK应答客户端再通过源端口号向服务器的目的端口号发送ACK确认从而建立连接。在四次挥手过程中客户端和服务器通过源端口号和目的端口号的交互来完成连接的断开。 在UDP协议中源端口号和目的端口号用于标识数据报的发送方和接收方。由于UDP是无连接协议每个数据报都是独立的源端口号和目的端口号在每个数据报中都是独立存在的不同的数据报可以有不同的端口号。 源端口号和目的端口号的存在使得网络通信更加灵活和高效。通过合理使用端口号我们可以实现多种多样的网络应用和服务。例如在一个网络应用中我们可以使用多个端口号来处理不同类型的请求如HTTP请求、文件传输请求和即时通讯请求等。 1.4 认识TCP协议 TCPTransmission Control Protocol传输控制协议是面向连接的协议提供可靠的数据传输。它通过三次握手建立连接保证数据的完整性和顺序性适用于对传输可靠性要求高的场景。 TCP协议的特点包括 面向连接在传输数据之前必须先建立连接。可靠传输通过序号、确认、重传和超时等机制保证数据的可靠传输。流量控制通过滑动窗口机制控制发送方发送数据的速度避免接收方接收不过来。拥塞控制通过拥塞窗口和慢启动机制控制网络中的数据流量避免网络拥塞。 在TCP协议中数据被分成若干个数据段Segment进行传输。每个数据段都有一个序号用于标识数据在整个数据流中的位置。接收方在接收到数据段后会发送一个确认报文ACK给发送方确认已接收到的数据段。发送方在接收到确认报文后会继续发送后续的数据段。如果发送方在一定时间内没有收到确认报文会进行数据重传直到接收到确认报文为止。 TCP协议的连接建立过程称为三次握手 SYN客户端向服务器发送一个SYN报文表示请求建立连接。SYN-ACK服务器收到SYN报文后回应一个SYN-ACK报文表示同意建立连接。ACK客户端收到SYN-ACK报文后回应一个ACK报文表示连接建立完成。 TCP协议的连接断开过程称为四次挥手 FIN客户端向服务器发送一个FIN报文表示请求断开连接。ACK服务器收到FIN报文后回应一个ACK报文表示同意断开连接。FIN服务器向客户端发送一个FIN报文表示准备断开连接。ACK客户端收到FIN报文后回应一个ACK报文表示断开连接完成。 TCP协议在实际应用中有着广泛的应用例如HTTP、FTP、SMTP等常见的网络协议都基于TCP协议实现。通过理解和掌握TCP协议我们可以更好地进行网络编程开发出高效、可靠的网络应用。 1.5 认识UDP协议 UDPUser Datagram Protocol用户数据报协议是无连接的协议不保证数据的可靠传输。它以数据报的形式发送数据每个数据报都是独立的适用于实时性要求高但对可靠性要求低的场景如视频直播、在线游戏等。 UDP协议的特点包括 无连接在传输数据之前不需要建立连接每个数据报都是独立的。不可靠传输UDP协议不保证数据报的传输成功也不保证数据报的顺序数据可能丢失、重复或乱序。面向数据报UDP协议以数据报的形式发送和接收数据每个数据报都有独立的报头和数据部分。 UDP协议的应用场景非常广泛尤其在实时性要求高的场景中UDP协议的无连接和低开销特点使得其非常适用。例如在线游戏、视频会议和语音通话等应用通常采用UDP协议以确保数据能够快速传输尽可能减少延迟。 在UDP协议中每个数据报都包含源端口号、目的端口号、长度和校验和等信息。源端口号和目的端口号用于标识数据报的发送方和接收方长度表示数据报的长度校验和用于检测数据报在传输过程中是否出现错误。由于UDP协议不提供重传和确认机制数据报在传输过程中可能会丢失或出现错误因此在应用层需要额外的处理逻辑来保证数据的完整性和可靠性。 1.6 网络字节序 在网络中多字节数据的传输顺序有两种大端字节序Big-endian和小端字节序Little-endian。大端字节序是指数据的高字节存储在内存的低地址而小端字节序则是指数据的低字节存储在内存的低地址。不同的计算机体系结构可能采用不同的字节序因此在进行网络传输时需要进行字节序的转换以保证数据在不同体系结构的计算机之间能够正确传输和解析。 TCP/IP协议规定网络数据流采用大端字节序即低地址存储高字节。为了保证不同架构计算机之间的兼容性我们需要进行字节序转换。C语言标准库提供了以下函数来进行字节序的转换 htonlHost to Network Long将32位长整数从主机字节序转换为网络字节序。htonsHost to Network Short将16位短整数从主机字节序转换为网络字节序。ntohlNetwork to Host Long将32位长整数从网络字节序转换为主机字节序。ntohsNetwork to Host Short将16位短整数从网络字节序转换为主机字节序。 这些函数名中的h表示host主机n表示network网络l表示32位长整数s表示16位短整数。例如htonl表示将32位的长整数从主机字节序转换为网络字节序用于在发送数据前对IP地址等数据进行转换。 通过字节序转换函数我们可以确保网络程序具有良好的可移植性使得同样的代码在大端和小端机器上都能正确运行。在实际开发中正确处理字节序是保证网络通信成功的关键步骤。 二、socket编程接口 2.1 socket 常见API socket API是网络编程的基础接口适用于各种底层网络协议如IPv4、IPv6等。以下是常见的socket API函数 int socket(int domain, int type, int protocol);创建一个新的套接字。domain参数指定协议族如AF_INET用于IPv4type参数指定套接字类型如SOCK_STREAM用于TCPprotocol参数指定协议通常为0由系统自动选择。int bind(int socket, const struct sockaddr *address, socklen_t address_len);将套接字绑定到本地地址。address参数指定本地地址和端口号address_len参数指定地址的长度。int listen(int socket, int backlog);将套接字设置为监听模式等待客户端连接。backlog参数指定等待连接队列的最大长度。int accept(int socket, struct sockaddr *address, socklen_t *address_len);接受客户端连接请求。address参数用于存储客户端的地址address_len参数用于存储地址的长度。int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);与服务器建立连接。addr参数指定服务器的地址和端口号addrlen参数指定地址的长度。 这些API函数构成了网络编程的基础通过它们我们可以实现各种网络通信功能。例如使用socket函数创建一个套接字使用bind函数将其绑定到本地地址和端口号使用listen函数将其设置为监听模式使用accept函数接受客户端连接请求使用connect函数与服务器建立连接。 在实际开发中我们经常需要结合使用这些API函数来实现复杂的网络通信功能。例如在实现一个TCP服务器时我们可以使用socket函数创建一个服务器套接字使用bind函数将其绑定到本地地址和端口号使用listen函数将其设置为监听模式然后使用accept函数接受客户端连接请求。在每个连接请求到来时我们可以创建一个新的套接字用于与客户端通信通过这个新的套接字接收和发送数据。 2.2 sockaddr结构 sockaddr结构是网络编程中通用的地址结构不同的网络协议对应不同的地址结构如IPv4地址用sockaddr_in表示IPv6地址用sockaddr_in6表示。通过将特定类型的地址结构转换为sockaddr可以实现代码的通用性。 sockaddr结构的定义如下 struct sockaddr {sa_family_t sa_family; // 地址族如AF_INET用于IPv4char sa_data[14]; // 地址数据实际长度由地址族决定 };对于IPv4地址通常使用sockaddr_in结构该结构的定义如下 struct sockaddr_in {sa_family_t sin_family; // 地址族AF_INET用于IPv4in_port_t sin_port; // 端口号使用网络字节序struct in_addr sin_addr; // IP地址char sin_zero[8]; // 保留字段必须设置为0 };对于IPv6地址通常使用sockaddr_in6结构该结构的定义如下 struct sockaddr_in6 {sa_family_t sin6_family; // 地址族AF_INET6用于IPv6in_port_t sin6_port; // 端口号使用网络字节序uint32_t sin6_flowinfo; // 流量信息struct in6_addr sin6_addr; // IP地址uint32_t sin6_scope_id; // 作用域ID };在实际编程中我们通常将特定类型的地址结构转换为sockaddr结构并在使用时强制转换回原始类型。例如在调用bind函数时我们可以将sockaddr_in结构转换为sockaddr结构 struct sockaddr_in addr; addr.sin_family AF_INET; addr.sin_port htons(8080); addr.sin_addr.s_addr inet_addr(127.0.0.1); memset(addr.sin_zero, 0, sizeof(addr.sin_zero));bind(sockfd, (struct sockaddr*)addr, sizeof(addr));通过这种方式我们可以实现代码的通用性处理不同类型的地址结构同时保证网络通信的正确性和可靠性。 2.3 in_addr结构 in_addr结构用于表示一个IPv4的IP地址其实质是一个32位的整数。in_addr结构的定义如下 struct in_addr {uint32_t s_addr; // IP地址使用网络字节序 };在网络编程中我们通常使用点分十进制的字符串表示IP地址例如192.168.0.1。为了在程序中使用这些字符串表示的IP地址我们需要进行字符串和in_addr结构之间的转换。标准库提供了以下函数来进行这些转换 inet_pton将点分十进制的字符串转换为in_addr结构。inet_ntop将in_addr结构转换为点分十进制的字符串。 以下是这些函数的使用示例 struct in_addr addr; inet_pton(AF_INET, 192.168.0.1, addr);char str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, addr, str, INET_ADDRSTRLEN);通过这些函数我们可以方便地在程序中处理IP地址进行IP地址的转换和解析确保网络通信的正确性。 三、简单的UDP网络程序 3.1 封装 UdpSocket 以下是一个简单的UdpSocket类的实现它封装了UDP套接字的常见操作 #pragma once #include stdio.h #include string.h #include stdlib.h #include cassert #include string #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.htypedef struct sockaddr sockaddr; typedef struct sockaddr_in sockaddr_in;class UdpSocket { public:UdpSocket() : fd_(-1) {}bool Socket() {fd_ socket(AF_INET, SOCK_DGRAM, 0);if (fd_ 0) {perror(socket);return false;}return true;}bool Close() {close(fd_);return true;}bool Bind(const std::string ip, uint16_t port) {sockaddr_in addr;addr.sin_family AF_INET;addr.sin_addr.s_addr inet_addr(ip.c_str());addr.sin_port htons(port);int ret bind(fd_, (sockaddr*)addr, sizeof(addr));if (ret 0) {perror(bind);return false;}return true;}bool RecvFrom(std::string* buf, std::string* ip NULL, uint16_t* port NULL) {char tmp[1024 * 10] {0};sockaddr_in peer;socklen_t len sizeof(peer);ssize_t read_size recvfrom(fd_, tmp, sizeof(tmp) - 1, 0, (sockaddr*)peer, len);if (read_size 0) {perror(recvfrom);return false;}buf-assign(tmp, read_size);if (ip ! NULL) {*ip inet_ntoa(peer.sin_addr);}if (port ! NULL) {*port ntohs(peer.sin_port);}return true;}bool SendTo(const std::string buf, const std::string ip, uint16_t port) {sockaddr_in addr;addr.sin_family AF_INET;addr.sin_addr.s_addr inet_addr(ip.c_str());addr.sin_port htons(port);ssize_t write_size sendto(fd_, buf.data(), buf.size(), 0, (sockaddr*)addr, sizeof(addr));if (write_size 0) {perror(sendto);return false;}return true;}private:int fd_; };3.2 UDP通用服务器 以下是一个通用的UDP服务器类UdpServer的实现 #pragma once #include udp_socket.hpp #include functionaltypedef std::functionvoid (const std::string, std::string* resp) Handler;class UdpServer { public:UdpServer() {assert(sock_.Socket());}~UdpServer() {sock_.Close();}bool Start(const std::string ip, uint16_t port, Handler handler) {bool ret sock_.Bind(ip, port);if (!ret) {return false;}for (;;) {std::string req;std::string remote_ip;uint16_t remote_port 0;bool ret sock_.RecvFrom(req, remote_ip, remote_port);if (!ret) {continue;}std::string resp;handler(req, resp);sock_.SendTo(resp, remote_ip, remote_port);printf([%s:%d] req: %s, resp: %s\n, remote_ip.c_str(), remote_port, req.c_str(), resp.c_str());}sock_.Close();return true;}private:UdpSocket sock_; };基于以上封装我们可以实现一个简单的英译汉服务器 #include udp_server.hpp #include unordered_map #include iostreamstd::unordered_mapstd::string, std::string g_dict;void Translate(const std::string req, std::string* resp) {auto it g_dict.find(req);if (it g_dict.end()) {*resp 未查到!;return;}*resp it-second; }int main(int argc, char* argv[]) {if (argc ! 3) {printf(Usage ./dict_server [ip] [port]\n);return 1;}g_dict.insert(std::make_pair(hello, 你好));g_dict.insert(std::make_pair(world, 世界));g_dict.insert(std::make_pair(c, 最好的编程语言));g_dict.insert(std::make_pair(bit, 特别NB));UdpServer server;server.Start(argv[1], atoi(argv[2]), Translate);return 0; }3.3 UDP通用客户端 以下是一个通用的UDP客户端类UdpClient的实现 #pragma once #include udp_socket.hppclass UdpClient { public:UdpClient(const std::string ip, uint16_t port) : ip_(ip), port_(port) {assert(sock_.Socket());}~UdpClient() {sock_.Close();}bool RecvFrom(std::string* buf) {return sock_.RecvFrom(buf);}bool SendTo(const std::string buf) {return sock_.SendTo(buf, ip_, port_);}private:UdpSocket sock_;std::string ip_;uint16_t port_; };基于以上封装我们可以实现一个简单的英译汉客户端 #include udp_client.hpp #include iostreamint main(int argc, char* argv[]) {if (argc ! 3) {printf(Usage ./dict_client [ip] [port]\n);return 1;}UdpClient client(argv[1], atoi(argv[2]));for (;;) {std::string word;std::cout 请输入您要查的单词: ;std::cin word;if (!std::cin) {std::cout Good Bye std::endl;break;}client.SendTo(word);std::string result;client.RecvFrom(result);std::cout word 意思是 result std::endl;}return 0; }四、简单的TCP网络程序 4.1 TCP socket API 详解 TCPTransmission Control Protocol是一种面向连接的、可靠的、基于字节流的传输层通信协议。以下是TCP socket API的详细介绍 int socket(int domain, int type, int protocol);创建一个新的TCP套接字。domain参数指定协议族IPv4使用AF_INETtype参数指定套接字类型TCP使用SOCK_STREAM。int bind(int socket, const struct sockaddr *address, socklen_t address_len);将套接字绑定到本地地址和端口号。int listen(int socket, int backlog);将套接字设置为监听模式backlog参数指定等待连接队列的最大长度。int accept(int socket, struct sockaddr *address, socklen_t *address_len);接受客户端连接请求返回一个新的套接字用于与客户端通信。int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);与服务器建立连接。 这些API函数构成了TCP网络编程的基础通过它们我们可以实现各种网络通信功能。例如使用socket函数创建一个套接字使用bind函数将其绑定到本地地址和端口号使用listen函数将其设置为监听模式使用accept函数接受客户端连接请求使用connect函数与服务器建立连接。 在实际开发中我们经常需要结合使用这些API函数来实现复杂的网络通信功能。例如在实现一个TCP服务器时我们可以使用socket函数创建一个服务器套接字使用bind函数将其绑定到本地地址和端口号使用listen函数将其设置为监听模式然后使用accept函数接受客户端连接请求。在每个连接请求到来时我们可以创建一个新的套接字用于与客户端通信通过这个新的套接字接收和发送数据。 4.2 封装 TCP socket 以下是一个简单的TCP套接字封装类TcpSocket的实现 #pragma once #include stdio.h #include string.h #include stdlib.h #include string #include cassert #include unistd.h #include sys/socket.h #include netinet/in.h#include arpa/inet.h #include fcntl.htypedef struct sockaddr sockaddr; typedef struct sockaddr_in sockaddr_in;#define CHECK_RET(exp) if (!(exp)) { return false; }class TcpSocket { public:TcpSocket() : fd_(-1) {}TcpSocket(int fd) : fd_(fd) {}bool Socket() {fd_ socket(AF_INET, SOCK_STREAM, 0);if (fd_ 0) {perror(socket);return false;}printf(open fd %d\n, fd_);return true;}bool Close() const {close(fd_);printf(close fd %d\n, fd_);return true;}bool Bind(const std::string ip, uint16_t port) const {sockaddr_in addr;addr.sin_family AF_INET;addr.sin_addr.s_addr inet_addr(ip.c_str());addr.sin_port htons(port);int ret bind(fd_, (sockaddr*)addr, sizeof(addr));if (ret 0) {perror(bind);return false;}return true;}bool Listen(int num) const {int ret listen(fd_, num);if (ret 0) {perror(listen);return false;}return true;}bool Accept(TcpSocket* peer, std::string* ip NULL, uint16_t* port NULL) const {sockaddr_in peer_addr;socklen_t len sizeof(peer_addr);int new_sock accept(fd_, (sockaddr*)peer_addr, len);if (new_sock 0) {perror(accept);return false;}printf(accept fd %d\n, new_sock);peer-fd_ new_sock;if (ip ! NULL) {*ip inet_ntoa(peer_addr.sin_addr);}if (port ! NULL) {*port ntohs(peer_addr.sin_port);}return true;}bool Recv(std::string* buf) const {buf-clear();char tmp[1024 * 10] {0};ssize_t read_size recv(fd_, tmp, sizeof(tmp), 0);if (read_size 0) {perror(recv);return false;}if (read_size 0) {return false;}buf-assign(tmp, read_size);return true;}bool Send(const std::string buf) const {ssize_t write_size send(fd_, buf.data(), buf.size(), 0);if (write_size 0) {perror(send);return false;}return true;}bool Connect(const std::string ip, uint16_t port) const {sockaddr_in addr;addr.sin_family AF_INET;addr.sin_addr.s_addr inet_addr(ip.c_str());addr.sin_port htons(port);int ret connect(fd_, (sockaddr*)addr, sizeof(addr));if (ret 0) {perror(connect);return false;}return true;}int GetFd() const {return fd_;}private:int fd_; };4.3 TCP通用服务器 以下是一个通用的TCP服务器类TcpServer的实现 #pragma once #include functional #include tcp_socket.hpptypedef std::functionvoid (const std::string req, std::string* resp) Handler;class TcpServer { public:TcpServer(const std::string ip, uint16_t port) : ip_(ip), port_(port) {}bool Start(Handler handler) {CHECK_RET(listen_sock_.Socket());CHECK_RET(listen_sock_.Bind(ip_, port_));CHECK_RET(listen_sock_.Listen(5));for (;;) {TcpSocket new_sock;std::string ip;uint16_t port 0;if (!listen_sock_.Accept(new_sock, ip, port)) {continue;}printf([client %s:%d] connect!\n, ip.c_str(), port);for (;;) {std::string req;bool ret new_sock.Recv(req);if (!ret) {printf([client %s:%d] disconnect!\n, ip.c_str(), port);new_sock.Close();break;}std::string resp;handler(req, resp);new_sock.Send(resp);printf([%s:%d] req: %s, resp: %s\n, ip.c_str(), port, req.c_str(), resp.c_str());}}return true;}private:TcpSocket listen_sock_;std::string ip_;uint64_t port_; };4.4 英译汉服务器 基于以上封装我们可以实现一个简单的英译汉服务器 #include unordered_map #include tcp_server.hppstd::unordered_mapstd::string, std::string g_dict;void Translate(const std::string req, std::string* resp) {auto it g_dict.find(req);if (it g_dict.end()) {*resp 未找到;return;}*resp it-second; }int main(int argc, char* argv[]) {if (argc ! 3) {printf(Usage ./dict_server [ip] [port]\n);return 1;}g_dict.insert(std::make_pair(hello, 你好));g_dict.insert(std::make_pair(world, 世界));g_dict.insert(std::make_pair(bit, 贼NB));TcpServer server(argv[1], atoi(argv[2]));server.Start(Translate);return 0; }五、简单的TCP网络程序多进程版本 通过每个请求创建子进程的方式来支持多连接我们可以实现一个多进程版本的TCP服务器。以下是TcpProcessServer的实现 #pragma once #include functional #include signal.h #include tcp_socket.hpptypedef std::functionvoid (const std::string req, std::string* resp) Handler;class TcpProcessServer { public:TcpProcessServer(const std::string ip, uint16_t port) : ip_(ip), port_(port) {signal(SIGCHLD, SIG_IGN); // 忽略子进程的退出信号避免产生僵尸进程}void ProcessConnect(const TcpSocket new_sock, const std::string ip, uint16_t port, Handler handler) {int ret fork(); // 创建子进程if (ret 0) {// 父进程new_sock.Close();return;} else if (ret 0) {// 子进程for (;;) {std::string req;bool ret new_sock.Recv(req);if (!ret) {printf([client %s:%d] disconnected!\n, ip.c_str(), port);exit(0);}std::string resp;handler(req, resp);new_sock.Send(resp);printf([client %s:%d] req: %s, resp: %s\n, ip.c_str(), port, req.c_str(), resp.c_str());}} else {perror(fork);}}bool Start(Handler handler) {CHECK_RET(listen_sock_.Socket());CHECK_RET(listen_sock_.Bind(ip_, port_));CHECK_RET(listen_sock_.Listen(5));for (;;) {TcpSocket new_sock;std::string ip;uint16_t port 0;if (!listen_sock_.Accept(new_sock, ip, port)) {continue;}printf([client %s:%d] connect!\n, ip.c_str(), port);ProcessConnect(new_sock, ip, port, handler);}return true;}private:TcpSocket listen_sock_;std::string ip_;uint64_t port_; };5.1 多进程版本的实现分析 在上述实现中我们通过fork()系统调用为每个新的客户端连接创建一个子进程。父进程负责监听和接受新的连接请求而子进程则负责处理与客户端的具体通信。这样通过多进程的方式我们可以支持多个客户端同时连接和通信。 优点 并发处理能力强每个连接由独立的进程处理可以充分利用多核CPU的性能。进程间隔离性强进程之间相互独立一个进程的崩溃不会影响到其他进程增加了系统的稳定性。 缺点 资源开销大每创建一个子进程操作系统都需要分配独立的内存和资源资源开销较大。上下文切换开销大进程之间的上下文切换比线程之间的上下文切换开销大影响性能。 在实际开发中使用多进程还是多线程需要根据具体场景进行选择。如果系统资源充足且需要高隔离性可以选择多进程模式如果需要高并发且资源受限可以选择多线程模式。 六、简单的TCP网络程序多线程版本 通过每个请求创建一个线程的方式来支持多连接我们可以实现一个多线程版本的TCP服务器。以下是TcpThreadServer的实现 #pragma once #include functional #include pthread.h #include tcp_socket.hpptypedef std::functionvoid (const std::string, std::string*) Handler;struct ThreadArg {TcpSocket new_sock;std::string ip;uint16_t port;Handler handler; };class TcpThreadServer { public:TcpThreadServer(const std::string ip, uint16_t port) : ip_(ip), port_(port) {}bool Start(Handler handler) {CHECK_RET(listen_sock_.Socket());CHECK_RET(listen_sock_.Bind(ip_, port_));CHECK_RET(listen_sock_.Listen(5));for (;;) {ThreadArg* arg new ThreadArg();arg-handler handler;bool ret listen_sock_.Accept(arg-new_sock, arg-ip, arg-port);if (!ret) {continue;}printf([client %s:%d] connect\n, arg-ip.c_str(), arg-port);pthread_t tid;pthread_create(tid, NULL, ThreadEntry, arg);pthread_detach(tid); // 分离线程自动回收资源}return true;}static void* ThreadEntry(void* arg) {ThreadArg* p reinterpret_castThreadArg*(arg);ProcessConnect(p);p-new_sock.Close();delete p;return NULL;}static void ProcessConnect(ThreadArg* arg) {for (;;) {std::string req;bool ret arg-new_sock.Recv(req);if (!ret) {printf([client %s:%d] disconnected!\n, arg-ip.c_str(), arg-port);break;}std::string resp;arg-handler(req, resp);arg-new_sock.Send(resp);printf([client %s:%d] req: %s, resp: %s\n, arg-ip.c_str(), arg-port, req.c_str(), resp.c_str());}}private:TcpSocket listen_sock_;std::string ip_;uint16_t port_; };6.1 多线程版本的实现分析 在上述实现中我们通过pthread_create()系统调用为每个新的客户端连接创建一个线程。主线程负责监听和接受新的连接请求而新创建的线程则负责处理与客户端的具体通信。这样通过多线程的方式我们可以支持多个客户端同时连接和通信。 优点 并发处理能力强每个连接由独立的线程处理可以充分利用多核CPU的性能。资源开销小线程共享进程的内存和资源相比多进程模式资源开销较小。上下文切换开销小线程之间的上下文切换比进程之间的上下文切换开销小性能更高。 缺点 线程安全问题多个线程共享进程的内存和资源需要注意线程安全问题可能导致数据竞争和死锁等问题。线程数量有限系统对线程的数量有一定限制过多的线程可能导致资源耗尽影响系统稳定性。 在实际开发中使用多线程模式可以在一定程度上提高并发处理能力但需要注意线程安全问题合理控制线程数量避免资源耗尽。 七、TCP协议通讯流程 7.1 建立连接的过程 TCP协议的连接建立过程称为三次握手 SYN客户端向服务器发送一个SYN报文表示请求建立连接。SYN-ACK服务器收到SYN报文后回应一个SYN-ACK报文表示同意建立连接。ACK客户端收到SYN-ACK报文后回应一个ACK报文表示连接建立完成。 在三次握手过程中双方需要交换序列号和确认号以确保连接的可靠性和数据的有序性。具体过程如下 客户端发送SYN报文设置序列号为x。服务器收到SYN报文发送SYN-ACK报文设置序列号为y确认号为x1。客户端收到SYN-ACK报文发送ACK报文确认号为y1。 通过三次握手客户端和服务器建立了可靠的连接后续可以进行数据传输。 7.2 数据传输的过程 TCP协议提供全双工通信服务通信双方可以同时发送和接收数据。服务器从accept()返回后可以调用read()读取客户端发送的数据同时客户端可以调用write()发送请求。 在数据传输过程中TCP协议通过序列号和确认号确保数据的有序性和完整性。具体过程如下 发送方将数据分成若干数据段每个数据段都有一个序列号。接收方收到数据段后发送一个确认报文ACK确认号为已接收到的数据段的序列号加1。发送方在接收到确认报文后继续发送后续的数据段。如果发送方在一定时间内没有收到确认报文会进行数据重传直到接收到确认报文为止。 通过这种方式TCP协议保证了数据的可靠传输避免数据丢失和重复。 7.3 断开连接的过程 TCP协议的连接断开过程称为 四次挥手 FIN客户端向服务器发送一个FIN报文表示请求断开连接。ACK服务器收到FIN报文后回应一个ACK报文表示同意断开连接。FIN服务器向客户端发送一个FIN报文表示准备断开连接。ACK客户端收到FIN报文后回应一个ACK报文表示断开连接完成。 在四次挥手过程中双方需要交换序列号和确认号以确保连接的可靠断开。具体过程如下 客户端发送FIN报文设置序列号为x。服务器收到FIN报文发送ACK报文确认号为x1。服务器发送FIN报文设置序列号为y。客户端收到FIN报文发送ACK报文确认号为y1。 通过四次挥手客户端和服务器断开了连接释放了相关资源。 八、TCP 和 UDP 对比 8.1 可靠传输 vs 不可靠传输 TCP提供可靠的数据传输通过确认和重传机制保证数据的完整性和顺序性UDP则不保证数据的传输可靠性数据可能丢失、重复或乱序。 TCP通过序列号、确认号和重传机制确保数据在传输过程中不丢失、不重复和按顺序到达接收方。这使得TCP适用于对传输可靠性要求高的场景如文件传输、电子邮件等。 UDP则不提供这些保证数据报在传输过程中可能会丢失或乱序。因此UDP适用于对实时性要求高但对传输可靠性要求低的场景如视频直播、在线游戏等。 8.2 有连接 vs 无连接 TCP是面向连接的协议在传输数据前需要建立连接UDP是无连接的协议每个数据报都是独立的不需要建立连接。 TCP在传输数据前通过三次握手建立连接确保双方可以进行可靠的数据传输。建立连接后双方可以通过连接进行全双工通信传输数据和确认报文。 UDP则不需要建立连接每个数据报都是独立的可以直接发送和接收。由于不需要建立连接UDP的传输开销较小适用于对实时性要求高的场景。 8.3 字节流 vs 数据报 TCP是面向字节流的协议数据被视为连续的字节流进行传输UDP是面向数据报的协议每个数据报都是一个独立的消息。 TCP在传输数据时将数据分成若干数据段每个数据段都有一个序列号。接收方在接收到数据段后将其按序号排列成连续的字节流确保数据的有序性和完整性。 UDP则将数据分成若干数据报每个数据报都是一个独立的消息。接收方在接收到数据报后可以直接处理每个数据报而不需要按顺序排列。这使得UDP适用于对实时性要求高但对有序性和完整性要求低的场景。 九、总结 通过本文我们详细介绍了网络编程套接字的基础知识包括IP地址、端口号、TCP和UDP协议、网络字节序等内容。随后我们通过封装的方式实现了简单的UDP和TCP网络程序并探讨了多进程和多线程版本的实现方式。最后我们详细讲解了TCP协议的连接建立、数据传输和连接断开过程以及TCP和UDP协议的对比。 在实际开发中选择使用TCP还是UDP取决于具体的应用场景和需求。如果需要可靠的数据传输和连接控制可以选择TCP协议如果需要低延迟和高实时性可以选择UDP协议。通过理解和掌握这些基础知识和编程技巧我们可以更好地进行网络编程开发出高效、可靠的网络应用。 嗯就是这样啦文章到这里就结束啦真心感谢你花时间来读。 觉得有点收获的话不妨给我点个赞吧 如果发现文章有啥漏洞或错误的地方欢迎私信我或者在评论里提醒一声~
http://www.w-s-a.com/news/40913/

相关文章:

  • 塔式服务器主机建网站定制美瞳网站建设
  • 网站是先解析后备案吗永久免费网站模板
  • wordpress站点演示php根据ip 跳转网站
  • 东莞市凤岗建设局网站网站开发有哪些职位
  • 企业网站手机版模板免费下载辣条网站建设书
  • 南昌网站建设维护vc 做网站源码
  • 网站动态logo怎么做织梦移动端网站怎么做
  • 三亚城乡建设局网站app下载安装官方网站
  • 公司被其它人拿来做网站郑州哪家做网站最好
  • 山东省建设厅官方网站抖音代运营业务介绍
  • 网站制作 牛商网wordpress商城 微信支付
  • 平面设计培训网站建文帝网站建设
  • python网站建设佛山乐从网站建设
  • 网站 免费 托管运营app软件大全
  • 爱网站找不到了网站设计制作要交印花税
  • 分销平台是什么意思网站如何从行为数据进行优化
  • 做网站公司职务做民俗酒店到哪些网站推荐
  • 从0到建网站wordpress导航主题模板下载地址
  • 以3d全景做的网站统计网站的代码
  • 北辰网站建设WordPress换主题文件夹
  • 做网站的合同范文百度分析工具
  • 深圳企业网站制作公司单位注册wordpress发送邮件
  • 兰州专业网站建设团队wordpress 拉取点击数
  • 基于php房产网站开发ppt模板免费下载第一ppt
  • 网站盈利模式分析怎么做山东营销网站建设联系方式
  • 二级网站建设 知乎我的个人主页模板
  • wordpress小说网站模板下载地址百度优化服务
  • 云南网页设计制作seo计费系统源码
  • 屏蔽ip网站吗行业外贸网站建设
  • 河北城乡建设学校网站常州网站建设公司平台