网络套接字实现TCP机制通信

           

         昨天写了一篇实现UDP通信的博客,UDP和TCP当然是两个极端,今天我们也来介绍一下TCP,在上一篇博客中,博主也介绍了一些TCP常用的系统调用接口,在这里博客就不再赘述了。另外,在这里祝福大家以及我参加秋招和找实习生的伙伴,拿个好offer,我们千万不要等毕业再去找工作,那时候失去应届生身份后就特别难进大厂了。

目录

认识TCP协议

单进程版本

Makefile

tcp_client.cc

tcp_server.cc

多进程版本

Makefile

tcp_client.cc

tcp_server.cc

多进程版本2

tcp_server.cc

单线程版本

tcp_server.cc

线程池版本

Makefile

task.hpp

tcp_client.cc

tcp_server.cc

thread_pool.hpp

测试结果(只展线程池版本的测试结果)

总结

服务端

客户端


认识TCP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识; 后面我们再详细讨论TCP的一些细节问题.
1、传输层协议

2、有连接
3、可靠传输

4、面向字节流

关于TCP协议更多的知识讲解,我们到传输层报文那里着重介绍。

单进程版本

Makefile

.PHONY:all
all:tcp_client tcp_server

udp_client:tcp_client.cc
	g++ -o $@ $^ -std=c++11

udp_server:tcp_server.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f tcp_client tcp_server

tcp_client.cc

#include<iostream>
#include<string>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>

using namespace std;

void Usage(string proc)
{
    cout << "Usage: " << proc << " server_ip server_port" << endl;
}
//./tcp_client server_ip server_port
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }

    //从命令行上获取的
    string svr_ip = argv[1];
    uint16_t port = atoi(argv[2]);

    //1、创建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
    {
        cerr << "socket error!" << endl;
        return 2;
    }

    //客户端不需要bind!


    //2、发起链接
    struct sockaddr_in server;
    bzero(&server, sizeof(server)); //清0
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(svr_ip.c_str()); //客户端需要绑定明确的ip

    if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
    {
        cout << "connect server failed!" << endl;
        return 3;
    }
    cout << "connect success!" << endl;

    //3、进行正常的业务请求了
    while (true)
    {
        cout << "Plase Enter# ";
        char buffer[1024];
        fgets(buffer, sizeof(buffer)-1, stdin);

        write(sock, buffer, strlen(buffer)); //不要用sizeof()buffer, 因为不一定是要写1024字节

        ssize_t s = read(sock, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;
            cout << "Server echo# " << buffer << endl;
        }

    }
    return 0;
}

tcp_server.cc

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

using namespace std;

void Usage(string proc)
{
    cout << "Usage: " << proc << "port" << endl;
}
void ServiceIO(int new_sock)
{
    while(true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        ssize_t s = read(new_sock, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;
            cout << "client# " << buffer << endl;

            string echo_hello = ">>>server<<<: ";
            echo_hello += buffer;
            write(new_sock, echo_hello.c_str(),echo_hello.size()); 
        }
        else if(s == 0)
        {
            cout << "client quit..." << endl;
        }
        else
        {
            cerr << "read error" << endl;
            break;
        }
    }
}
// ./tcp_server port
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    //1、创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }

    //2、bind 绑定
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY; //可以接收任意ip

    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind cerror: " << errno << endl;
        return 3;
    }

    //3、套接字设置成监听状态
    const int back_log = 5;
    if(listen(listen_sock, back_log) < 0)
    {
        cerr << "listen cerror: " << errno << endl;
        return 4;
    }

    signal(SIGCHLD, SIG_IGN); //子进程退出后会自动释放资源
    while(1)
    {
        struct sockaddr_in perr;
        socklen_t len = sizeof(perr);
        int new_sock = accept(listen_sock, (struct sockaddr*)&perr, &len);
        if(new_sock < 0)
        {
            continue; //建立连接失败,不是直接终止,还需要继续
        }

        cout << "get a new link ..." << new_sock << endl;

        ServiceIO(new_sock); //拿到链接直接干活
    }
    return 0;
}

多进程版本

Makefile

.PHONY:all
all:tcp_client tcp_server

tcp_client:tcp_client.cc
	g++ -o $@ $^ -std=c++11
tcp_server:tcp_server.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f tcp_client tcp_server

tcp_client.cc

客户端和上面单进程版本一样,这里以及下面只改变服务端的代码。

tcp_server.cc

#include<iostream>
#include<string.h>
#include<string>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>

using namespace std;

void Usage(string proc)
{
    cout << "Usage: " << proc << " prot" << endl;
}

void ServiceIO(int new_sock)
{
    //提供服务,我们是一个死循环
    while(true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));//空间初始化为0
        ssize_t s = read(new_sock, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;//将获取的内容当成字符串
            cout << "client# " << buffer << endl;

            string echo_hello = ">>>server<<<: ";
            echo_hello += buffer;
            write(new_sock, echo_hello.c_str(), echo_hello.size());
        }
        else if(s == 0)
        {
            cout << "client quit ..." << endl;
            break;
        }
        else
        {
            cerr << "read error" << endl;
            break;
        }
    }
}

// .tcp_server 8081
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    //tcp server
    //1、创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }

    //2、bind 绑定
    struct sockaddr_in local;
    local.sin_family = AF_INET; //协议种类
    local.sin_port = htons(atoi(argv[1])); //端口号
    local.sin_addr.s_addr = INADDR_ANY; //ip

    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind error: " << errno << endl;
        return 3;
    }

    //3、因为tcp是面向连接的,a. 在通信前,需要建立连接 b. 然后才能通信
        //一定有人主动建立(客户端,需要服务的一方建立连接),
        //一定有人被动接收连接(服务端,童工服务)
        //我们当前写的是一个server,周而复始的不间断的等待客户到来
        //我们要不断的给用户提供一个建立连接的功能

    //设置套接字是listen状态,本质是允许用户连接
    const int back_log = 5;
    if(listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error: " << errno << endl;
        return 4;
    }

    signal(SIGCHLD, SIG_IGN);//子进程退出后会自动释放资源,Linux下才能使用
    for(; ;)
    {
        struct sockaddr_in perr;
        socklen_t len = sizeof(perr);
        int new_sock = accept(listen_sock, (struct sockaddr*)&perr, &len);
        if(new_sock < 0)
        {
            continue;
        }

        cout << "get a new link ...-> " << new_sock << endl;

        pid_t id = fork();
        if(id < 0)
        {
            continue;
        }
        else if(id == 0) //曾经被父进程打开的fd,会被子进程继承
        {
            //child
            close(listen_sock); //关闭掉继承父进程下来的listen_sock,子进程不需要listen_sock
            ServiceIO(new_sock);
            close(new_sock); //子进程退出前关闭套接字
            exit(0);
        }
        else
        {
            //父进程不要关闭listen_sock
            close(new_sock);//父进程不需要,new_sock是给子进程使用
            //不能直接wait,否则父进程可能会一直等子进程,这样就有变成了单进程版本了
        }

        //version 1: 单进程版,没人使用
        //ServiceIO(new_sock);
    }

    return 0;
}

多进程版本2

tcp_server.cc

#include<iostream>
#include<string.h>
#include<string>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>

using namespace std;

void Usage(string proc)
{
    cout << "Usage: " << proc << " prot" << endl;
}

void ServiceIO(int new_sock)
{
    //提供服务,我们是一个死循环
    while(true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));//空间初始化为0
        ssize_t s = read(new_sock, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;//将获取的内容当成字符串
            cout << "client# " << buffer << endl;

            string echo_hello = ">>>server<<<: ";
            echo_hello += buffer;
            write(new_sock, echo_hello.c_str(), echo_hello.size());
        }
        else if(s == 0)
        {
            cout << "client quit ..." << endl;
            break;
        }
        else
        {
            cerr << "read error" << endl;
            break;
        }
    }
}

// .tcp_server 8081
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    //tcp server
    //1、创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }

    //2、bind 绑定
    struct sockaddr_in local;
    local.sin_family = AF_INET; //协议种类
    local.sin_port = htons(atoi(argv[1])); //端口号
    local.sin_addr.s_addr = INADDR_ANY; //ip

    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind error: " << errno << endl;
        return 3;
    }

    //3、因为tcp是面向连接的,a. 在通信前,需要建立连接 b. 然后才能通信
        //一定有人主动建立(客户端,需要服务的一方建立连接),
        //一定有人被动接收连接(服务端,童工服务)
        //我们当前写的是一个server,周而复始的不间断的等待客户到来
        //我们要不断的给用户提供一个建立连接的功能

    //设置套接字是listen状态,本质是允许用户连接
    const int back_log = 5;
    if(listen(listen_sock, back_log) < 0)
    {
        cerr << "bind error: " << errno << endl;
        return 4;
    }

    //signal(SIGCHLD, SIG_IGN);//子进程退出后会自动释放资源,Linux下才能使用

    for(; ;)
    {
        struct sockaddr_in perr;
        socklen_t len = sizeof(perr);
        int new_sock = accept(listen_sock, (struct sockaddr*)&perr, &len);
        if(new_sock < 0)
        {
            continue;
        }

        cout << "get a new link ...-> " << new_sock << endl;

        pid_t id = fork();
        if(id < 0)
        {
            continue;
        }
        else if(id == 0) //曾经被父进程打开的fd,会被子进程继承
        {
            //child
            close(listen_sock); 
            if(fork() > 0) //子进程创建子进程
            {
                exit(0);//子进程退出
            }
            //向后走的进程是孙子进程
            ServiceIO(new_sock);
            close(new_sock); 
            exit(0); //实际上孙子进程就成了孤儿进程,被OS领养了
        }
        else
        {
            //父进程不要关闭listen_sock
            close(new_sock);//父进程不需要,new_sock是给子进程使用
            waitpid(id, nullptr, 0);//这里等待不会被阻塞,因为子进程被创建后立马退出了
        }

        //version 1: 单进程版,没人使用
        //ServiceIO(new_sock);
    }

    return 0;
}

单线程版本

tcp_server.cc

#include<iostream>
#include<string.h>
#include<string>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>

using namespace std;

void Usage(string proc)
{
    cout << "Usage: " << proc << " prot" << endl;
}

void ServiceIO(int new_sock)
{
    //提供服务,我们是一个死循环
    while(true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));//空间初始化为0
        ssize_t s = read(new_sock, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;//将获取的内容当成字符串
            cout << "client# " << buffer << endl;

            string echo_hello = ">>>server<<<: ";
            echo_hello += buffer;
            write(new_sock, echo_hello.c_str(), echo_hello.size());
        }
        else if(s == 0)
        {
            cout << "client quit ..." << endl;
            break;
        }
        else
        {
            cerr << "read error" << endl;
            break;
        }
    }
}
void* HandlerRequest(void* args)
{
    pthread_detach(pthread_self());
    int sock = *(int*)args;
    delete (int*)args;

    ServiceIO(sock);
    close(sock); //线程关闭sock后,主线程就不用关闭了,他们是共享的!
}

// .tcp_server 8081
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    //tcp server
    //1、创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }

    //2、bind 绑定
    struct sockaddr_in local;
    local.sin_family = AF_INET; //协议种类
    local.sin_port = htons(atoi(argv[1])); //端口号
    local.sin_addr.s_addr = INADDR_ANY; //ip

    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind error: " << errno << endl;
        return 3;
    }

    //3、因为tcp是面向连接的,a. 在通信前,需要建立连接 b. 然后才能通信
        //一定有人主动建立(客户端,需要服务的一方建立连接),
        //一定有人被动接收连接(服务端,童工服务)
        //我们当前写的是一个server,周而复始的不间断的等待客户到来
        //我们要不断的给用户提供一个建立连接的功能

    //设置套接字是listen状态,本质是允许用户连接
    const int back_log = 5;
    if(listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error: " << errno << endl;
        return 4;
    }


    for(; ;)
    {
        struct sockaddr_in perr;
        socklen_t len = sizeof(perr);
        int new_sock = accept(listen_sock, (struct sockaddr*)&perr, &len);
        if(new_sock < 0)
        {
            continue;
        }
        uint16_t cli_port = ntohs(perr.sin_port); //网络转主机序列,并考虑大小端
        string cli_ip = inet_ntoa(perr.sin_addr); //将整型的IP转换为字符串风格的IP

        cout << "get a new link ...-> [" << cli_ip << ":" << cli_port << new_sock << endl;

        //曾经主线程打开的fd,新线程能看到,是共享的,共享文件描述符表
        pthread_t tid;
        int* pram = new int(new_sock);
        pthread_create(&tid, nullptr, HandlerRequest, (void*)pram);//创建一个线程

        //pthread_join(tid, nullptr);
        //pthread_detach(tid); //这样写更好

    }

    return 0;
}

线程池版本

Makefile

.PHONY:all
all:tcp_client tcp_server

tcp_client:tcp_client.cc
	g++ -o $@ $^ -std=c++11
tcp_server:tcp_server.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f tcp_client tcp_server

task.hpp

#pragma once

#include <iostream>
#include<string.h>
#include<unistd.h>
using namespace std;

namespace ns_task
{
    class Task
    {
    public:
        Task()
        {}
        Task(int sock)
            :_sock(sock)
        {}
        int Run()
        {
            //提供服务,我们是一个死循环
            //while(true)
            //{
            char buffer[1024];
            memset(buffer, 0, sizeof(buffer));
            ssize_t s = read(_sock, buffer, sizeof(buffer) - 1);
            if(s > 0)
            {
                buffer[s] = 0; //将获取的内容当成字符串
                cout << "client# " << buffer << endl; //输出从客户端读到的内容
                //拉取逻辑
                string echo_string = ">>>server<<<: ";
                echo_string += buffer;

                write(_sock, echo_string.c_str(), echo_string.size());
            }
            else if(s == 0)
            {
                cout << "client quit ..." << endl;
            }
            else
            {
                cerr << "read error" << endl;
            }
            //}
            close(_sock); //处理完任务关闭文件描述符
        }
        ~Task()
        {}
    private:
        int _sock;
    };
}

tcp_client.cc

#include<iostream>
#include<string>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>

using namespace std;
void Usage(string proc)
{
    cout << "Usage: " << proc << "server_ip server_prot" << endl;
}

// ./tcp_client server_ip server_proc
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    //从命令行上获取的
    string svr_ip = argv[1]; 
    uint16_t svr_port = atoi(argv[2]);
    
    //1、创建socket
    int sock = socket(AF_INET, SOCK_STREAM/*stream流*/, 0);
    if(sock < 0)
    {
        cerr << "socket error!" << endl;
        return 2;
    }

    //2、bind 3、listen 4、accept
    //client无需显示的bind,client->server
    //client->connect!
    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;//协议种类
    //该函数做两件事情
    //1、将点分十进制的字符串风格的IP,转换成为4字节IP
    //2、将4byte由主机序列转换成为网络序列
    server.sin_addr.s_addr = inet_addr(svr_ip.c_str()); //server ip
    server.sin_port = htons(svr_port);

    //2、发起连接
    if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
    {
        cout << "connect server failed!" << endl;
        return 3;
    }
    cout<< "connect success!" << endl;

    //进行正常的业务请求了
    while(true)
    {
        cout << "Please Enter# ";
        char buffer[1024];
        fgets(buffer, sizeof(buffer)-1, stdin);

        //buffer初始化为0时米酒可以用sizeof了
        write(sock, buffer, strlen(buffer)); //不要用sizeof(buffer),不一定是写1024字节
        ssize_t s = read(sock, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;
            cout << "server echo# " << buffer << endl;
        }
    }
    return 0;
}

tcp_server.cc

#include<iostream>
#include<string.h>
#include<string>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>
#include"task.hpp"
#include"thread_pool.hpp"

using namespace std;
using namespace ns_task;
using namespace ns_threadpool;

void Usage(string proc)
{
    cout << "Usage: " << proc << " prot" << endl;
}

// .tcp_server 8081
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    //tcp server
    //1、创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sock < 0)
    {
        cerr << "socket error: " << errno << endl;
        return 2;
    }

    //2、bind 绑定
    struct sockaddr_in local;
    local.sin_family = AF_INET; //协议种类
    local.sin_port = htons(atoi(argv[1])); //端口号
    local.sin_addr.s_addr = INADDR_ANY; //ip

    if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cerr << "bind error: " << errno << endl;
        return 3;
    }

    //3、因为tcp是面向连接的,a. 在通信前,需要建立连接 b. 然后才能通信
        //一定有人主动建立(客户端,需要服务的一方建立连接),
        //一定有人被动接收连接(服务端,童工服务)
        //我们当前写的是一个server,周而复始的不间断的等待客户到来
        //我们要不断的给用户提供一个建立连接的功能

    //设置套接字是listen状态,本质是允许用户连接
    const int back_log = 5;
    if(listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error: " << errno << endl;
        return 4;
    }


    for(; ;)
    {
        struct sockaddr_in perr;
        socklen_t len = sizeof(perr);
        int new_sock = accept(listen_sock, (struct sockaddr*)&perr, &len);
        if(new_sock < 0)
        {
            continue;
        }
        uint16_t cli_port = ntohs(perr.sin_port); //网络转主机序列,并考虑大小端
        string cli_ip = inet_ntoa(perr.sin_addr); //将整型的IP转换为字符串风格的IP

        cout << "get a new link ...-> [" << cli_ip << ":" << cli_port << new_sock << endl;
        Task t(new_sock); //将接收到客户端的sock交给任务构造,以便线程处理任务
        ThreadPool<Task>::GetInstance()->PushTask(t); //任务放入线程池,这时候可以t,Run了
    }

    return 0;
}

thread_pool.hpp

#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <unistd.h>
#include <pthread.h>
using namespace std;

namespace ns_threadpool
{
    const int g_num = 5; //创建线程数量

    template <class T>
    class ThreadPool
    {
    private:
        int _num;
        queue<T> _task_queue; //该成员是一个临界资源

        pthread_mutex_t _mtx;
        pthread_cond_t _cond;

        static ThreadPool<T>* ins;

    private:
        //构造函数必须得实现,但是必须要私有化
        ThreadPool(int num = g_num)
            : _num(num)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }
        ThreadPool(const ThreadPool<T>& tp) = delete;
        //赋值语句
        ThreadPool<T> operator=(const ThreadPool<T>& tp) = delete;

    public:
        static ThreadPool<T>* GetInstance()
        {
            //静态锁不用手动初始化和销毁
            static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
            //当前单列对象还没有被创建
            if(ins == nullptr) //双判定,减少锁的争用,提高获取单例的效率
            {
                pthread_mutex_lock(&lock);
                if(ins == nullptr)
                {
                    ins = new ThreadPool<T>();
                    ins->InitThreadPool(); //构造对象后初始化
                    cout << "首次加载对象" << endl;
                }
                pthread_mutex_unlock(&lock);
            }
            return ins;
        }
        void Lock()
        {
            pthread_mutex_lock(&_mtx);
        }
        void Unlock()
        {
            pthread_mutex_unlock(&_mtx);
        }
        void Wait()
        {
            pthread_cond_wait(&_cond, &_mtx);
        }
        void Wakeup()
        {
            pthread_cond_signal(&_cond);
        }
        bool IsEmpty()
        {
            return _task_queue.empty();
        }

    public:
        //在类中要让线程执行类内成员方法(参数个数匹配问题),是不可行的!
        //必须让线程执行静态方法
        static void* Rountine(void *args) //传this指针
        {
            pthread_detach(pthread_self()); //线程分离
            ThreadPool<T>* tp = (ThreadPool<T>*)args;

            while (true)
            {
                tp->Lock();
                while (tp->IsEmpty())
                {
                    //任务队列为空,线程该做些什么呢?
                    tp->Wait();
                }
                //到这里,该任务队列中一定有任务了
                T t;
                tp->PopTask(&t);
                tp->Unlock();

                t.Run();//拿完任务后就执行
            }
        }
        void InitThreadPool()
        {
            pthread_t tid;
            for (int i = 0; i < _num; i++)
            {
                pthread_create(&tid, nullptr, Rountine, (void *)this);
            }
        }

        void PushTask(const T &in)
        {
            Lock();
            _task_queue.push(in);
            Unlock();
            Wakeup(); 
        }
        void PopTask(T *out)
        {
            *out = _task_queue.front();
            _task_queue.pop(); //外部往线程池放任务,剩余的它会解决,Task()是处理方式
        }
        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_cond);
        }
    };
    template<class T>
    ThreadPool<T>* ThreadPool<T>::ins = nullptr;
}

测试结果(只展线程池版本的测试结果)

        这里我们在处理任务时没有写死循环,所以任务只处理一次,剩下的就不在处理了,但是链接还在。 

总结

服务端

1、创建套接字

int listen_sock = socket(AF_INET, SOCK_STREAM, 0);

2、绑定ip和端口号

bind(listen_sock, (struct sockaddr*)&local, sizeof(local))

3、设置监听套接字

listen(listen_sock, back_log)

4、接收请求

int new_sock = accept(listen_sock, (struct sockaddr*)&perr, &len)

5、提供服务

......

客户端

1、创建套接字

int sock = socket(AF_INET, SOCK_STREAM/*stream流*/, 0)

2、发起链接

connect(sock, (struct sockaddr*)&server, sizeof(server))

3、业务请求

......

         可能博主没有对代码一行一行分析,但是如果大家有什么看不懂的地方或者我有错误的地方的话,欢迎大家私信我~

看到这里,给博主点个赞吧~

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值