防止客户端套接字程序崩溃cpp google protobuf

时间:2019-07-14 17:49:09

标签: c++ multithreading sockets boost protocol-buffers

TL; DR

我需要一段代码才能正确进行多线程处理,并且对我返回或崩溃。我该怎么办才能消耗任何returntry/catch块,以使main()不结束?

故事

我已经将cpp与Google protobufs一起使用了很短的时间,但遇到了一个问题,但我无法尝试解决。我开始使用Google protobufs和this linkcpp学习服务器-客户端套接字程序。我首先通过编辑.proto文件将proto3文件转换为.proto。然后,我可以创建一个CMake文件来编译程序。

我的问题

我需要制作一个新的客户端程序,以便在用server.cpp取消Ctrl+c时,客户端程序 NOT 不会崩溃,退出,段错误等。基本上,当服务器死机时,客户端应等待服务器重新联机。

我尝试了什么?

我尝试了两件事:

  1. 我将整个套接字创建(包括变量创建)放入了一个巨大的while循环中。然后,我在开始时有一个延迟,这样,如果无法建立连接,程序将等待,然后重试。
  2. 使用boost的多线程

我尝试使用boost::threadboost::future创建一个boost::async。我在main()中有一个循环,它将创建一个boost::async()对象的矢量(长度为1),并用传递到boost::async()构造函数中的方法来调用程序的其余部分。然后,我简单地用boost::wait_for_all()begin()在迭代器上调用end()This is where我有期货的想法。这是语法示例:

while (true)
{
    printingmutex.lock();
    cout << "------------------\nStarting initialization of socket.\n" << endl;
    printingmutex.unlock();
    // Now I create the threads.
    std::vector<boost::future<void>> futures;
    futures.push_back(boost::async([host_name]{ doSocketswag(host_name); }));
    boost::wait_for_all(futures.begin(), futures.end());
    // Delay function after disconnection, implementation irrelevant to the question
    sleepApp(2000); // 2 seconds
}

为简便起见,这是新的.proto文件,可转换为proto3

syntax = "proto3";
message log_packet {
  fixed64 log_time = 1;
  fixed32 log_micro_sec = 2;
  fixed32 sequence_no = 3;
  fixed32 shm_app_id = 4;
  string packet_id = 5;
  string log_level= 6;
  string log_msg= 7;
}

为什么如此重要?

这很重要,因为当我解决此问题后,我会将客户端程序移植到Ubuntu盒以外的其他平台,但是我仍将使用Ubuntu盒,并且我需要能够运行程序在消息发送之外进行操作。我还将能够向程序中添加GUI,因此我将需要能够将套接字线程与程序的其余部分分开,从而防止不良连接杀死客户端程序。

总结

如何使客户端程序成为多线程,以使套接字通信不会发生

  1. 杀死程序
  2. 通过main()回来
  3. 只杀死套接字线程,没有别的?

一个基本的,没有多余的装饰的实现即可显示套接字与主程序分开运行。

另外,为了加快响应速度,我有一个CMake文件来编译clientserver.proto文件。我正在分享它,因此你们可以花更多的时间在实际回答问题上,而不是专门研究编译代码。

# Compiling the program, assuming all of the source files are in the same directory:
# mkdir build
# cd build
# cmake ..
# cmake --build . -- -j8 -l8

set(PROJECT_NAME CppProtobufdemo)
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(${PROJECT_NAME} LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_definitions(-std=c++11)
# CMAKE_CURRENT_SOURCE_DIR - This is the directory where CMake is running.
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)

find_package( Boost REQUIRED system thread timer chrono)
if(Boost_FOUND)
    message("Boost was found.")
    message(${Boost_INCLUDE_DIR})
endif()

include_directories(${CMAKE_CURRENT_BINARY_DIR})

set(THE_USER $ENV{USER})
message("This is the com user_:" ${THE_USER})

set(PROGRAMS_TO_COMPILE
server
client
)

# These lines are for autogenerating code from the *.proto files with CMake.
find_package(Protobuf REQUIRED)
if(PROTOBUF_FOUND)
    message("Google Protobuf has been found.")
endif()
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# https://cmake.org/cmake/help/git-master/module/FindProtobuf.html
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS message.proto)
# I want to see if I can access these proto files from python scripts
protobuf_generate_python(PROTO_PY message.proto)

foreach(aprogram ${PROGRAMS_TO_COMPILE})
    add_executable(${aprogram} ${PROTO_SRCS} ${PROTO_HDRS} ${PROJECT_SOURCE_DIR}/${aprogram}.cpp )
    target_link_libraries(${aprogram} ${Boost_LIBRARIES})
    target_link_libraries(${aprogram} ${PROTOBUF_LIBRARIES})
endforeach()

另一件事:在此过程中,我总是摆脱了goto FINISH语句。

如果您有任何意见或后续问题,我很乐意答复。

编辑:

我尝试将这行换行

boost::wait_for_all(futures.begin(), futures.end());

在这样的尝试中捕获:

try {
    // Now that I have created all of the threads, I need to wait for them to finish.
        boost::wait_for_all(futures.begin(), futures.end());
    }
    catch (const std::exception& e)
    {
    // Just keep swimming
    cout << "Thread died" << endl;
}

现在错误消息不被推送。相反,该程序只是退出,没有任何错误或任何消息。 我正在使用return语句终止线程方法,但是它们不应终止main()线程-除非我缺少其他内容。这是我用来演示此“修复程序”的客户端代码:

// This is for the futures functionality
#define BOOST_THREAD_VERSION 4
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
// cmake --build . -- -j8 -l8
#include "message.pb.h"
#include <iostream>
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
//
#include <boost/thread.hpp>
#include <chrono>
#include <thread>

using namespace google::protobuf::io;
using namespace std;

void doSocketSwag(void);

void sleepApp(int millis)
{
    std::this_thread::sleep_for(std::chrono::milliseconds{millis});
}

// Boost sleep function
void wait(int milliseconds)
{
    boost::this_thread::sleep_for(boost::chrono::milliseconds{milliseconds});
}

int main(int argv, char** argc)
{
    // I will put this whole thing into a while loop
    while (true)
    {
        cout << "Creating threads." << endl;
        std::vector<boost::future<void>> futures;
        // The socket thread
        futures.push_back(boost::async([]{ doSocketSwag(); }));
        // Now I need to catch exceptions
        try {
            // Now that I have created all of the threads, I need to wait for them to finish.
            boost::wait_for_all(futures.begin(), futures.end());
        }
        catch (const std::exception& e)
        {
            // Just keep swimming
            cout << "Thread died" << endl;
        }
    }
    //delete pkt;
    //FINISH:
    //close(hsock);
    return 0;
}

//
void doSocketSwag(void)
{
    log_packet payload;
    payload.set_log_time(10);
    payload.set_log_micro_sec(10);
    payload.set_sequence_no(1);
    payload.set_shm_app_id(101);
    payload.set_packet_id("TST");
    payload.set_log_level("DEBUG");
    payload.set_log_msg("What shall we say then");
    //cout<<"size after serilizing is "<<payload.ByteSize()<<endl;
    int siz = payload.ByteSize()+4;
    char *pkt = new char [siz];
    google::protobuf::io::ArrayOutputStream aos(pkt,siz);
    CodedOutputStream *coded_output = new CodedOutputStream(&aos);
    coded_output->WriteVarint32(payload.ByteSize());
    payload.SerializeToCodedStream(coded_output);

    int host_port= 1101;
    char* host_name="127.0.0.1";

    struct sockaddr_in my_addr;

    char buffer[1024];
    int bytecount;
    int buffer_len=0;

    int hsock;
    int * p_int;
    int err;

    hsock = socket(AF_INET, SOCK_STREAM, 0);
    if(hsock == -1){
        printf("Error initializing socket %d\n",errno);
        //goto FINISH;
        return;
    }

    p_int = (int*)malloc(sizeof(int));
    *p_int = 1;

    if( (setsockopt(hsock, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1 ) || (setsockopt(hsock, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1 ) ){
        printf("Error setting options %d\n",errno);
        free(p_int);
        //goto FINISH;
        return;
    }
    free(p_int);

    my_addr.sin_family = AF_INET ;
    my_addr.sin_port = htons(host_port);

    memset(&(my_addr.sin_zero), 0, 8);
    my_addr.sin_addr.s_addr = inet_addr(host_name);
    if( connect( hsock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 ){
        if((err = errno) != EINPROGRESS){
            fprintf(stderr, "Error connecting socket %d\n", errno);
            //goto FINISH;
            return;
        }
    }

    while (true)
    {
        if( (bytecount=send(hsock, (void *) pkt,siz,0))== -1 ) {
            // THIS is where the program dies.
            fprintf(stderr, "Error sending data %d\n", errno);
            //goto FINISH;
            break;
        }
        //printf("Sent bytes %d\n", bytecount);
        //usleep(1);
        sleepApp(1000);
        cout << "Thread slept for 1 second." << endl;
    }
}

如果return语句那么糟糕,我该如何替换它们?

1 个答案:

答案 0 :(得分:0)

这是伙计们

查看Jeremy Friesner提供的链接后,我发现此问题绝对不是原始问题。事实证明,套接字能够发布信号来中断cpp程序,需要用户进行处理。 link provided解释了它是如何工作的。基本上,我只需要处理一个中断条件就可以使程序继续运行:程序中的逻辑是正确的,但是我对套接字的了解还不够多,无法询问SIGPIPESIG_IGN解决我的问题。我在下面显示了两个解决问题的示例,类似于Jeremy Friesner的链接。多线程现在也可以正常工作,并且观看起来很棒。 ?

在此示例中,我直接从链接中复制解决方案:

// This is for the futures functionality
//#define BOOST_THREAD_VERSION 4
#define BOOST_THREAD_PROVIDES_FUTURE
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
// cmake --build . -- -j8 -l8
#include "message.pb.h"
#include <iostream>
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
//
#include <boost/thread.hpp>
#include <chrono>
#include <thread>
#include <signal.h>

using namespace google::protobuf::io;
using namespace std;

void doSocketSwag(void);

void sleepApp(int millis)
{
    std::this_thread::sleep_for(std::chrono::milliseconds{millis});
}

// Boost sleep function
void wait(int milliseconds)
{
    boost::this_thread::sleep_for(boost::chrono::milliseconds{milliseconds});
}

log_packet payload;
int siz;
char *pkt;
google::protobuf::io::ArrayOutputStream * aos;
CodedOutputStream *coded_output;

int host_port= 1101;
char* host_name="127.0.0.1";

struct sockaddr_in my_addr;

char buffer[1024];
int bytecount;
int buffer_len=0;

int hsock;
int * p_int;
int err;

// This boolean controls whether of not the socket thread loops.
std::atomic_bool dosockets{true};

// This is the mutex for printing
std::mutex printlock;

// Try to do client socket program without server.
int main(int argv, char** argc)
{
    // Registering the singal PIPE ignore
    signal(SIGPIPE, SIG_IGN);

    // We build the packet.
    payload.set_log_time(10);
    payload.set_log_micro_sec(10);
    payload.set_sequence_no(1);
    payload.set_shm_app_id(101);
    payload.set_packet_id("TST");
    payload.set_log_level("DEBUG");
    payload.set_log_msg("What shall we say then");

    // Now I fill in the socket fields
    siz = payload.ByteSize()+4;
    pkt = new char [siz];
    aos = new google::protobuf::io::ArrayOutputStream(pkt,siz);
    coded_output = new CodedOutputStream(aos);

    // Now we do methods on the objects
    coded_output->WriteVarint32(payload.ByteSize());
    payload.SerializeToCodedStream(coded_output);

    // I will put this whole thing into a while loop
    while (true)
    {
        printlock.lock();
        cout << "Creating threads." << endl;
        printlock.unlock();
        std::vector<boost::future<void>> futures;
        // The socket thread
        futures.push_back(boost::async([]{ doSocketSwag(); }));
        boost::wait_for_all(futures.begin(), futures.end());
    }
    //delete pkt;
    //FINISH:
    //close(hsock);
    return 0;
}

//
void doSocketSwag(void)
{
    // 
    while (dosockets)
    {
        printlock.lock();
        cout << "Waiting to try again." << endl;
        printlock.unlock();
        sleepApp(1000);

        hsock = socket(AF_INET, SOCK_STREAM, 0);
        if(hsock == -1){
            printlock.lock();
            printf("Error initializing socket %d\n",errno);
            printlock.unlock();
            //goto FINISH;
            continue;
        }

        p_int = (int*)malloc(sizeof(int));
        *p_int = 1;

        if( (setsockopt(hsock, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1 ) || (setsockopt(hsock, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1 ) ){
            printlock.lock();
            printf("Error setting options %d\n",errno);
            printlock.unlock();
            free(p_int);
            //goto FINISH;
            continue;
        }
        free(p_int);

        my_addr.sin_family = AF_INET ;
        my_addr.sin_port = htons(host_port);

        memset(&(my_addr.sin_zero), 0, 8);
        my_addr.sin_addr.s_addr = inet_addr(host_name);
        if( connect( hsock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 ){
            if((err = errno) != EINPROGRESS)
            {
                printlock.lock();
                fprintf(stderr, "Error connecting socket %d\n", errno);
                printlock.unlock();
                //goto FINISH;
                continue;
            }
        }

        while (true)
        {
            if( (bytecount=send(hsock, (void *) pkt,siz,0))== -1 )
            {
                printlock.lock();
                fprintf(stderr, "Error sending data %d\n", errno);
                printlock.unlock();
                //goto FINISH;
                //break;
                close(hsock);
                break;
            }
            //printf("Sent bytes %d\n", bytecount);
            //usleep(1);
            sleepApp(1000);
            printlock.lock();
            cout << "Thread slept for 1 second." << endl;
            printlock.unlock();
        }
    }
}

然后是在示例中,当SIGPIPE信号发出时,我创建了一个回调函数:

// This is for the futures functionality
//#define BOOST_THREAD_VERSION 4
#define BOOST_THREAD_PROVIDES_FUTURE
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
// cmake --build . -- -j8 -l8
#include "message.pb.h"
#include <iostream>
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
//
#include <boost/thread.hpp>
#include <chrono>
#include <thread>
#include <signal.h>

using namespace google::protobuf::io;
using namespace std;

void doSocketSwag(void);

void sleepApp(int millis)
{
    std::this_thread::sleep_for(std::chrono::milliseconds{millis});
}

// Boost sleep function
void wait(int milliseconds)
{
    boost::this_thread::sleep_for(boost::chrono::milliseconds{milliseconds});
}

log_packet payload;
int siz;
char *pkt;
google::protobuf::io::ArrayOutputStream * aos;
CodedOutputStream *coded_output;

int host_port= 1101;
char* host_name="127.0.0.1";

struct sockaddr_in my_addr;

char buffer[1024];
int bytecount;
int buffer_len=0;

int hsock;
int * p_int;
int err;

// This boolean controls whether of not the socket thread loops.
std::atomic_bool dosockets{true};

// This is the mutex for printing
std::mutex printlock;

// The signal pipe error
void my_handler(int input)
{
    // Simply print
    printlock.lock();
    cout << "I got into the pipe handler LOOK AT ME WOOO HOOO." << endl;
    printlock.unlock();
}

// Try to do client socket program without server.
int main(int argv, char** argc)
{
    // Registering the singal PIPE ignore
    struct sigaction sigIntHandler;
    sigIntHandler.sa_handler = my_handler;
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;
    sigaction(SIGPIPE, &sigIntHandler, NULL);

    // We build the packet.
    payload.set_log_time(10);
    payload.set_log_micro_sec(10);
    payload.set_sequence_no(1);
    payload.set_shm_app_id(101);
    payload.set_packet_id("TST");
    payload.set_log_level("DEBUG");
    payload.set_log_msg("What shall we say then");

    // Now I fill in the socket fields
    siz = payload.ByteSize()+4;
    pkt = new char [siz];
    aos = new google::protobuf::io::ArrayOutputStream(pkt,siz);
    coded_output = new CodedOutputStream(aos);

    // Now we do methods on the objects
    coded_output->WriteVarint32(payload.ByteSize());
    payload.SerializeToCodedStream(coded_output);

    // I will put this whole thing into a while loop
    while (true)
    {
        printlock.lock();
        cout << "Creating threads." << endl;
        printlock.unlock();
        std::vector<boost::future<void>> futures;
        // The socket thread
        futures.push_back(boost::async([]{ doSocketSwag(); }));
        boost::wait_for_all(futures.begin(), futures.end());
    }
    //delete pkt;
    //FINISH:
    //close(hsock);
    return 0;
}

//
void doSocketSwag(void)
{
    // 
    while (dosockets)
    {
        printlock.lock();
        cout << "Waiting to try again." << endl;
        printlock.unlock();
        sleepApp(1000);

        hsock = socket(AF_INET, SOCK_STREAM, 0);
        if(hsock == -1){
            printlock.lock();
            printf("Error initializing socket %d\n",errno);
            printlock.unlock();
            //goto FINISH;
            continue;
        }

        p_int = (int*)malloc(sizeof(int));
        *p_int = 1;

        if( (setsockopt(hsock, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1 ) || (setsockopt(hsock, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1 ) ){
            printlock.lock();
            printf("Error setting options %d\n",errno);
            printlock.unlock();
            free(p_int);
            //goto FINISH;
            continue;
        }
        free(p_int);

        my_addr.sin_family = AF_INET ;
        my_addr.sin_port = htons(host_port);

        memset(&(my_addr.sin_zero), 0, 8);
        my_addr.sin_addr.s_addr = inet_addr(host_name);
        if( connect( hsock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 ){
            if((err = errno) != EINPROGRESS)
            {
                printlock.lock();
                fprintf(stderr, "Error connecting socket %d\n", errno);
                printlock.unlock();
                //goto FINISH;
                continue;
            }
        }

        while (true)
        {
            if( (bytecount=send(hsock, (void *) pkt,siz,0))== -1 )
            {
                printlock.lock();
                fprintf(stderr, "Error sending data %d\n", errno);
                printlock.unlock();
                //goto FINISH;
                //break;
                close(hsock);
                break;
            }
            //printf("Sent bytes %d\n", bytecount);
            //usleep(1);
            sleepApp(1000);
            printlock.lock();
            cout << "Thread slept for 1 second." << endl;
            printlock.unlock();
        }
    }
}

服务器代码与问题开头提供的original link相同。

谢谢你们的评论。他们将我引向我的答案,以及cpp的全新领域,我不知道是什么signal

(如果你们想继续发表评论,那也很好。我认为这个问题已经得到回答。)