当前位置: 首页 > news >正文

网络套接字实现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、业务请求

......

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

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

相关文章:

  • 一个非教条式的TDD例子
  • Spring 整合 MyBatis
  • Verilog 有符号数详解(含代码验证)
  • 今天是圣诞节, 要打印一个漂亮的圣诞树送给想象中的女朋友,请你帮助他实现梦想。
  • 同样是测试工程师,月薪8k的功能测试和月薪14k的自动化测试,差在了那里?
  • k8s 认证机制源码分析
  • Java-KoTime:接口耗时监测与邮件通知接口耗时情况
  • 【Linux】Linux系统编程(入门与系统编程)(一)(环境搭建、常见指令以及权限理解)
  • 【JavaScript高级】函数相关知识:函数、纯函数、柯里化、严格模式
  • Android多渠道之自定义apk输出
  • Day03 Css的学习深入 background-X属性
  • aardio + Python 可视化快速开发桌面程序,一键生成独立 EXE
  • 分享两款智慧物业系统源码,前后端分离,前端VUE,Uni-app框架
  • 新手看过来----讨厌的运算符
  • Matlab中importdata函数的使用
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • C++入门教程(10):for 语句
  • Cumulo 的 ClojureScript 模块已经成型
  • ES6核心特性
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • JavaScript 奇技淫巧
  • JS题目及答案整理
  • Magento 1.x 中文订单打印乱码
  • Median of Two Sorted Arrays
  • php ci框架整合银盛支付
  • Python实现BT种子转化为磁力链接【实战】
  • Spark RDD学习: aggregate函数
  • spring-boot List转Page
  • SpringBoot几种定时任务的实现方式
  • Spring框架之我见(三)——IOC、AOP
  • Vim 折腾记
  • Web Storage相关
  • 每天一个设计模式之命令模式
  • 前端技术周刊 2019-02-11 Serverless
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 什么软件可以剪辑音乐?
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 线性表及其算法(java实现)
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (02)Hive SQL编译成MapReduce任务的过程
  • (2)STM32单片机上位机
  • (LeetCode 49)Anagrams
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (实战篇)如何缓存数据
  • (转)甲方乙方——赵民谈找工作
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器