c++实现多对多生产者消费者和socket连用

背景:服务端实现一个多对多的生产者消费者模式,监听某个端口,一旦有client连入,将socket存入队列。通知消费者进程进行消费。在消费者进程中,拿到客户端的socket,接收客户端的信息,并将接收到的数据返回服务端。

难点:锁,server main函数如何生成多对多的线程(这是个大坑,放的位置或逻辑不对极易退化成一对一模式,在实践中,本人将监听放入生产者函数中,进行循环监听,main函数类比网上的设计,结果导致多个客户端链接时,当第一个client处在输入状态,另起client时,即使后面的client输入完成也得不到响应,得等待第一个client输入处理结束,改了许久才实现,仅以此记录自己踩过的坑)

pthread_t tid_produce[THREAD_NUM], tid_consume[THREAD_NUM];
for(int i=0; i < THREAD_NUM; i++){
     pthread_create(&tid_produce[i], NULL, &produce, NULL);
}
for(int i=0; i<THREAD_NUM; i++){
    pthread_create(&tid_consume[i], NULL, &consume, NULL);
}
for(int i=0; i<THREAD_NUM; i++){
     pthread_join(tid_produce[i], NULL);
}
for(int i=0; i<THREAD_NUM; i++){
     pthread_join(tid_consume[i], NULL);
}

客户端代码【简单的socket编程】

#include <iostream>
#include <unistd.h>
#include <strings.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT 9090
#define MAXDATASIZE 100

char receiveM[100];
char sendM[100];

int main(int argc, char *argv[]) {
    int fd, numbytes;
    struct hostent *he;
    struct sockaddr_in server;
    if (argc != 2) {
        std::cout<<"Usage args <IP Address>"<<std::endl;
        exit(1);
    }
    // 通过函数 gethostbyname()获得字符串形式的ip地址,并赋给he
    if ((he = gethostbyname(argv[1])) == NULL) {
        std::cout<<"gethostbyname() error"<<std::endl;
        exit(1);
    }
    // 产生套接字fd
    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        std::cout<<"socket() error"<<std::endl;
        exit(1);
    }
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr = *((struct in_addr *) he->h_addr);
    if (connect(fd, (struct sockaddr *) &server, sizeof(struct sockaddr)) == -1) {
        std::cout<<"connect() error"<<std::endl;
        exit(1);
    }
    // 向服务器发送数据
    std::cout<<"send message to server:";
    fgets(sendM, 100, stdin);
    int send_le;
    send_le = strlen(sendM);
    sendM[send_le - 1] = '\0';
    send(fd, sendM, strlen(sendM), 0);
    // 从服务器接收数据
    if ((numbytes = recv(fd, receiveM, MAXDATASIZE, 0)) == -1) {
        std::cout<<"recv() error";
        exit(1);
    }
    std::cout<<"receive message from server:"<<receiveM<<std::endl;
    close(fd);
}

多对多生产者消费者代码

#include <unistd.h>
#include <cstdlib>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static const int bufferSize = 4; // Item buffer size.
//static const int totalProducts = 10;   // How many items we plan to produce.

struct ItemRepository {
    void *item_buffer[bufferSize];
    size_t read_position;
    size_t write_position;
    size_t produced_item_counter;
    size_t consumed_item_counter;
    std::mutex mtx;
    std::mutex produced_item_counter_mtx;
    std::mutex consumed_item_counter_mtx;
    std::condition_variable repo_not_full;
    std::condition_variable repo_not_empty;
} gItemRepository;

typedef struct ItemRepository ItemRepository;


void ProduceItem(ItemRepository *ir, void *item) {
    std::unique_lock <std::mutex> lock(ir->mtx);
    while (((ir->write_position + 1) % bufferSize) == ir->read_position) {
        std::cout << "Producer is waiting for an empty slot...\n";
        (ir->repo_not_full).wait(lock);
    }

    (ir->item_buffer)[ir->write_position] = item;
    (ir->write_position)++;
    if (ir->write_position == bufferSize)
        ir->write_position = 0;

    (ir->repo_not_empty).notify_all();
    lock.unlock();
}

void *ConsumeItem(ItemRepository *ir) {
    void *data;
    std::unique_lock <std::mutex> lock(ir->mtx);
    while (ir->write_position == ir->read_position) {
        std::cout << "Consumer is waiting for items...\n";
        (ir->repo_not_empty).wait(lock);
    }

    data = (ir->item_buffer)[ir->read_position];
    (ir->read_position)++;
    if (ir->read_position >= bufferSize)
        ir->read_position = 0;
    (ir->repo_not_full).notify_all();
    lock.unlock();

    return data;
}

void *ProducerTask(void *args) {
    std::unique_lock <std::mutex> lock(gItemRepository.produced_item_counter_mtx);
    if (gItemRepository.produced_item_counter < totalProducts) {
        ++(gItemRepository.produced_item_counter);
        ProduceItem(&gItemRepository, args);
        std::cout << "Producer thread  is producing the " << gItemRepository.produced_item_counter
                  << "^th item" << std::endl;
    }
    lock.unlock();
}

void *ConsumerTask(void *args) {
    void *item;
    std::unique_lock <std::mutex> lock(gItemRepository.consumed_item_counter_mtx);
    if (gItemRepository.consumed_item_counter < totalProducts) {
        item = ConsumeItem(&gItemRepository);
        ++(gItemRepository.consumed_item_counter);
        std::cout << "Consumer thread  is consuming the " << gItemRepository.consumed_item_counter << "^th item"
                  << std::endl;
    }
    lock.unlock();
    int fd = *(int *) item;
    char buf[100];
    if (recv(fd, buf, 100, 0) == -1) {//receive data
        exit(1);
    }
    std::cout << "receive msg**************" << buf << std::endl;
    std::string res = buf;
    send(fd, res.c_str(), res.length(), 0);
    std::cout << "send finished..." << std::endl;
}

void InitItemRepository(ItemRepository *ir) {
    ir->write_position = 0;
    ir->read_position = 0;
    ir->produced_item_counter = 0;
    ir->consumed_item_counter = 0;
}

server函数端

#include <iostream>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<pthread.h>
#include "configParams.h"
#include "producerConsumerModel.h"

#define BACKLOG 1
using std::cout;
using std::endl;
using std::string;
using std::exception;

static const int THREAD_NUM = 4;
int main(int argc, char *argv[]) {
    string ip = "*.*.*.*";// change it
    int port = 9090;
    cout << ip << "  " << port << endl;
    int listenfd;
    struct sockaddr_in server;
    int connectfd;
    struct sockaddr_in client;
    socklen_t sin_size;
    sin_size = sizeof(struct sockaddr_in);

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        exit(1);
    }
    int opt = SO_REUSEADDR;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = htonl(INADDR_ANY);//local any ip(one machine has many network card,one card has many ip)
    // bind
    if (bind(listenfd, (struct sockaddr *) &server, sizeof(struct sockaddr)) == -1) {
        exit(1);
    }
    // listen
    if (listen(listenfd, BACKLOG) == -1) {
        exit(1);
    }
    //----------must notice as follow way------------------------
    InitItemRepository(&gItemRepository);
    while (1) {
        // accept
        pthread_t pdthread, cmthread; //define a pthread
        if ((connectfd = accept(listenfd, (struct sockaddr *) &client, &sin_size)) == -1) {
            exit(1);
        }
        pthread_create(&pdthread, NULL, ProducerTask, (void *) &connectfd);
        pthread_create(&cmthread, NULL, ConsumerTask, NULL);
    }
    close(listenfd);

}

参考文献

http://www.cnblogs.com/haippy/p/3252092.html
https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter11-Application/11.1%20Producer-Consumer-solution.md

猜你喜欢

转载自blog.csdn.net/banana1006034246/article/details/83826527