相关接口
创建socket和udp的差不多,只不过type设为SOCK_STREAM,绑定方式一样,相比udp,要设置监听的状态,还要建立连接。
监听:

接收连接

成功了,返回一个文件描述符,将来通信就用这个fd,而之前创建socket的fd是用来获取新连接的。
建立连接

成功0,失败-1。
使用案例
回显服务器
cpp
//Log.hpp
#pragma once
#include<unistd.h>
#include<iostream>
#include<time.h>
#include<stdarg.h>
#include<fstream>
#include<string.h>
#include<pthread.h>
enum
{
DEBUG = 1,
INFO,
WARNING,
ERROR,
FATAL
};
const std::string logfile = "log.txt";
pthread_mutex_t _mutex;
#define SCREEN_TYPE 1
#define FILE_TYPE 2
std::string LevelToString(int level)
{
switch (level)
{
case DEBUG:
return "DEBUG";
break;
case INFO:
return "INFO";
break;
case ERROR:
return "ERROR";
break;
case WARNING:
return "WARNING";
break;
case FATAL:
return "FATAL";
break;
default:
return "UNKONW";
break;
}
}
std::string GetTime()
{
time_t now = time(nullptr);
struct tm *curr = localtime(&now);
char buf[128];
snprintf(buf, sizeof(buf), "%d-%02d-%02d %02d:%02d:%02d",
curr->tm_year+1900,
curr->tm_mon+1,
curr->tm_mday,
curr->tm_hour,
curr->tm_min,
curr->tm_sec);
return buf;
}
class LockGuard
{
public:
LockGuard(pthread_mutex_t* td):_td(td)
{
pthread_mutex_lock(_td);
}
~LockGuard()
{
pthread_mutex_unlock(_td);
}
private:
pthread_mutex_t *_td;
};
class Logmessage
{
public:
std::string _level;
pid_t _id;
std::string _filename;
int _filenumber;//行号
std::string _curr_time;
std::string _message_info;
};
class Log
{
public:
Log(const std::string& filename=logfile):_logfile(filename)
{}
void Enable(int type)
{
_type = type;
}
void FlushToScreen(Logmessage& lg)
{
printf("[%s][%d][%s][%d][%s] %s", lg._level.c_str(), lg._id, lg._filename.c_str(), lg._filenumber, lg._curr_time.c_str(), lg._message_info.c_str());
}
void FlushToFile(Logmessage& lg)
{
std::ofstream t(_logfile,std::ios::app);
if(!t.is_open())
return;
char logtxt[1024];
snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s] %s", lg._level.c_str(), lg._id, lg._filename.c_str(), lg._filenumber, lg._curr_time.c_str(), lg._message_info.c_str());
t.write(logtxt, strlen(logtxt));
t.close();
}
void FlushLog(Logmessage& lg)
{
LockGuard ld(&_mutex);
// 此处可以加过滤,本代码没加
if(_type==SCREEN_TYPE)
{
FlushToScreen(lg);
}
else if(_type==FILE_TYPE)
{
FlushToFile(lg);
}
}
void logmessage(int level,std::string filename,int filenumber,const char* format,...)
{
Logmessage lg;
lg._level = LevelToString(level);
lg._id = getpid();
lg._filename = filename;
lg._filenumber = filenumber;
lg._curr_time = GetTime();
va_list ap;
va_start(ap, format);
char info[512];
vsnprintf(info, sizeof(info), format, ap);
va_end(ap);
lg._message_info = info;
FlushLog(lg);
}
~Log(){}
private:
int _type=SCREEN_TYPE;
std::string _logfile;
};
Log lg;
#define LOG(level,format,...) do{lg.logmessage(level, __FILE__, __LINE__, format, ##__VA_ARGS__);}while (0)
#define EnableScreen() do{ lg.Enable(SCREEN_TYPE);} while (0)
#define EnableFILE() do{ lg.Enable(FILE_TYPE);} while (0)
cpp
//InetAddr.hpp
#pragma once
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<iostream>
using namespace std;
class InetAddr
{
private:
void ToHost()
{
_port = ntohs(_addr.sin_port);
_ip = inet_ntoa(_addr.sin_addr);
}
public:
InetAddr(const struct sockaddr_in& addr):_addr(addr)
{
ToHost();
}
string Ip() { return _ip; }
uint16_t Port() { return _port; }
private:
string _ip;
uint16_t _port;
struct sockaddr_in _addr;
};
cpp
//TcpServer.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include"Log.hpp"
#include<cstring>
#include"InetAddr.hpp"
#include<sys/wait.h>
using namespace std;
const static uint16_t gport = 6666;
const static int gsock = -1;
const static int gblcklog = 8;
enum
{
SOCKET_ERROR = 1,
BIND_ERROR,
LISTEN_ERROR
};
class TcpServer
{
public:
TcpServer(uint16_t port=gport):_port(port),_listensockfd(gsock)
{
}
class ThreadDate
{
public:
ThreadDate(int sockfd,TcpServer* self,const InetAddr& addr):_sockfd(sockfd),_self(self),_addr(addr)
{}
int _sockfd;
TcpServer *_self;
InetAddr _addr;
};
void InitServer()
{
_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if(_listensockfd<0)
{
LOG(FATAL, "socket create error\n");
exit(SOCKET_ERROR);
}
LOG(INFO, "socket create success\n");
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY;
int n = ::bind(_listensockfd, (sockaddr *)&local, sizeof(local));
if(n<0)
{
LOG(FATAL, "bind error\n");
exit(BIND_ERROR);
}
LOG(INFO, "bind success\n");
//tcp面向连接,要求不断获取连接,因此设为listen状态
if(::listen(_listensockfd,gblcklog)<0)
{
LOG(FATAL, "listen error\n");
exit(LISTEN_ERROR);
}
LOG(INFO, "listen success\n");
}
void Loop()
{
_isrunning = true;
while(_isrunning)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);
if(sockfd<0)
{
LOG(WARNING, "accept error\n");
continue;
}
InetAddr addr(client);
LOG(INFO, "new connect %s\n",addr.Ip().c_str());
// //多进行版本
// pid_t id=fork();
// if(id==0)
// {
// ::close(_listensockfd);
// //让孙子变成孤儿,然后执行任务
// if(fork()>0)
// exit(0);
// Service(sockfd,addr);
// exit(0);
// }
// ::close(sockfd);
// int n = waitpid(id, nullptr, 0);
// if(n>0)
// {
// LOG(INFO, "wait child success\n");
// }
//多线程版本
pthread_t tid;
ThreadDate *td = new ThreadDate(sockfd, this,addr);
pthread_create(&tid, nullptr, Excute, td);
}
}
static void* Excute(void* args)
{
pthread_detach(pthread_self());
ThreadDate *td = static_cast<ThreadDate *>(args);
td->_self->Service(td->_sockfd,td->_addr);
delete td;
return nullptr;
}
void Service(int sockfd,InetAddr addr)
{
while(1)
{
char buf[512];
ssize_t n = ::read(sockfd, buf, sizeof(buf) - 1);
if(n>0)
{
buf[n] = 0;
string msg = "[" + addr.Ip() + "]:" + buf;
::write(sockfd, msg.c_str(), msg.size());
}
else if(n==0)
{
LOG(INFO, "client %s quit\n", addr.Ip().c_str());
break;
}
else
{
LOG(ERROR, "read %s error\n", addr.Ip().c_str());
break;
}
}
::close(sockfd);
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensockfd;
bool _isrunning=false;
};
cpp
//TcpServerMain.cpp
#include"TcpServer.hpp"
#include<memory>
int main()
{
std::unique_ptr<TcpServer> server = std::make_unique<TcpServer>();
server->InitServer();
server->Loop();
return 0;
}
cpp
//TcpClientMain.cpp
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<cstring>
#include"InetAddr.hpp"
using namespace std;
int main(int argc, char *argv[])
{
if (argc != 3)
{
cerr << "usage:" << argv[0] << "error" << endl;
exit(0);
}
string serverip = argv[1];
uint16_t serverport = htons(stoi(argv[2]));
//创建
int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if(sockfd<0)
{
cout<<"create socket error"<<endl;
exit(1);
}
//不需要显示绑定,会自己随机生产端口
//建立连接
struct sockaddr_in server;
server.sin_port = serverport;
server.sin_family = AF_INET;
::inet_pton(AF_INET, serverip.c_str(), &server.sin_addr);
int n = ::connect(sockfd, (struct sockaddr *)&server, sizeof(server));
if(n<0)
{
cerr << "connect error" << endl;
exit(1);
}
while(1)
{
string message;
cout << "Enter:";
getline(std::cin, message);
write(sockfd, message.c_str(), message.size());
char buf[512];
int n=read(sockfd, buf, sizeof(buf) - 1);
if(n>0)
{
buf[n] = 0;
cout << "server:" << buf << endl;
}
else
{
break;
}
}
::close(sockfd);
return 0;
}
多线程远程命令执行

创建管道,创建子进程然后子进程去执行command,把结果写到管道。
在上面的基础上把服务器改一改.
cpp
#pragma once
#include "Log.hpp"
#include "InetAddr.hpp"
#include <string>
#include<cstdio>
class Command
{
public:
Command() {}
string Excute(const string& cmdstr)
{
string result;
FILE *fp = popen(cmdstr.c_str(), "r");
if(fp)
{
char line[512];
while(fgets(line,sizeof(line),fp))
{
result += line;
}
return result.empty()?"success":result;
}
else{
return "execute error";
}
}
void HanderCommand(int sockfd, InetAddr addr)
{
while (1)
{
char buf[512];
ssize_t n = ::recv(sockfd, buf, sizeof(buf) - 1,0);
if (n > 0)
{
buf[n] = 0;
string msg = "[" + addr.Ip() + "]:" + buf;
LOG(INFO, "%s\n", msg.c_str());
string result=Excute(buf);
::send(sockfd, result.c_str(), result.size(), 0);
}
else if (n == 0)
{
LOG(INFO, "client %s quit\n", addr.Ip().c_str());
break;
}
else
{
LOG(ERROR, "read %s error\n", addr.Ip().c_str());
break;
}
}
::close(sockfd);
}
~Command() {}
};
cpp
//TcpServer.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include"Log.hpp"
#include<cstring>
#include"InetAddr.hpp"
#include<sys/wait.h>
#include<functional>
using namespace std;
using command_service_t = function<void(int, InetAddr)>;
const static uint16_t gport = 6666;
const static int gsock = -1;
const static int gblcklog = 8;
enum
{
SOCKET_ERROR = 1,
BIND_ERROR,
LISTEN_ERROR
};
class TcpServer
{
public:
TcpServer(command_service_t service,uint16_t port=gport):_port(port),_listensockfd(gsock),_service(service)
{
}
class ThreadDate
{
public:
ThreadDate(int sockfd,TcpServer* self,const InetAddr& addr):_sockfd(sockfd),_self(self),_addr(addr)
{}
int _sockfd;
TcpServer *_self;
InetAddr _addr;
};
void InitServer()
{
_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if(_listensockfd<0)
{
LOG(FATAL, "socket create error\n");
exit(SOCKET_ERROR);
}
LOG(INFO, "socket create success\n");
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY;
int n = ::bind(_listensockfd, (sockaddr *)&local, sizeof(local));
if(n<0)
{
LOG(FATAL, "bind error\n");
exit(BIND_ERROR);
}
LOG(INFO, "bind success\n");
//tcp面向连接,要求不断获取连接,因此设为listen状态
if(::listen(_listensockfd,gblcklog)<0)
{
LOG(FATAL, "listen error\n");
exit(LISTEN_ERROR);
}
LOG(INFO, "listen success\n");
}
void Loop()
{
_isrunning = true;
while(_isrunning)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);
if(sockfd<0)
{
LOG(WARNING, "accept error\n");
continue;
}
InetAddr addr(client);
LOG(INFO, "new connect %s\n",addr.Ip().c_str());
// //多进行版本
// pid_t id=fork();
// if(id==0)
// {
// ::close(_listensockfd);
// //让孙子变成孤儿,然后执行任务
// if(fork()>0)
// exit(0);
// Service(sockfd,addr);
// exit(0);
// }
// ::close(sockfd);
// int n = waitpid(id, nullptr, 0);
// if(n>0)
// {
// LOG(INFO, "wait child success\n");
// }
//多线程版本
pthread_t tid;
ThreadDate *td = new ThreadDate(sockfd, this,addr);
pthread_create(&tid, nullptr, Excute, td);
}
}
static void* Excute(void* args)
{
pthread_detach(pthread_self());
ThreadDate *td = static_cast<ThreadDate *>(args);
td->_self->_service(td->_sockfd,td->_addr);
delete td;
return nullptr;
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensockfd;
bool _isrunning=false;
command_service_t _service;
};
cpp
//TcpServer.cpp
#include"TcpServer.hpp"
#include"Command.hpp"
#include<memory>
int main()
{
Command cmdservice;
command_service_t func = std::bind(&Command::HanderCommand,
&cmdservice, std::placeholders::_1, std::placeholders::_2);
std::unique_ptr<TcpServer> server = std::make_unique<TcpServer>(func);
server->InitServer();
server->Loop();
return 0;
}