1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Linux网络编程 | Socket编程(二)TCPSocket的封装 TCP服务器多进程 多线程版本的实现

Linux网络编程 | Socket编程(二)TCPSocket的封装 TCP服务器多进程 多线程版本的实现

时间:2023-12-27 14:25:09

相关推荐

Linux网络编程 | Socket编程(二)TCPSocket的封装 TCP服务器多进程 多线程版本的实现

目录

TCP的通信流程TCPSocket的封装TCP客户端TCP服务器多进程版本多线程版本

TCP的通信流程

计算机网络 (三) 传输层 :一文搞懂UDP与TCP协议

在这篇博客中,我描述了UDP与TCP的特性以及通信流程,下面就根据特性来规划该如何通过Socket来实现UDP通信。

TCPSocket的封装

为了使用更方便,先封装一个TCPSocket

#include<iostream>#include<string>#include<unistd.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>const int MAX_LISTEN = 5;inline void CheckSafe(bool ret){if(ret == false){exit(0);}}class TcpSocket{public:TcpSocket() : _socket_fd(-1){}//创建套接字bool Socket(){_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(_socket_fd < 0){std::cerr << "socket create error" << std::endl;return false;}return true;}//绑定地址信息bool Bind(const std::string& ip, uint16_t& port){struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(sockaddr_in);int ret = bind(_socket_fd, (sockaddr*)&addr, len);if(ret < 0){std::cerr << "bind error" << std::endl;return false;}return true;}//监听bool Listen(int backlog = MAX_LISTEN){//用初始的套接字开始监听int ret = listen(_socket_fd, backlog);if(ret < 0){std::cerr << "connect error" << std::endl;}return true;}//新建连接bool Accept(TcpSocket *new_sock, std::string* ip = NULL, uint16_t* port = NULL){struct sockaddr_in addr;socklen_t len = sizeof(sockaddr_in);//创建一个新的套接字与客户端建立连接int new_fd = accept(_socket_fd, (sockaddr*)&addr, &len);if(new_fd < 0){std::cerr << "accept error" << std::endl;}new_sock->_socket_fd = new_fd;if(ip != NULL){*ip = inet_ntoa(addr.sin_addr);}if(port != NULL){*port = ntohs(addr.sin_port);}return true;}//发起连接请求bool Connect(const std::string& ip, uint16_t port){struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(sockaddr_in);int ret = connect(_socket_fd, (sockaddr*)&addr, len);if(ret < 0){std::cerr << "connect error" << std::endl;}return true;}//发送数据bool Send(const std::string& data){int ret = send(_socket_fd, data.c_str(), data.size(), 0); if(ret < 0){std::cerr << "send error" << std::endl;}return true;}//接收数据bool Recv(std::string& data){char buff[4096] = {0 };int ret = recv(_socket_fd, buff, 4096, 0);if(ret == 0){std::cerr << "connect error" << std::endl;return false;}else if(ret < 0){std::cerr << "recv error" << std::endl;return false;}data.assign(buff, ret);return true;}void Close(){if(_socket_fd > 0){close(_socket_fd);_socket_fd = -1;}}private:int _socket_fd;};

TCP客户端

按照这个流程,来实现TCP的客户端

#include<iostream>#include"TcpSocket.hpp"using namespace std;int main(int argc, char* argv[]){if(argc != 3){cerr << "正确输入方式: ./ ip port\n" << endl;return -1; } string srv_ip = argv[1];uint16_t srv_port = stoi(argv[2]);TcpSocket socket;//创建套接字CheckSafe(socket.Socket());//申请连接服务器CheckSafe(socket.Connect(srv_ip, srv_port));while(1){string data;cout << "cli send message: ";getline(cin, data);if(data == "quit"){break;}//发送数据CheckSafe(socket.Send(data));data.clear();//接收数据CheckSafe(socket.Recv(data));cout << "srv recv message :" << data << endl;}//关闭套接字socket.Close();return 0;}

TCP服务器

因为在一段时间内可能会建立多个连接,所以多个执行流分别去控制这些连接。每当创建一个新连接,就分配一个新的执行流去执行它。

下面就分别实现多进程版本和多线程版本的服务器

多进程版本

注意:因为父子进程数据独有,子进程会拷贝一份父进程,所以对于父进程来说新建的套接字用不到,需要关闭。父进程也不需要去阻塞等待子进程,处理好信号即可

#include<iostream>#include<signal.h>#include<sys/wait.h>#include"TcpSocket.hpp"using namespace std;void sigcb(int no){while(waitpid(-1, NULL, WNOHANG) > 0);}int main(int argc, char* argv[]){if(argc != 3){cerr << "正确输入方式: ./. ip port\n" << endl;return -1; } signal(SIGCHLD, sigcb);string srv_ip = argv[1];uint16_t srv_port = stoi(argv[2]);TcpSocket socket;//创建套接字CheckSafe(socket.Socket());//绑定地址信息CheckSafe(socket.Bind(srv_ip, srv_port));//开始监听CheckSafe(socket.Listen());while(1){TcpSocket new_sock;//通过监听套接字建立连接CheckSafe(socket.Accept(&new_sock));//创建子进程int pid = fork();if(pid == 0){while(1){string data;//接收数据CheckSafe(new_sock.Recv(data));cout << "cli send message: " << data << endl;data.clear();cout << "srv reply message: ";//发送数据getline(cin, data);CheckSafe(new_sock.Send(data));}//关闭子进程连接的套接字new_sock.Close();exit(0);}//关闭父进程套接字new_sock.Close();}//关闭监听套接字socket.Close();return 0;}

多线程版本

注意:因为线程之间共享描述符表,所以主线程创建线程之后千万不能关闭新建的套接字。

#include<iostream>#include"TcpSocket.hpp"#include<pthread.h>using namespace std;void* thr_work(void* arg){//因为参数只能是void*,所以要强转获取操作句柄,所以读取前四个字节即可long fd = (long)arg;TcpSocket new_sock;new_sock.SetFd(fd);while(1){string data;//接收数据new_sock.Recv(data);cout << "cli send message: " << data << endl;data.clear();cout << "srv reply message: ";//发送数据getline(cin, data);new_sock.Send(data);}new_sock.Close();return nullptr;}int main(int argc, char* argv[]){if(argc != 3){cerr << "正确输入方式: ./. ip port\n" << endl;return -1; } string srv_ip = argv[1];uint16_t srv_port = stoi(argv[2]);TcpSocket lst_socket;//创建监听套接字CheckSafe(lst_socket.Socket());//绑定地址信息CheckSafe(lst_socket.Bind(srv_ip, srv_port));//开始监听CheckSafe(lst_socket.Listen());while(1){TcpSocket* new_sock = new TcpSocket();//通过监听套接字获取连接bool ret = lst_socket.Accept(new_sock);//连接失败则重新连接if(!ret){cerr << "连接失败" << endl;continue;}//创建线程pthread_t tid;int res = pthread_create(&tid, NULL, thr_work, (void*)new_sock->GetFd());if(res != 0){cerr << "线程创建失败" << endl;continue;}//不需要知道返回值,所以直接分离线程pthread_detach(tid);}//关闭监听套接字lst_socket.Close();return 0;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。