小白一步一步学并发编程(1)—— tcp单发服务

最简单的tcp服务,一次只能处理一个请求,请求处理完就直接关闭连接。这里只为了展示并发编程的逻辑,所以业务逻辑处理比较简单,就是接收客户端请求,打印客户端发送的信息。

tcp_server.h头文件

#ifndef TCP_SERVER_H_
#define TCP_SERVER_H_

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <thread> 
#include <pthread.h>
#include <string>
#include <mutex>
#include <unistd.h>
#include "logger.h"
                
                
//using namespace std;
        
#define MAXLINE 4096

class Server{
        public: 
                Server(string ip, int port){
                        memset(&addr,0,sizeof(addr));
                        addr.sin_family = AF_INET;
                        addr.sin_port = htons(port);
                        addr.sin_addr.s_addr = inet_addr(ip.c_str());
                }
                virtual int start();
                int connectionWorker();
                bool init(logger *log);
        protected:
                sockaddr_in addr;
                static logger *m_log;
};
#endif

tcp_server.cpp实现文件

#include <iostream>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <thread>
#include <string>
#include <mutex>
#include "tcp_server.h"
#include "logger.h"

logger* Server::m_log = NULL;

int Server::start(){
        int sockfd,connfd;
        struct sockaddr_in cliaddr;
        socklen_t clilen;
        char buff[MAXLINE];
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
                m_log->outputLog("socket create failed: error %s, errno:%d",strerror(errno),errno);
                return 0;
        }
        m_log->outputLog("socket create %d", sockfd);
        int ret = ::bind(sockfd, (struct sockaddr*) &addr, sizeof(addr));
        if(ret == -1){
                m_log->outputLog("bind socket error: %s, errno:%d",strerror(errno),errno);
                return 0;
        }       
        if((listen(sockfd,10)) == -1){
                m_log->outputLog("listen error: %s, errno:%s",strerror(errno),errno);
                return 0;
        }       
        //socklen_t len = sizeof(cliaddr);
        
        while(true){
                socklen_t len = sizeof(cliaddr);
                if((connfd = accept(sockfd,(struct sockaddr*)&cliaddr,&len)) == -1){
                        m_log->outputLog("accept error: %s, errno:%s",strerror(errno),errno);
                        continue;
                }       
                int n = recv(connfd,buff,MAXLINE,MSG_DONTWAIT);
                buff[n] = '\0';
                m_log->outputLog("recv msg:%s\n",buff);
                close(connfd);
        }       
        close(sockfd);
}       

bool Server::init(logger *log){
        if(log != NULL){
                m_log = log;
                return true;
        }
        return false;
}

int Server::connectionWorker(){
}

 简单的日志类的实现

日志类头文件logger.h

#ifndef LOGGER_H_
#define LOGGER_H_

#include <iostream>
#include <sstream>
#include <string>
#include <stdio.h>
//#include <mutex>
using namespace std;
class logger{
        public:
                ~logger();
                void closeFd();
                static logger* get_instance();
                bool openLogFile(const string filepath, const string filename, bool bFlush);
                void outputLog(const char *fmt, ...);
                string getCurTime();
        private:
                static logger* m_instance;
                static pthread_mutex_t m_log_mutex;
                FILE * m_file_handler;
                bool m_flush;
                logger();
};

#endif

日志类实现文件logger.cpp,日志采用的是单例设计模式,类中声明的静态变量必须在类外单独初始化。

#include "logger.h"
#include <stdarg.h>


using namespace std;

pthread_mutex_t logger::m_log_mutex = PTHREAD_MUTEX_INITIALIZER;
logger* logger::m_instance = NULL;

logger::~logger(){
        if(m_file_handler){
                closeFd();
        }
        pthread_mutex_destroy(&m_log_mutex);
}

logger::logger(){
        m_file_handler = NULL;
        m_flush = false;
        pthread_mutex_init(&m_log_mutex,NULL);
}

void logger::closeFd(){
        if(m_file_handler){
                fclose(m_file_handler);
        }
}

logger* logger::get_instance(){
        if(m_instance == NULL){
                pthread_mutex_lock(&m_log_mutex);
                if(m_instance == NULL){
                        m_instance = new logger();
                }
                pthread_mutex_unlock(&m_log_mutex);
        }
        return m_instance;
}

bool logger::openLogFile(const string filepath, const string filename, bool bFlush){
        m_flush = bFlush;
        string fullname = filepath;
        if(fullname.length() > 0){
                if(fullname.substr(fullname.length()-1,1) != "/"){
                        fullname += "/";
                }
        }
        m_file_handler = fopen((fullname + filename).c_str(),"a");
        if(m_file_handler == NULL){
                return false;
        }
        return true;
}

string logger::getCurTime(){
        time_t timep;
        time(&timep);
        char tmp[64];
        strftime(tmp, sizeof(tmp),"%Y-%m-%d %H:%M:%S",localtime(&timep));
        return tmp;
}

void logger::outputLog(const char* fmt, ... ){
        ostringstream ofmt;
        string time_str = getCurTime();
        ofmt << getCurTime() << "\t[INFO]\t";
        va_list args;
        va_start(args,fmt);
        ofmt << fmt << "\n";
        if(m_file_handler){
                vfprintf(m_file_handler, ofmt.str().c_str(), args);
        }
        if(m_flush){
                fflush(m_file_handler);
        }
}

客户端程序tcp_client.cpp

#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <thread>
#include <string>
#include <string.h>
#include <mutex>
#include "logger.h"

using namespace std;

#define MAXLINE 4096

class Client{
        public:
                int run(int i);
                Client(){}
                ~Client(){}
                int init(string ip, int port);
                int setLog(logger * log);
                int exit();
        private:
                struct sockaddr_in servaddr;
                logger * m_log;
                bool m_exit;
};
int Client::run(int i){
        int sockfd, n;
        m_exit = true;
        ostringstream tmp;
        tmp << "I'm client " << i << " .";
        string sendline = tmp.str();
        if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0){
                m_log->outputLog("create socket error: %s(errno: %d)\n", strerror(errno), errno);
                return -1;
        }
        if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
                m_log->outputLog("connect socket error: %s(errno: %d)\n", strerror(errno), errno);
                return -1;
        }
        while(true){
                m_log->outputLog("send %s",sendline.c_str());
                if(send(sockfd, sendline.c_str(), sendline.length(), 0) < 0){
                        m_log->outputLog("send error: %s(errno: %d)\n", strerror(errno), errno);
                        return -1;
                }
                m_log->outputLog("sleep 1s");
                sleep(1);
        }
        close(sockfd);
        return 0;
}
int Client::init(string ip, int port){
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(port);
        servaddr.sin_addr.s_addr = inet_addr(ip.c_str());
}

int Client::setLog(logger * log){
        if(log != NULL){
                m_log = log;
                return 0;
        }
        return -1;
}

测试主程序

服务器程序

#include "logger.h"
#include "tcp_server.h"
#include "tcp_server_thread.h"
#include "tcp_server_process.h"
#include "tcp_server_epoll.h"
#include "tcp_server_libevent.h"
#include "tcp_server_threadpoolevent.h"
#include <signal.h>
#include <sys/wait.h>


using namespace std;
logger *m_log = logger::get_instance();

void func_wait(int signo){
        pid_t pid;
        int stat;
        pid = wait(&stat);
        m_log->outputLog("child %d exit\n",pid);
        return;
}

void func_waitpid(int signo){
        pid_t pid;
        int stat;
        while( (pid = waitpid(-1,&stat,WNOHANG)) > 0 ){
                m_log->outputLog("child %d exit\n",pid);
        }
        return;
}
nt main(){
        signal(SIGCHLD, &func_waitpid);
        //logger *m_log = logger::get_instance();
        m_log->openLogFile("./","test_server.log",true);

        Server *m_server = new Server("111.206.73.111",12345);
        //Server *m_server = new ThreadServer("111.206.73.111",12345);
        //Server *m_server = new ProcessServer("111.206.73.111",12345);
        //Server *m_server = new poolEventServer("111.206.73.111",12345);
        m_server->init(m_log);
        m_server->start();
}

客户端测试主程序

#include "logger.h"
#include "tcp_client.h"
#include <stdlib.h>

using namespace std;


int main(int argc, char **argv){
        logger *m_log = logger::get_instance();
        m_log->openLogFile("./","test_client.log",true);
        Client *m_client = new Client();
        m_client->init("111.206.73.111",12345);
        m_client->setLog(m_log);
        m_client->run(atoi(argv[1]));
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,生可以习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,生可以习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值