国外psd网页模板网站李守洪
简易tcp代码
- 开始监听socket(TCP,服务器)
int listen(int socket, int backlog);
TCP是面向连接的 ,服务器一般是比较”被动“的,服务器要一直处于一种,一直等待连接到来的状态。就需要进行监听
该函数的作用是将套接字设置为listen状态 就可以通过该套接字等待新连接的到来。
后面TCP协议会解释第二个参数 这里先直接用
- 接收请求(TCP,服务器)
int accept(int socket,struct sockaddr* address,socklen t* address len);
第一个参数: socket 刚刚打开的,并且已经设置为listen状态的套接字
第二、三个参数:是输出型参数,可以知道是哪个客户端来连接我
成功了返回一个整数的文件描述符 --->4 失败返回-1
- 建立连接(TCP,客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen t addrlen);
将套接字连接到远程服务器的函数
指定的套接字、指定的网络目标地址去发起连接
//单进程版本
//多进程版本
多线程版本的
TcpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <pthread.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"const int defaultfd = -1; //缺省值定义
const std::string defaultip = "0.0.0.0";
const int backlog = 10; //一般不要设置的太大 后面会解释Log lg;enum
{UsageError = 1,SockError,BindError,ListenError
};
class TcpServer;//设置一组线程数据
class ThreadData
{
public:ThreadData(int fd, const std::string &ip, const uint16_t &p,TcpServer* t):sockfd(fd),clientip(ip),clientport(p),tsvr(t){}
public:int sockfd;std::string clientip;uint16_t clientport;TcpServer *tsvr;
};class TcpServer
{
public:TcpServer(const uint16_t &port, const std::string &ip = defaultip):listensock_(defaultfd),port_(port),ip_(ip){}void InitServer(){//1、创建监听套接字listensock_ = socket(AF_INET, SOCK_STREAM, 0); //第二个参数是字节流 是tcpif(listensock_ < 0){lg(Fatal,"create listensock_ ,errno: %d, errstring: %s",errno,strerror(errno));exit(SockError);}lg(Info, "create socket success, listensock_:%d", listensock_);//2、填充字段struct sockaddr_in local; //现在只是在用户空间上填充了变量 还需要进行绑定memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);inet_aton(ip_.c_str(), &(local.sin_addr));//点分十进制的字符串风格的ip转为4字节的//local.sin_addr.s_addr = INADDR_ANY;//3、将套接字与ip地址和端口号进行绑定if(bind(listensock_, (struct sockaddr*)&local,sizeof(local)) < 0){lg(Fatal,"bind error,errno: %d, errstring: %s",errno,strerror(errno));exit(BindError);}//4、将套接字设置为监听状态//Tcp是面向连接的 通信之前得先进行连接 所以server就必须得等别人来连if(listen(listensock_,backlog) < 0){lg(Fatal,"listen error,errno: %d, errstring: %s",errno,strerror(errno));exit(ListenError);}//套接字已经被设置为listen状态}static void *Routine(void *args){pthread_detach(pthread_self()); //把自己设置为分离状态//主线程一直在获取新连接,然后就不管了,让新线程去进行服务//多线程不用关闭文件描述符表 因为在线程中所有的资源都是共享的 关闭就会出现混乱ThreadData *td = static_cast<ThreadData*>(args); //线程拿到数据td->tsvr->Service(td->sockfd, td->clientip, td->clientport);delete td; //在堆上开辟的空间return nullptr;}void Start(){//signal(SIGCHLD,SIG_IGN);lg(Info, "tcpServer is running...");for(;;){//1、获取新连接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);//被创建的、被绑定的、被监听的socket_套接字,它只是从底层把新的连接获取上来。而未来真正提供IO通信服务的并不是创建的sockfd_//而是accept返回的sockfd sockfd_一般只有一个 而sockfd可以有多个 我们把sockfd_叫作listsnsock_if(sockfd < 0){lg(Warning,"accept error,errno: %d, errstring: %s",errno,strerror(errno));continue;//服务器获取连接失败不会退出 只会去获取别人连接}//拿到谁连的我的客户端信息uint16_t clientport = ntohs(client.sin_port); char clientip[32];inet_ntop(AF_INET,&(client.sin_addr),clientip, sizeof(clientip)); //IPV4 客户端的ip地址//2、根据新连接来进行通信lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d",sockfd, clientip, clientport);//------------单进程版// Service(sockfd, clientip, clientport); //如果给一客户端提供服务的时候 其他客户端不能请求连接 服务器也不能给他提供服务 它只能等着 这就是单进程版的// close(sockfd);//-------------多进程版// pid_t id = fork();// if(id == 0)// {// close(listensock_); //要关闭这个套接字 可以理解为 子进程是去提供服务的 不需要监听套接字 只需要sockfd就好了// //child// //子进程提供服务// if(fork() > 0) exit(0); //子进程已经退出 // //后面提供服务的是孙子进程 因为孙子进程的父进程已经退出了, 那孙子进程怎么办 它会被系统领养 执行完之后会被系统自动回收// Service(sockfd, clientip, clientport); //父进程打开的文件描述符 会被子进程继承下去// close(sockfd);// exit(0);// }// close(sockfd);//sockfd是服务套接字 这个套接字已经去提供服务了 而父进程是进行监听的// //father --- 去获取新连接// pid_t rid = waitpid(id, nullptr, 0); //这是阻塞等待 阻塞的等待子进程退出 要怎么解决这个问题????? 创建孙子进程 让子进程退出 即可// (void)rid;//----------多线程版 --- 因为创建一个进程成本太高了//线程创建了 我们要给线程传递的参数 sockfd 客户端ip 客户端port 创建一个类ThreadData *td = new ThreadData(sockfd,clientip,clientport,this); //传入三个参数pthread_t tid;pthread_create(&tid, nullptr, Routine, td);//pthread_join(); //主线程不去等待 否则就会卡住 不能实现并发运行 如果主线程去等待新线程的退出 主线程就会卡住 所以应该让新线程自己等待自己退出}}void Service(int sockfd, const std::string &clientip, const uint16_t &clientport){//客户端如果自己退了服务器应该怎么办//服务器就会读到0while(true){//因为tcp是面向字节流的 所以读网络就像读取文件一样char buffer[4096];ssize_t n = read(sockfd, buffer, sizeof(buffer)); //收一个消息if(n > 0){buffer[n] = 0;std::cout << "client say#" << buffer << std::endl;std::string echo_string = "tcpserver echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(),echo_string.size()); //发回去}else if(n == 0){lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport,sockfd);break;}else //读取出错{lg(Warning, "read error,sockfd: %d,client ip: %s,client port: %d",sockfd,clientip.c_str(), clientport);break;}}}~TcpServer(){}
private:int listensock_;uint16_t port_;std::string ip_;
};
线程池版本
Makefile
.PHONY:all
all:tcpserver tcpclienttcpserver:Main.ccg++ -o $@ $^ -std=c++11
tcpclient:TcpClient.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm tcpserver tcpclient
Main.cc
#include "TcpServer.hpp"
#include <iostream>
#include <memory>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl; //提示输入正确的端口号
}
// ./tcpserver 8080
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(UsageError); //不等于2 直接终止进程}uint16_t port = std::stoi(argv[1]); //将字符串转为整数 因为命令函参数中的端口号是字符串类型std::unique_ptr<TcpServer> tcp_Svr(new TcpServer(port));tcp_Svr->InitServer();tcp_Svr->Start();return 0;
}
TcpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <pthread.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"const int defaultfd = -1; //缺省值定义
const std::string defaultip = "0.0.0.0";
const int backlog = 10; //一般不要设置的太大 后面会解释
extern Log lg;enum
{UsageError = 1,SockError,BindError,ListenError
};
class TcpServer;//设置一组线程数据
class ThreadData
{
public:ThreadData(int fd, const std::string &ip, const uint16_t &p,TcpServer* t):sockfd(fd),clientip(ip),clientport(p),tsvr(t){}
public:int sockfd;std::string clientip;uint16_t clientport;TcpServer *tsvr;
};class TcpServer
{
public:TcpServer(const uint16_t &port, const std::string &ip = defaultip):listensock_(defaultfd),port_(port),ip_(ip){}void InitServer(){//1、创建监听套接字listensock_ = socket(AF_INET, SOCK_STREAM, 0); //第二个参数是字节流 是tcpif(listensock_ < 0){lg(Fatal,"create listensock_ ,errno: %d, errstring: %s",errno,strerror(errno));exit(SockError);}lg(Info, "create socket success, listensock_:%d", listensock_);//2、填充字段struct sockaddr_in local; //现在只是在用户空间上填充了变量 还需要进行绑定memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);inet_aton(ip_.c_str(), &(local.sin_addr));//点分十进制的字符串风格的ip转为4字节的//local.sin_addr.s_addr = INADDR_ANY;//3、将套接字与ip地址和端口号进行绑定if(bind(listensock_, (struct sockaddr*)&local,sizeof(local)) < 0){lg(Fatal,"bind error,errno: %d, errstring: %s",errno,strerror(errno));exit(BindError);}//4、将套接字设置为监听状态//Tcp是面向连接的 通信之前得先进行连接 所以server就必须得等别人来连if(listen(listensock_,backlog) < 0){lg(Fatal,"listen error,errno: %d, errstring: %s",errno,strerror(errno));exit(ListenError);}//套接字已经被设置为listen状态}static void *Routine(void *args){pthread_detach(pthread_self()); //把自己设置为分离状态//主线程一直在获取新连接,然后就不管了,让新线程去进行服务//多线程不用关闭文件描述符表 因为在线程中所有的资源都是共享的 关闭就会出现混乱ThreadData *td = static_cast<ThreadData*>(args); //线程拿到数据//td->tsvr->Service(td->sockfd, td->clientip, td->clientport);delete td; //在堆上开辟的空间return nullptr;}void Start(){//signal(SIGCHLD,SIG_IGN);lg(Info, "tcpServer is running...");for(;;){//1、获取新连接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);//被创建的、被绑定的、被监听的socket_套接字,它只是从底层把新的连接获取上来。而未来真正提供IO通信服务的并不是创建的sockfd_//而是accept返回的sockfd sockfd_一般只有一个 而sockfd可以有多个 我们把sockfd_叫作listsnsock_if(sockfd < 0){lg(Warning,"accept error,errno: %d, errstring: %s",errno,strerror(errno));continue;//服务器获取连接失败不会退出 只会去获取别人连接}//拿到谁连的我的客户端信息uint16_t clientport = ntohs(client.sin_port); char clientip[32];inet_ntop(AF_INET,&(client.sin_addr),clientip, sizeof(clientip)); //IPV4 客户端的ip地址//2、根据新连接来进行通信lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d",sockfd, clientip, clientport);//------------单进程版// Service(sockfd, clientip, clientport); //如果给一客户端提供服务的时候 其他客户端不能请求连接 服务器也不能给他提供服务 它只能等着 这就是单进程版的// close(sockfd);//-------------多进程版// pid_t id = fork();// if(id == 0)// {// close(listensock_); //要关闭这个套接字 可以理解为 子进程是去提供服务的 不需要监听套接字 只需要sockfd就好了// //child// //子进程提供服务// if(fork() > 0) exit(0); //子进程已经退出 // //后面提供服务的是孙子进程 因为孙子进程的父进程已经退出了, 那孙子进程怎么办 它会被系统领养 执行完之后会被系统自动回收// Service(sockfd, clientip, clientport); //父进程打开的文件描述符 会被子进程继承下去// close(sockfd);// exit(0);// }// close(sockfd);//sockfd是服务套接字 这个套接字已经去提供服务了 而父进程是进行监听的// //father --- 去获取新连接// pid_t rid = waitpid(id, nullptr, 0); //这是阻塞等待 阻塞的等待子进程退出 要怎么解决这个问题????? 创建孙子进程 让子进程退出 即可// (void)rid;//----------多线程版 --- 因为创建一个进程成本太高了//线程创建了 我们要给线程传递的参数 sockfd 客户端ip 客户端port 创建一个类// ThreadData *td = new ThreadData(sockfd,clientip,clientport,this); //传入三个参数// pthread_t tid;// pthread_create(&tid, nullptr, Routine, td);//pthread_join(); //主线程不去等待 否则就会卡住 不能实现并发运行 如果主线程去等待新线程的退出 主线程就会卡住 所以应该让新线程自己等待自己退出//-------线程池版本 预先创建一批线程 并且提供短服务 有线程进来的时候也会有线程退出//构建一个任务对象 把任务传进来Task t(sockfd, clientip, clientport); //构建任务ThreadPool<Task>::GetInstance()->Push(t);//把任务给线程池}}// void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)// {// //客户端如果自己退了服务器应该怎么办// //服务器就会读到0// while(true)// {// //因为tcp是面向字节流的 所以读网络就像读取文件一样// char buffer[4096];// ssize_t n = read(sockfd, buffer, sizeof(buffer)); //收一个消息// if(n > 0)// {// buffer[n] = 0;// std::cout << "client say#" << buffer << std::endl;// std::string echo_string = "tcpserver echo# ";// echo_string += buffer;// write(sockfd, echo_string.c_str(),echo_string.size()); //发回去// }// else if(n == 0)// {// lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport,sockfd);// break;// }// else //读取出错// {// lg(Warning, "read error,sockfd: %d,client ip: %s,client port: %d",sockfd,clientip.c_str(), clientport);// break;// }// }// }~TcpServer(){}
private:int listensock_;uint16_t port_;std::string ip_;
};
TcpClient.cc
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << "serverip serverport\n"<< std::endl;
}
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}//通过命令函参数已经拿到了server的IP地址和portstd::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]); //port必须由字符串转为整数int sockfd = socket(AF_INET,SOCK_STREAM, 0);if(sockfd < 0){std::cerr << "socket error" << std::endl;return 1;}//将套接字连接到远程服务器//客户端要知道服务器的ip地址和端口号才能和server建立连接 这是要让别人告诉服务器的 即在服务器运行时 命令行参数里面带的// ./tcpclient serverip serverport 设置main函数的参数就好了//因为要传入的参数是sockaddr类型的 所以 我们要先定义出sockaddrstruct sockaddr_in server;memset(&server,0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_pton(AF_INET, serverip.c_str(),&(server.sin_addr));//字符串风格转四字节//至此就将要连接的目标服务器的字段就填好了//tcp客户端要不要bind? 要。 但是是由系统进行随机bind 只不过不需要显示的绑定//那什么时候进行绑定呢??//客户端发起connect的时候,进行自动随机bindint n = connect(sockfd,(struct sockaddr*)&server,sizeof(server));if(n < 0){std::cerr << "connect error..." << std::endl;return 2;}std::string message;while(true){std::cout << "Please Enter# ";std::getline(std::cin,message);//将消息写给对方write(sockfd, message.c_str(),message.size());//往sockfd里面写 --- 发消息//读对方给我返回的消息char inbuffer[4096];int n = read(sockfd, inbuffer,sizeof(inbuffer));//从sockfd里面读 --- 收消息if(n > 0){inbuffer[n] = 0;//字符串最后以0结尾std::cout << inbuffer << std::endl;}}close(sockfd);return 0;
}
ThreadPool.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>struct ThreadInfo
{pthread_t tid;std::string name;
};static const int defaultnum = 5;template<class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_); //没有资源就去休眠}bool IsQueueEmpty(){return Tasks_.empty();}public:static void *HandlerTask(void * args) //线程函数只有一个参数 如果类内函数的话它的第一个参数是this指针 就会存在参数个数不符的情况 加static就可以让他变成静态成员函数{ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args); //线程检测 有任务就处理 没任务就休眠std::string name = tp->GetThreadName(pthread_self());while(true){tp->Lock();while(tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop(); //拿任务tp->Unlock(); //解锁之后去处理任务 因为你拿到任务之后,这个任务就是你的 所以可以在解锁之后去处理你的任务t();}}//线程池对外接口 启动线程池void Start(){int num = threads_.size();for(int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i+1);pthread_create(&(threads_[i].tid),nullptr,HandlerTask, this); //创建线程时直接给线程传递当前对象}}T Pop(){T t = Tasks_.front();Tasks_.pop();return t;}//线程池对外接口 给线程池中放入任务void Push(const T &t){Lock();Tasks_.push(t);Wakeup();Unlock();}static ThreadPool<T> *GetInstance(){if(nullptr == tp_){pthread_mutex_lock(&lock_);if(nullptr == tp_){std::cout << "log: singletton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;}private:ThreadPool(int num = defaultnum) : threads_(num){pthread_mutex_init(&mutex_,nullptr);pthread_cond_init(&cond_,nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}ThreadPool(const ThreadPool<T> &) = delete;const ThreadPool<T> &operator = (const ThreadPool<T> &) = delete;
private:std::vector<ThreadInfo> threads_;std::queue<T> Tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPool<T> *tp_;//指针有获取单例的方法static pthread_mutex_t lock_;
};template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
Log.hpp
#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch(level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch(printMethod){case Screen:std::cout << logtxt <<std::endl;break;case Onefile:printOneFile(LogFile,logtxt);break;case Classfile:printClassFile(level,logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path +logname;int fd = open(_logname.c_str(),O_WRONLY | O_CREAT | O_APPEND, 0666);if(fd < 0){return;}write(fd,logtxt.c_str(),logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level);}~Log(){}void operator()(int level,const char *format,...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900,ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min,ctime->tm_sec);va_list s;va_start(s,format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer),format,s);va_end(s);char logtxt[SIZE * 2];snprintf(logtxt,sizeof(logtxt), "%s %s", leftbuffer,rightbuffer);printLog(level,logtxt);}private:int printMethod;std::string path;
};
Log lg;
Task.hpp
#pragma once
#include <iostream>
#include <string>
#include "Log.hpp"
extern Log lg;class Task
{
public:Task(int sockfd, const std::string &clientip, const uint16_t &clientport):sockfd_(sockfd),clientip_(clientip),clientport_(clientport){}void run(){//客户端如果自己退了服务器应该怎么办//服务器就会读到0char buffer[4096];//因为tcp是面向字节流的 所以读网络就像读取文件一样ssize_t n = read(sockfd_, buffer, sizeof(buffer)); //收一个消息if(n > 0){buffer[n] = 0;std::cout << "client say#" << buffer << std::endl;std::string echo_string = "tcpserver echo# ";echo_string += buffer;write(sockfd_, echo_string.c_str(),echo_string.size()); //发回去}else if(n == 0){lg(Info, "%s:%d quit, server close sockfd: %d", clientip_.c_str(), clientport_,sockfd_);}else //读取出错{lg(Warning, "read error,sockfd: %d,client ip: %s,client port: %d",sockfd_,clientip_.c_str(), clientport_);}close(sockfd_);}void operator()(){run();}~Task(){}
private:int sockfd_;std::string clientip_;uint16_t clientport_;
};
守护进程
任务是要指派给进程组的 自成进程组自成会话
想让一个进程自成会话
#include <unistd.h>
pid_t setsid(void);
将组id设置为会话id
成功返回新的 session ID 调用进程的pid ,否则返回-1 错误码被设置 -1被返回
要创建一个新的会话,调用进程不能是这个组的leader(组长)
因为进程启动 我的id和我的组id是完全一样的,我就是组长。那如何保证自己不是组长??
一般是第一个进程是组长,那如果我不是第一个进程,那就不是组长了。
if(fork() > 0) exit(0); //父进程退出
setsid(); //让子进程去创建守护进程
守护进程的本质,也是孤儿进程!因为它的父进程已经退出了 立马就要被系统领养。
它是一个新的会话,它不会受任何用户登录的影响。
#include <unistd.h>
int daemon(int nochdir, int noclose)
在后台运行
nochdir:设置为0,工作在根目录下,否则就是在当前目录下
noclose:为0,将标准输入、输出、错误 重定向到 /dev.null 否则不改变
Daemon.hpp
#pragma once#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string nullfile = "/dev/null";void Daemon(const std::string &cwd = "")
{//1、忽略其他异常信号signal(SIGCLD,SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);//2、将自己变成独立的会话if(fork() > 0)exit(0); //进来的是父进程 出去的是子进程 setsid(); //让自己变成一个新的会话//3、更改当前调用进程的工作目录if(!cwd.empty()) //当前的cwd不为空 chdir(cwd.c_str());//4、将标准输入、标准输出、标准错误 全部重定向到/dev/null 里面int fd = open(nullfile.c_str(), O_RDWR);if(fd > 0){dup2(fd,0);dup2(fd,1);dup2(fd,2);close(fd);}}
tcp通信是全双工通信的。
tcp底层会提供两个缓冲区, tcp的发送缓冲区、tcp的接收缓冲区 用户自己上层也有自己的缓冲区,要发数据,先将数据写到发送缓冲区,然后发送缓冲区通过网络交给对方的接收缓冲区。对方就可以读数据了。 其实就是发送缓冲区和接收缓冲区是独立的。 多线程对同一个fd进行读写时是不会互相影响的,因为资源是互相隔离的。 对这个fd进行读的时候也可以对他写。