Linux网络Socket编程TCP

相关接口

创建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;
}
相关推荐
YoungLime8 小时前
DVWA靶场之十六:未验证的重定向漏洞(Open HTTP Redirect)
网络·安全·web安全
张人玉13 小时前
C# TCP 客户端开发笔记(TcpClient)
笔记·tcp/ip·c#
青草地溪水旁14 小时前
linux信号(14)——SIGALRM:从“手机闹钟”看SIGALRM:进程的非阻塞定时神器
linux·信号机制
心灵宝贝14 小时前
libopenssl-1_0_0-devel-1.0.2p RPM 包安装教程(openSUSE/SLES x86_64)
linux·服务器·数据库
XUE-521131415 小时前
路由策略与路由控制实验
运维·网络·网络协议·智能路由器
加油201915 小时前
如何快速学习一个网络协议?
网络·网络协议·学习·方法论
BullSmall15 小时前
linux zgrep命令介绍
linux·运维
emma羊羊16 小时前
【文件读写】图片木马
linux·运维·服务器·网络安全·靶场
wdfk_prog17 小时前
闹钟定时器(Alarm Timer)初始化:构建可挂起的定时器基础框架
java·linux·数据库