网站访问跳出率,有网站有安全狗进不去了,如何夸奖一个网站做的好,代推广平台文章目录 前言一. 服务器1. 初始化服务器2. 启动服务器 二. 客户端三. 多进程服务器结束语 前言
本系列文章是计算机网络学习的笔记#xff0c;欢迎大佬们阅读#xff0c;纠错#xff0c;分享相关知识。希望可以与你共同进步。
本篇博客基于UDP socket基础#xff0c;介绍… 文章目录 前言一. 服务器1. 初始化服务器2. 启动服务器 二. 客户端三. 多进程服务器结束语 前言
本系列文章是计算机网络学习的笔记欢迎大佬们阅读纠错分享相关知识。希望可以与你共同进步。
本篇博客基于UDP socket基础介绍TCP socket编程接口和细节
UDP socket编程可参看【计算机网络学习之路】UDP socket编程
本次编写的服务器和客户端依然是最简单的echo服务器
一. 服务器
服务器的基本框架
tcp_server.hpp
#pragma once#include iostream
#include string
#include cstring
#include cerrno
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.hnamespace ns_server
{const uint16_t default_port 8888;class TcpServer{public:TcpServer(uint16_t port default_port) : _port(port){}void InitServer(){}//初始化服务器void Start(){}//启动服务器~TcpServer(){}private:int _sock; // 监听套接字uint16_t _port; // 端口号};
}tcp_server.cc
#includetcp_server.hpp
#includememoryusing namespace std;
using namespace ns_server;static void usage(char*argv)
{coutUsage\n\targv serverPortendl;
}
int main(int argc,char*argv[])
{if(argc!2){usage(argv[0]);exit(USAGE_ERR);}uint16_t portatoi(argv[1]);unique_ptrTcpServer usvr(new TcpServer(echo,port));usvr-InitServer();usvr-Start();return 0;
}1. 初始化服务器
服务器的初始化还是一样的
创建套接字绑定套接字 void InitServer()
{// 1.创建套接字_sock socket(AF_INET, SOCK_STREAM, 0);if (_listensock 0){std::cerr create sock error, strerror(errno) std::endl;exit(1);}std::cout create listensock success: _sock std::endl;// 2.绑定套接字struct sockaddr_in local;local.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr INADDR_ANY;if (bind(_sock , (struct sockaddr *)local, sizeof(local)) 0){std::cerr bind error, strerror(errno) std::endl;exit(2);}
}需要注意的是socket的第二个参数为SOCK_STREAM面向字节流
TCP与UDP不同的地方是TCP是面向连接的UDP是无连接的 所以TCP还需要listen 返回值成功返回0失败返回-1并设置错误码
backlog参数需要在后续TCP详解中学习先定义大小为32
const int backlog 32;
void InitServer()
{// 1.创建套接字_sock socket(AF_INET, SOCK_STREAM, 0);if (_listensock 0){std::cerr create sock error, strerror(errno) std::endl;exit(1);}std::cout create listensock success: _sock std::endl;// 2.绑定套接字struct sockaddr_in local;local.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr INADDR_ANY;if (bind(_sock , (struct sockaddr *)local, sizeof(local)) 0){std::cerr bind error, strerror(errno) std::endl;exit(2);}// 3.监听if (listen(_listensock, backlog) 0){std::cerr listen error, strerror(errno) std::endl;exit(3);}
}初始化到此就结束了 接下来是启动服务器
2. 启动服务器
TCP通过accept获取客户端连接 sockfdsocket返回的文件描述符addr输入输出型参数客户端信息的结构体addrlen输入输出型参数结构体大小。注意需要传入addr的大小返回值是网络文件描述符
在TCP中socket返回的网络文件可以理解为连接文件内部保存了连接信息 而accept是从连接文件中获取连接然后创建套接字网络文件。 真正通信的是connect创建的网络文件
我们将私有成员的_sock改为_listensock
void Start()
{while (true){struct sockaddr_in client;memset(client, 0, sizeof(client));socklen_t len sizeof(client);int sock accept(_listensock, (struct sockaddr *)client, len);if (sock 0){std::cerr accept error std::endl;continue;}// 提取客户端信息std::string clientIp inet_ntoa(client.sin_addr);uint16_t clientPort ntohs(client.sin_port);std::string name [ clientIp : std::to_string(clientPort) ];std::cout create sock sock from _listensock std::endl;}
}接下来就可以在connect返回的套接字中读写数据了。 本次使用read和write
void Start()
{while (true){struct sockaddr_in client;memset(client, 0, sizeof(client));socklen_t len sizeof(client);int sock accept(_listensock, (struct sockaddr *)client, len);if (sock 0){std::cerr accept error std::endl;continue;}// 提取客户端信息std::string clientIp inet_ntoa(client.sin_addr);uint16_t clientPort ntohs(client.sin_port);std::string name [ clientIp : std::to_string(clientPort) ];std::cout create sock sock from _listensock std::endl;char buffer[1024];while (true){int n read(sock, buffer, sizeof(buffer) - 1);if (n 0){buffer[n] \0;std::cout name # buffer std::endl;std::string responce buffer;//返回收到的数据int m write(sock, responce.c_str(), responce.size());}else if (n 0){// 写端关闭std::cout name quit,me to std::endl;close(sock);break;}else{// 读数据异常std::cerr read error std::endl;break;}}}
}完整代码 tcp_server.hpp
#pragma once#include iostream
#include string
#include cstring
#include cerrno
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.hnamespace ns_server
{const uint16_t default_port 8888;const int backlog 32;class TcpServer{public:TcpServer(func_t func, uint16_t port default_port) : _port(port), _func(func){}void InitServer(){// 1.创建套接字_listensock socket(AF_INET, SOCK_STREAM, 0);if (_listensock 0){std::cerr create sock error, strerror(errno) std::endl;exit(1);}std::cout create listensock success: _listensock std::endl;// 2.绑定套接字struct sockaddr_in local;local.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr INADDR_ANY;if (bind(_listensock, (struct sockaddr *)local, sizeof(local)) 0){std::cerr bind error, strerror(errno) std::endl;exit(2);}// 3.监听if (listen(_listensock, backlog) 0){std::cerr listen error, strerror(errno) std::endl;exit(3);}}void Start(){while (true){struct sockaddr_in client;memset(client, 0, sizeof(client));socklen_t len sizeof(client);int sock accept(_listensock, (struct sockaddr *)client, len);if (sock 0){std::cerr accept error std::endl;continue;}// 提取客户端信息std::string clientIp inet_ntoa(client.sin_addr);uint16_t clientPort ntohs(client.sin_port);std::string name [ clientIp : std::to_string(clientPort) ];std::cout create sock sock from _listensock std::endl;char buffer[1024];while (true){int n read(sock, buffer, sizeof(buffer) - 1);if (n 0){buffer[n] \0;std::cout name # buffer std::endl;std::string responce buffer;int m write(sock, responce.c_str(), responce.size());}else if (n 0){// 写端关闭std::cout name quit,me to std::endl;close(sock);break;}else{// 读数据异常std::cerr read error std::endl;break;}}}}~TcpServer(){}private:int _listensock; // 监听套接字uint16_t _port; // 端口号};
}PS上述的服务器是单进程所以只能同时处理一个客户端读者可以尝试添加一下多进程多线程或者线程池
本篇博客最后会贴出多进程的方案
二. 客户端
客户端就不作封装了
最开始也是要创建套接字 然后TCP的客户端需要connect服务器 sockfdsocket返回的文件描述符addr服务器信息的结构体addrlen结构体大小。返回值成功返回0失败返回-1并设置错误码
注意connect时OS会bind客户端 UDP是在发送数据时才会bind
#include iostream
#include string
#include unistd.h
#include cstring
#include cerrno
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.husing namespace std;static void usage(char *argv)
{cout Usage:\n\t argv serverIp serverPort endl;
}int main(int argc, char *argv[])
{if (argc ! 3){usage(argv[0]);exit(USAGE_ERR);}string serverIp argv[1];uint16_t serverPort atoi(argv[2]);// 1.创建套接字int sock socket(AF_INET, SOCK_STREAM, 0);if (sock 0){cerr create sock error, strerror(errno) endl;exit(SOCKET_ERR);}coutcreate sock sucess:sockendl;// 2. 连接struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_addr.s_addr inet_addr(serverIp.c_str());server.sin_port htons(serverPort);int cnt 5; // 记录重连次数// connect时会bindwhile (connect(sock, (struct sockaddr *)server, sizeof(server)) 0){cout 正在重连...还有 cnt-- 次 endl;if (cnt 0){ cerr连接失败endl;exit(CONNECT_ERR);}sleep(1);}// 连接成功string name [serverIp : to_string(serverPort)];cout connect name sucess endl;return 0;
}然后也可以开始读写数据了
完整代码
tcp_client.cc
#include iostream
#include string
#include unistd.h
#include cstring
#include cerrno
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.husing namespace std;static void usage(char *argv)
{cout Usage:\n\t argv serverIp serverPort endl;
}int main(int argc, char *argv[])
{if (argc ! 3){usage(argv[0]);exit(USAGE_ERR);}string serverIp argv[1];uint16_t serverPort atoi(argv[2]);// 1.创建套接字int sock socket(AF_INET, SOCK_STREAM, 0);if (sock 0){cerr create sock error, strerror(errno) endl;exit(SOCKET_ERR);}coutcreate sock sucess:sockendl;// 2. 连接struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_addr.s_addr inet_addr(serverIp.c_str());server.sin_port htons(serverPort);int cnt 5; // 记录重连次数// connect时会bindwhile (connect(sock, (struct sockaddr *)server, sizeof(server)) 0){cout 正在重连...还有 cnt-- 次 endl;if (cnt 0){ cerr连接失败endl;exit(CONNECT_ERR);}sleep(1);}// 连接成功string name [serverIp : to_string(serverPort)];cout connect name sucess endl;// 发送消息while (true){cout please enter your message# ;string message;getline(cin, message);int n write(sock, message.c_str(), message.size());if (n 0){cerr write error, strerror(errno) endl;break;}else if (n 0){cout 读端关闭停止写 endl;break;}char buffer[1024];int m read(sock, buffer, sizeof(buffer) - 1);if (m 0){buffer[n] \0;coutname echo bufferendl;}else if (m 0){// 写端关闭std::cout name quit,me to std::endl;close(sock);break;}else{// 读数据异常std::cerr read error std::endl;break;}}return 0;
}三. 多进程服务器
tcp_server.hpp
#pragma once#include iostream
#include string
#include cstring
#include cerrno
#include unistd.h
#include signal.h
#include sys/wait.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.hnamespace ns_server
{const uint16_t default_port 8888;const int backlog 32;class TcpServer{public:TcpServer(uint16_t port default_port) : _port(port){}void InitServer(){// 1.创建套接字_listensock socket(AF_INET, SOCK_STREAM, 0);if (_listensock 0){std::cerr create sock error, strerror(errno) std::endl;exit(1);}std::cout create listensock success: _listensock std::endl;// 2.绑定套接字struct sockaddr_in local;local.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr INADDR_ANY;if (bind(_listensock, (struct sockaddr *)local, sizeof(local)) 0){std::cerr bind error, strerror(errno) std::endl;exit(2);}// 3.监听if (listen(_listensock, backlog) 0){std::cerr listen error, strerror(errno) std::endl;exit(3);}}void Start(){//忽略子进程的信号不需要等待子进程退出推荐signal(SIGCHLD,SIG_IGN);while (true){struct sockaddr_in client;memset(client, 0, sizeof(client));socklen_t len sizeof(client);int sock accept(_listensock, (struct sockaddr *)client, len);if (sock 0){std::cerr accept error std::endl;continue;}std::cout create sock sock from _listensock std::endl;// 多进程pid_t id fork();if (id 0){close(sock);continue;}else if (id 0){//子进程close(_listensock);//建议关掉不需要的fdif(fork()0)exit(0);//子进程退掉后续为孙子进程// 提取客户端信息std::string clientIp inet_ntoa(client.sin_addr);uint16_t clientPort ntohs(client.sin_port);service(sock, clientIp, clientPort);exit(0);}//父进程//一定关掉不需要的fd防止fd泄露close(sock);//pid_t retwaitpid(id,nullptr,0);//默认为阻塞等待//pid_t retwaitpid(id,nullptr,WNOHANG);//非阻塞//if(retid) std::coutwait id sucessstd::endl;}}void service(int sock, std::string clientIp, uint16_tclientPort){std::string name [ clientIp : std::to_string(clientPort) ];char buffer[1024];while (true){int n read(sock, buffer, sizeof(buffer) - 1);if (n 0){buffer[n] \0;std::cout name # buffer std::endl;std::string responce buffer;int m write(sock, responce.c_str(), responce.size());}else if (n 0){// 写端关闭std::cout name quit,me to std::endl;close(sock);break;}else{// 读数据异常std::cerr read error std::endl;break;}}}~TcpServer(){}private:int _listensock; // 监听套接字uint16_t _port; // 端口号};
}结束语
本篇博客到此结束感谢看到此处。 欢迎大家纠错和补充 如果觉得本篇文章对你有所帮助的话不妨点个赞支持一下博主拜托啦这对我真的很重要。