运行结果如下:
文章插图
注意: 方法二中,父进程创建好子进程之后,子进程可以将监听套接字关闭,此时该套接字对子进程来说是没有用的,当然也可以不用关闭,没有多大的浪费 。但父进程关闭掉服务sock是有必要的,因为此时父进程不需要维护这些套接字了,孙子进程维护即可,如果不关闭,且有很多客户端向服务端发起请求,那么父进程这边就要维护很多不必要的套接字,让父进程的文件描述符不够用,造成文件描述符泄漏,所以父进程关闭服务套接字是必须的 。方法二代码编写:
//循环获取连接 void loop() {struct sockaddr_in peer;//获取客户端的端口号和ipsocklen_t len = sizeof(peer);while(1){//获取连接//sock是进行通信的一个套接字,_listen_sock是用来监听的套接字int sock = accept(_listen_sock,(struct sockaddr *)&peer,&len);if(sock<0){cout<<"accept fail,continue accept"<<endl;continue;}//创建子进程pid_t id = fork();if(id == 0){//子进程//子进程和父进程文件描述符一致close(_listen_sock);//可以不关闭,但是建议关闭,防止后期子进程对监听套接字进>行一些操作,影响父进程if(fork()>0){//父进程//直接退出,让孙子进程被os(1号进程)领养,退出的时候资源被操作系统回收exit(0);}//孙子进程int peerPort = ntohs(peer.sin_port);string peerIp = inet_ntoa(peer.sin_addr);cout<<"获得了一个新的连接,["<< peerIp <<"]:["<< peerPort <<"]"<<endl;this->Server(peerIp,peerPort,sock);}//关闭sock,如果不关闭,那么爷爷进程可用的文件描述符越来越少//通信的工作交给孙子进程close(sock);//爷爷进程等待儿子进程waitpid(-1,nullptr,0);} }void Server(string ip, int port, int sock){while (1){char buf[256];ssize_t size = read(sock, buf, sizeof(buf)-1);if (size > 0){// 正常读取size字节的数据buf[size] = 0;cout << "[" << ip << "]:[" << port<< "]# "<< buf <<endl;string msg = "server get!-> ";msg += buf;write(sock, msg.c_str(), msg.size());}else if (size == 0){// 对端关闭cout << "[" << ip << "]:[" << port<< "]# close" << endl;break;}else{// 出错cerr << sock << "read error" << endl;break;}}close(sock);cout << "service done" << endl;// 子进程退出exit(0);}
小伙伴们可以动手运行一下哦~多线程版本思路: 通过创建一个线程为客户端提供服务,创建好的线程之间进行线程分离,这样主线程就不需要等待其它线程了方法: 让启动函数执行服务的代码,其中最后一个参数可以传一个类过去,这个类包含了,客户端端口号和套接字信息,如下:
struct Info{int _port;std::string _ip;int _sock;Info(int port, string ip, int sock):_port(port),_ip(ip),_sock(sock){}};
注意: 这里为了不让thread_run
多一个this
指针这个参数,所以用static
修饰该函数,就没有this
指针这个参数了,为了让创建出来的线程线程就可以调用该Service
函数,这里将Service
函数也用static
修饰static void* thread_run(void* arg){Info info = *(Info*)arg;delete (Info*)arg;// 线程分离pthread_detach(pthread_self());Service(info._ip, info._port, info._sock);}void loop(){struct sockaddr_in peer;// 获取远端端口号和ip信息socklen_t len = sizeof(peer);while (1){// 获取链接// sock 是进行通信的一个套接字_listen_sock 是进行监听获取链接的一个套接字int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){cout << "accept fail, continue accept" << endl;continue;}// 多线程版本pthread_t tid;int peerPort = ntohs(peer.sin_port);string peerIp = inet_ntoa(peer.sin_addr);Info* info = new Info(peerPort, peerIp, sock);pthread_create(&tid, nullptr, thread_run, (void*)info);}}static void Service(string ip, int port, int sock){while (1){char buf[256];ssize_t size = read(sock, buf, sizeof(buf)-1);if (size > 0){// 正常读取size字节的数据buf[size] = 0;cout << "[" << ip << "]:[" << port<< "]# "<< buf << endl;string msg = "server get!-> ";msg += buf;write(sock, msg.c_str(), msg.size());}else if (size == 0){// 对端关闭cout << "[" << ip << "]:[" << port<< "]# close" << endl;break;}else{// 出错cout << sock << "read error" << endl;break;}}close(sock);cout << "service done" << endl;}
经验总结扩展阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 抓包分析 TCP 握手和挥手
- 概念+协议的了解+OSI七层模型,TCP/IP五层协议,网络数据传输流程 Linux--网络基础
- 关于网页实现串口或者TCP通讯的说明
- 如何kill一条TCP连接?
- TCP 序列号和确认号是如何变化的?
- 2d游戏怎么编程(怎么编写一个2d游戏)
- 怎么编写游戏程序(游戏外挂编写教程)
- Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog
- 手写编程语言-如何为 GScript 编写标准库
- 编写HelloWorld并运行