45

Qt下使用fork创建进程并使用socket通信

 4 years ago
source link: https://www.tuicool.com/articles/AbaQ3qq
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

把最近工作总结下。

之前在嵌入python解释器的过程中,我们没有处理这样一种情况:当 Python 解释器正在执行一个阻塞操作 ( 比如 socket server 在监听一个客户端连入 ) ,这时我们需要终止解释器的运行,该如何操作呢?

在Qt线程中不容易实现该功能,如果在 socket 监听时终止 python 解释器,那么再次运行时端口就会显示被占用,因为资源没有清理。为了解决这种情况,我们让解释器运行在一个进程中。这样在需要停止时,我们可以发送 kill 信号终止进程。

老规矩,接下来上码:

#include "mainwindow.h"

#include "ui_mainwindow.h"

#include <QThread>

#include <QDebug>

#include <signal.h>

#include <sys/types.h>

#include <unistd.h>    //close head file

#include <sys/wait.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int childProcessId = -1;

int client_sockfd;

void sigint(int);

void sigint(int signal)

{

if(SIGINT == signal)

{

qDebug() << "My DADDY has Killed me!!!";

exit(0);

}

}

MainWindow::MainWindow(QWidget *parent) :

QMainWindow(parent),

ui(new Ui::MainWindow)

{

ui->setupUi(this);

//tcp server

processServer = new QTcpServer();

connect(processServer, SIGNAL(newConnection()),

this, SLOT(slot_recvClientConnect()));

if(!processServer->listen(QHostAddress::Any, 8866))

{

// 监听本地主机的 88 66 端口,如果出错就输出错误信息,并关闭

qDebug() <<"error message"<<processServer->errorString();

processServer->close();

}

///////////////client

struct sockaddr_in remote_addr; // 服务器端网络地址结构体

memset(&remote_addr,0,sizeof(remote_addr)); // 数据初始化 -- 清零

remote_addr.sin_family=AF_INET; // 设置为 IP 通信

remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");// 服务器 IP 地址

remote_addr.sin_port=htons(8866); // 服务器端口号

/* 创建客户端套接字 --IPv4 协议,面向连接通信, TCP 协议 */

if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)

{

perror("socket");

return;

}

/* 将套接字绑定到服务器的网络地址上 */

if(::connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0)

{

perror("connect");

return;

}

}

MainWindow::~MainWindow()

{

processServer->close();

delete ui;

delete processServer;

}

void MainWindow::slot_recvClientConnect()

{

qDebug() << "accept connect";

clientConnect = processServer->nextPendingConnection();

connect(clientConnect, SIGNAL(readyRead()),

this, SLOT(slot_readClientData()));

}

void MainWindow::slot_readClientData()

{

QString str;

str = clientConnect->readAll();

clientConnect->write("hello");

qDebug() << "client data>>:" << str  ;

}

void MainWindow::on_pbn_start_clicked()

{

pid_t pid;

pid = fork();

if(pid < 0)

{

qDebug() << "creat process faild!";

}

else if((pid_t)0 == pid)  //child process,pid is child pid

{

signal(SIGINT, sigint);

qDebug() << "this is client proccess!";

char buf1[] = "socket client";

while(1)

{

send(client_sockfd,buf1,strlen(buf1),0);

QThread::sleep(3);

}

}

else                  // parent process

{

childProcessId = pid;

qDebug() << "child process pid is " << childProcessId;

}

}

void MainWindow::on_pbn_stop_clicked()

{

if(-1 != childProcessId)

{

pid_t pidClear;

kill(childProcessId, SIGINT);

QThread::usleep(500);

do{

pidClear = waitpid(childProcessId,NULL,WNOHANG);

if(-1 == pidClear)

{

qDebug() << "clear  error!!!";

}

if(childProcessId == pidClear)

{

qDebug() << "clear   success!!!";

}

QThread::usleep(500);

}while(0 == pidClear);

childProcessId = -1;

}

}

程序环境是ubuntu + Qt。

简单介绍下程序的主体构成:构造函数中使用c创建客户端,使用 Qt 的类创建服务器。

界面点击 start 按钮时,使用 fork 创建子进程,在进程中客户端向服务器发送数据。点击 stop 按钮时,父进程向子进程发出 kill 信号,由于子进程注册了中断信号,所以会执行信号函数中的操作。之后父进程使用  waitpid(childProcessId,NULL,WNOHANG) 等待清理子进程,之后进程资源被释放。如果 python 解释器运行在进程中,那么 python 解释器也会被中断,资源被清理故而不会被占用。 Python 重定向信息输出也可通过 socket 发送出来。

程序运行效果:

uArIfi6.png!web

在子进程被中断前,先进入信号函数。之后由父进程清理子进程资源。

如果大家觉得还阔以,欢迎大家后台留言交流。

eIZJ3iQ.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK