您现在的位置是:首页 >技术教程 >tcp套接字的应用网站首页技术教程

tcp套接字的应用

JDSZGLLL 2024-06-24 00:01:02
简介tcp套接字的应用

tcp服务端流程

 tcp客户端流程

 客户端代码

tcpClient.hpp

#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

using namespace std;

namespace szg
{
    class tcpClient
    {
    public:
        tcpClient(string ip, uint16_t port)
        :_ip(ip), _port(port)
        {
            _sock = socket(AF_INET, SOCK_STREAM, 0);
            if(_sock < 0)
            {
                cerr << "用户端创建套接字失败" << endl;
                exit(-1);
            }
            cout << "用户端创建套接字成功" << endl;
        }

        void start()
        {
            sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_port = htons(_port);
            server.sin_addr.s_addr = inet_addr(_ip.c_str());


            if(connect(_sock, (sockaddr*)&server, sizeof(server)))
            {
                cerr << "客户端获取服务失败" << endl;
            }
            cout << "客户端获取服务成功" << endl;


            while(true)
            {
                string message;
                while(true)
                {
                    getline(cin, message);
                    write(_sock, message.c_str(), message.size());


                    char rvbuf[1024];
                    size_t n = read(_sock, rvbuf, sizeof(rvbuf) - 1);
                    rvbuf[n] = 0;
                    if(0 == n)
                    {
                        cerr << "server qiut , me too" << endl;
                        break;
                    }
                    cout << "server 回显:" << rvbuf << endl;

                }
            }
        }

    private:
        int  _sock;
        string _ip;
        uint16_t _port;
    };



}

tcpClient.cc

#include"tcpClient.hpp"
#include<memory>


int main(int args, char* argv[])
{
    if(args != 3)
    {
        cout << "
Usage:
	" << argv[0] << " serverip serverport

";
    }

    string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    unique_ptr<szg::tcpClient> tcpclient(new szg::tcpClient(ip, port));
    tcpclient->start();

    return 0;
}



客户端进程版

tcpServer.hpp

#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include <string.h>
#include <errno.h>

using namespace std;

namespace szg
{
    string df_ip = "0.0.0.0";
    uint16_t df_port = 8080;
    class tcpServer
    {
    public:
        tcpServer(uint16_t port = df_port)
        : _port(port)
        {
            //1.创建套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if(_listensock < 0)
            {
                cerr << "服务端创建套接字失败" << endl;
                exit(-1);
            }
            cout << "服务端创建套接字成功" << endl;
        }

        void start()
        {
            //2.绑定套接字
            sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(_port);
            server.sin_addr.s_addr = INADDR_ANY;
            if(-1 == bind(_listensock, (sockaddr*)&server, sizeof(server)))
            {
                cerr << "服务端绑定监听套接字失败" << endl;
                perror("server bind :");
                exit(1);
            }
            cout << "服务端绑定监听套接字成功" << endl;


            //3.设置套接字为监听状态
            if(listen(_listensock, 5))
            {
                cerr << "服务器监听状态设置失败" << endl;
                exit(2);
            }
            cout << "服务器监听状态设置成功" << endl;

            signal(SIGCHLD, SIG_IGN);


            
            while(true)
            {
                sockaddr_in peer;
                socklen_t len = sizeof(peer);

                //4.建立连接
                int newsock = accept(_listensock, (sockaddr*)&peer, &len);
                if(newsock < 0)
                {
                    cerr << "服务器监听客户端失败" << endl;
                }
                cout << "服务器监听客户端成功" << endl;
                if(0 == fork())
                {
                    char rdbuf[1024];
                    while (true)
                    {
                        size_t n = read(newsock, rdbuf, sizeof(rdbuf) - 1);
                        if(n == 0)
                        {
                            cout << "client offline,  me too" << endl;
                            break;
                        }
                        rdbuf[n] = 0;
                        
                        write(newsock, rdbuf, strlen(rdbuf));
                    }
                    exit(0);   
                }
                close(newsock);

            }
        }

    private:
        int  _listensock;
        uint16_t _port;
    };



}

tcpServer.cc

#include"tcpServer.hpp"
#include<memory>


int main(int args, char* argv[])
{
    if(args != 2)
    {
        cout << "
server:
	" << argv[0] << " serverport

";
    }

    uint16_t port = atoi(argv[1]);

    unique_ptr<szg::tcpServer> tcpserver(new szg::tcpServer(port));
    tcpserver->start();

    return 0;
}

客户端线程池版

MutexGuard.hpp

#pragma once
#include<pthread.h>

class MutexGuard
{
public:
    MutexGuard(pthread_mutex_t* pmutex)
    :_pmutex(pmutex)
    {
        pthread_mutex_lock(_pmutex);
    }

    ~MutexGuard()
    {
        pthread_mutex_unlock(_pmutex);
    }
private:
    pthread_mutex_t* _pmutex;
};

mythread.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cassert>
#include <functional>
#include <pthread.h>

class Thread;

//context传递对象的地址,帮助静态成员函数可以使用类内成员变量
class context
{
public:
    context(Thread* t = nullptr, void* args = nullptr)
    :_this(t)
    ,_args(args)
    {}
    Thread* _this;
    void* _args;
};

class Thread
{
public:
    Thread(const std::function<void*(void*)>& f, void* args = nullptr, const std::string& name = "")
    :_func(f)
    ,_args(args)
    ,_name(name)
    {
        ++_num;
        if(_name.size() == 0)
        {
            _name += "thread  ";
            _name += std::to_string(_num);
        }
        //创建上下文结构体
        context* pcon = new context(this, _args);
        int n = pthread_create(&_tid, nullptr, route_start, pcon);
        assert(0 == n);
        (void)n;
    }
    static void* route_start(void* pcon)
    {
        context* cont = static_cast<context*>(pcon);
        void* ret = cont->_this->run(cont->_args);
        delete cont;
        return ret;
    }

    void* run(void* args)
    {
        return _func(args);
    }

    void join()
    {
        int n = pthread_join(_tid, nullptr);
        assert(n == 0);
        (void)n;
    }

    std::string name()
    {
        return _name;
    }

    ~Thread()
    {
        --_num;
    }
private:
    pthread_t  _tid;
    std::string _name;
    void* _args;
    std::function<void*(void*)> _func;
    static int _num;
};
int Thread::_num = 0;

mythreadpool.hpp

#pragma once
#include<vector>
#include<queue>
#include<mutex>
#include"mythread.hpp"
#include"MutexGuard.hpp"

const int maxnum = 5;

template<class T>
class threadpool;
//上下文
template<class T>
class condate
{
public:
    condate(T* t, int num)
    :_this(t), _num(num)
    {}
    T* _this;
    int _num;
};

template<class T>
class threadpool
{
private:
    static void* task_handler(void* args)
    {
        condate<threadpool<T>>* pcondate = static_cast<condate<threadpool<T>>* >(args);
        threadpool<T>* tp = pcondate->_this;
        while(true)
        {
            T t;
            tp->pop(t);
            std::cout << tp->_threads[pcondate->_num]->name() << " : ";
            t();
        }
        delete pcondate;
    }

    threadpool(int num = maxnum)
    :_num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for(int i = 0; i < _num; ++i)
        {

            condate<threadpool<T>>* ct = new condate<threadpool<T>>(this, i);
            _threads.push_back(new Thread(task_handler, ct));
        }
    }

public:
    void pop(T& t)
    {
        MutexGuard mg(&_mutex);
        while(_task.empty())
            pthread_cond_wait(&_cond, &_mutex);
        t = _task.front();
        _task.pop();
    }

    void push(const T& t)
    {
        MutexGuard mg(&_mutex);
        _task.push(t);
        pthread_cond_signal(&_cond);
    }

    ~threadpool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for(auto& t : _threads)
        {
            delete t;
        }
    }

    static threadpool<T>* getinstance()
    {
        if(nullptr == _singleton)
        {
            _singletonlock.lock();
            if(nullptr == _singleton)
            {
                _singleton = new threadpool();
            }
            _singletonlock.unlock();
        }
        return _singleton;
    }


private:
    int _num;
    std::vector<Thread*> _threads;
    std::queue<T> _task;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    static std::mutex _singletonlock;
    static threadpool<T>* _singleton;

};

template<class T>
std::mutex threadpool<T>::_singletonlock;

template<class T>
threadpool<T>* threadpool<T>::_singleton = nullptr;

tcpServer.hpp

#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"mythreadpool.hpp"
#include"log.hpp"

#include <string.h>
#include <errno.h>

using namespace std;

namespace szg
{

    class tcp_task
    {
        public:
        tcp_task(int sock = 0)
        :_sock(sock)
        {}

        void operator()()
        {
            char rdbuf[1024];
            while (true)
            {
                size_t n = read(_sock, rdbuf, sizeof(rdbuf) - 1);
                if(n == 0)
                {
                    cout << "client offline,  me too" << endl;
                    break;
                }
                rdbuf[n] = 0;
                
                write(_sock, rdbuf, strlen(rdbuf));
            }
            close(_sock);
        }

        int _sock;
    };


    string df_ip = "0.0.0.0";
    uint16_t df_port = 8080;
    class tcpServer
    {
    public:
        tcpServer(uint16_t port = df_port)
        : _port(port)
        {
            //1.创建套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if(_listensock < 0)
            {
                cerr << "服务端创建套接字失败" << endl;
                exit(-1);
            }
            cout << "服务端创建套接字成功" << endl;

            logMessage(NORMAL, "accept a new link success, get new sock: %d", _listensock); // ?
            logMessage(DEBUG, "accept error, next");
            logMessage(WARNING, "accept error, next");
            logMessage(FATAL, "accept error, next");
            logMessage(NORMAL, "accept error, next");

            logMessage(NORMAL, "accept a new link success, get new sock: %d", _listensock); // ?
            logMessage(DEBUG, "accept error, next");
            logMessage(WARNING, "accept error, next");
            logMessage(FATAL, "accept error, next");
            logMessage(NORMAL, "accept error, next");

            logMessage(NORMAL, "accept a new link success, get new sock: %d", _listensock); // ?
            logMessage(DEBUG, "accept error, next");
            logMessage(WARNING, "accept error, next");
            logMessage(FATAL, "accept error, next");
            logMessage(NORMAL, "accept error, next");
        }

        void start()
        {
            //2.绑定套接字
            sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(_port);
            server.sin_addr.s_addr = INADDR_ANY;
            if(-1 == bind(_listensock, (sockaddr*)&server, sizeof(server)))
            {
                cerr << "服务端绑定监听套接字失败" << endl;
                perror("server bind :");
                exit(1);
            }
            cout << "服务端绑定监听套接字成功" << endl;


            //3.设置套接字为监听状态
            if(listen(_listensock, 5))
            {
                cerr << "服务器监听状态设置失败" << endl;
                exit(2);
            }
            cout << "服务器监听状态设置成功" << endl;

            signal(SIGCHLD, SIG_IGN);


            
            while(true)
            {
                sockaddr_in peer;
                socklen_t len = sizeof(peer);

                //4.建立连接
                int newsock = accept(_listensock, (sockaddr*)&peer, &len);
                if(newsock < 0)
                {
                    cerr << "服务器监听客户端失败" << endl;
                }
                cout << "服务器监听客户端成功:" << newsock << endl;
                threadpool<tcp_task>::getinstance()->push(tcp_task(newsock));
                // if(0 == fork())
                // {
                //     char rdbuf[1024];
                //     while (true)
                //     {
                //         size_t n = read(newsock, rdbuf, sizeof(rdbuf) - 1);
                //         if(n == 0)
                //         {
                //             cout << "client offline,  me too" << endl;
                //             break;
                //         }
                //         rdbuf[n] = 0;
                        
                //         write(newsock, rdbuf, strlen(rdbuf));
                //     }
                //     exit(0);   
                // }
                // close(newsock);



            }
        }

    private:
        int  _listensock;
        uint16_t _port;
    };



}

日志记录文件

log.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>

#define LOG_NORMAL "log.txt"
#define LOG_ERR "log.error"

#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3
#define FATAL   4

const char * to_levelstr(int level)
{
    switch(level)
    {
        case DEBUG : return "DEBUG";
        case NORMAL: return "NORMAL";
        case WARNING: return "WARNING";
        case ERROR: return "ERROR";
        case FATAL: return "FATAL";
        default : return nullptr;
    }
}


void logMessage(int level, const char* formate, ...)
{
    char logprefix[1024];
    sprintf(logprefix, "[%s][time:%ld][pid:%d]",to_levelstr(level), (long long)time(nullptr), getpid());
    char logbackfix[1024];
    va_list arg;
    va_start(arg, formate);
    vsnprintf(logbackfix, sizeof(logbackfix) - 1, formate, arg);

    FILE *log =  fopen(LOG_NORMAL, "a");
    FILE *err = fopen(LOG_ERR, "a");
    if(log != nullptr && err != nullptr)
    {
        FILE *curr = nullptr;
        if(level == DEBUG || level == NORMAL || level == WARNING) curr = log;
        if(level == ERROR || level == FATAL) curr = err;
        if(curr) fprintf(curr, "%s%s
", logprefix, logbackfix);

        fclose(log);
        fclose(err);
    }
}

守护进程

ctrl + z将前台引用放到后台(暂停)

jobs  查看作业

fg + 作业号(切到前台) <————> bg + 作业号(启动暂停的作业号) 

守护进程,精灵进程的本质是孤儿进程。

守护进程化

setsid: 中间新建一个会话,必须不是作业的组长才能使用

1. 让调用进程忽略掉异常的信号

2. 如何让自己不是组长,setsid且创建子进程

3. 守护进程是脱离终端的,关闭或者重定向以前进程默认打开的文件

4.更改可执行路径

#pragma once

#include <unistd.h>
#include <signal.h>
#include <cstdlib>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define DEV "/dev/null"

void daemonSelf(const char *currPath = nullptr)
{
    // 1. 让调用进程忽略掉异常的信号
    signal(SIGPIPE, SIG_IGN);

    // 2. 如何让自己不是组长,setsid
    if (fork() > 0)
        exit(0);
    // 子进程 -- 守护进程,精灵进程,本质就是孤儿进程的一种!
    pid_t n = setsid();
    assert(n != -1);

    // 3. 守护进程是脱离终端的,关闭或者重定向以前进程默认打开的文件
    int fd = open(DEV, O_RDWR);
    if(fd >= 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);

        close(fd);
    }
    else
    {
        close(0);
        close(1);
        close(2);
    }

    // 4. 可选:进程执行路径发生更改

    if(currPath) chdir(currPath);
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。