一个略粗暴的人脸认证标注工具的兑现

前言  – 一个简短开场白 

小喵的唠叨话:话说最近小喵也只要从头勾画论文了,想了点儿圆满或无眉目,不懂得该写几什么。恰好又受分配了一些标注数据的办事,于是乎想写点代码,休闲一下。结果吧不怕是即时首博客。对了,小喵对GUI编程一窍不通,只略知一二Windows有MFC,Mac上之不亮堂。。。恰好听说过QT,而且知道者界面库是跨平台的,也即选用了此家伙了。

  winds 的 select 和 linux 的 select 是少数只完全不同的东西.
然而凡人欢喜拿它们揉在一起.

 

非阻塞的connect业务是独自带超时机制的 connect.
实现机制无外乎利用select(也出 epoll的).

正文系原创,转载请注明出处~

本文是只源码软文, 专注解决客户端的跨平台的connect问题. 服务器的connect
要比客户端多考虑一丁点.

小喵的博客:http://www.miaoerduo.com

发出会再次扯. 对于 select 网上资料太多, 几乎都起接触不痛不痒. 了解本质推荐
man and msdn !!!

博客原文:http://www.miaoerduo.com/qt/一个简便粗暴的人头脸认证标注工具的落实.html

 

 

刚巧文 – 所有的且要前戏

那么现在起来与小喵一起瞎猫似的捯饬QT吧~

那么开始吧 .  一切由丑陋之跨平台宏开始

先期押同样双眼效果图:

#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <signal.h>

//
// IGNORE_SIGPIPE - 管道破裂,忽略SIGPIPE信号
//
#define IGNORE_SIGNAL(sig)    signal(sig, SIG_IGN)

#ifdef __GNUC__

#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/select.h>
#include <sys/resource.h>

/*
* This is used instead of -1, since the
* SOCKET type is unsigned.
*/
#define INVALID_SOCKET      (~0)
#define SOCKET_ERROR        (-1)

#define IGNORE_SIGPIPE()    IGNORE_SIGNAL(SIGPIPE)

// connect链接还在进行中, linux显示 EINPROGRESS,winds是 WSAEWOULDBLOCK
#define ECONNECTED          EINPROGRESS

typedef int socket_t;

#elif _MSC_VER

#undef    FD_SETSIZE
#define FD_SETSIZE          (1024)
#include <ws2tcpip.h>

#undef    errno
#define   errno              WSAGetLastError()

#define IGNORE_SIGPIPE()

// connect链接还在进行中, linux显示 EINPROGRESS,winds是 WSAEWOULDBLOCK
#define ECONNECTED           WSAEWOULDBLOCK

typedef int socklen_t;
typedef SOCKET socket_t;

static inline void _socket_start(void) {
    WSACleanup();
}

#endif

// 目前通用的tcp udp v4地址
typedef struct sockaddr_in sockaddr_t;

//
// socket_start    - 单例启动socket库的初始化方法
// socket_addr    - 通过ip, port 得到 ipv4 地址信息
// 
inline void socket_start(void) {
#ifdef _MSC_VER
#    pragma comment(lib, "ws2_32.lib")
    WSADATA wsad;
    WSAStartup(WINSOCK_VERSION, &wsad);
    atexit(_socket_start);
#endif
    IGNORE_SIGPIPE();
}

这时候又装进一些,  简化操作. 

大凡休是新一押还生炫酷。功能上为还吓,至少简单的号工作还能够完成了。那么让咱来同样步一步之成功这个家伙吧。

inline socket_t socket_stream(void) {
    return socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
}

inline int socket_close(socket_t s) {
#ifdef _MSC_VER
    return closesocket(s);
#else
    return close(s);
#endif
}

inline int socket_set_block(socket_t s) {
#ifdef _MSC_VER
    u_long mode = 0;
    return ioctlsocket(s, FIONBIO, &mode);
#else
    int mode = fcntl(s, F_GETFL, 0);
    if (mode == SOCKET_ERROR)
        return SOCKET_ERROR;
    if (mode & O_NONBLOCK)
        return fcntl(s, F_SETFL, mode & ~O_NONBLOCK);
    return 0;
#endif    
}

inline int socket_set_nonblock(socket_t s) {
#ifdef _MSC_VER
    u_long mode = 1;
    return ioctlsocket(s, FIONBIO, &mode);
#else
    int mode = fcntl(s, F_GETFL, 0);
    if (mode == SOCKET_ERROR)
        return SOCKET_ERROR;
    if (mode & O_NONBLOCK)
        return 0;
    return fcntl(s, F_SETFL, mode | O_NONBLOCK);
#endif    
}

inline int socket_connect(socket_t s, const sockaddr_t * addr) {
    return connect(s, (const struct sockaddr *)addr, sizeof(*addr));
}

平等、功能需求

以此序要的功效是水到渠成一个口脸认证的标工具。

具体来说,就是加很多针对性颜的图纸,要标注一下即无异于对准是免是同一个人数。同时,每有之图片的人数脸一摆放是生活照,一布置凡证件照,需要以标注出啦张凡证件照,那张是生活照。照片还是由此检测及指向共同之,这个家伙就需要就简单的亮、标注、保存记录的做事就可。

当然考虑到有时候要标注的list可能坏十分,可以参加跳转的功用。标注结果还保存在内存,用户可以天天变动,点击保存,则刻画副硬盘。

 

老二、数据结构

那么是未是现行便得入手写代码了为?当然不是!

小喵写这个软件一共用了3天之日,第一上就了一个超简单demo程序,熟悉了一下QT的事件添加,路径选择与显示图片的几乎独职能。之后还要密切的思维了瞬间各种数据的构造,才动手做了马上等同版工具。没有一个清楚的多少的概念,会造成过多的无用功。所以,大家以描写程序的时,要于预备阶段多花一点时来揣摩,毕竟写代码才是极简便的事体未是啊?

  1. 输入数据格式:因为小喵的工作环境下,大家都指向linux有局部询问,所以可以自行生成好图片的路径的list,这里统一要求,list必须是偶数履行(2n行),代表n对,相邻之图纸为同一对。
  2. 号数据存储:考虑到我们不仅要标注是未是部分,还得标注哪张是证件照,所以不妨直接当朗诵数据的早晚即便分为两客,这样就用鲜独std::vector<std::string>来存储就实施了。
  3. 号过程的状态:我们得懂得标注过程遭到之那些信息呢?主要应该有:总数据量,当前曾标明的对数。
  4. 标明结果:每一样针对还起同一组对承诺地
    结果,考虑到有4遭遇状态:未标注,不确定,不般配,匹配这四种植,我们定义一个枚举的状态表enum
    AnnoState就好。之后用一个std::vector<enum
    AnnoState>来囤标注结果。

全局的测试中心main 函数部分之类

老三、界面制作

GUI程序的界面直是独雅为人头疼的问题,记得在本科学习Java的时节,需要好亲手写一个控件,使用new
JButton()类似之点子创造按钮,然后上加至主界面上,位置什么的还得调用这个目标来装,十分底繁琐。那么QT能不能够简化这进程为?答案是必然之。

创项目->选择Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其他任何默认设置,就创造好了一个类型了。这个初步的种类里面有3只文件夹:头文件,源文件及界面文件,以及一个.pro结尾的门类布局文件。

既用编制界面,我们本来会怀念查转界面文件了,双击MainWindow.ui(我这里全部都是默认的名)。出现的凡一个充斥各种控件的可视化界面编辑器。

据我们事先的界面样式,拖动左边的控件,就得好界面的编了。小喵这里才所以到了几乎种控件:

QPushButton:各种按钮

QLabel:所以展示文字与图像的区域还是即刻这控件

QFrame:一个器皿,小喵用它们只是是为组织及再清晰

QSlider:滑动条,小喵用的凡水平滑动条

QStatusBar:状态栏,
这应是自带的,如果删掉的话,在MainWindow控件点击右键就可以创造了

拖动完成后,双击空间,就可于空间设置文本,同时注意让每个控件起一个好听的名字(起名字怪关键的!《代码大全》中还是用相同章,好几十页的篇幅介绍如何命名)。

关于其他的控件,大家好自行钻研。反正小喵现在的道行应该才是筑基。

那我们就是欣喜的成功了界面的编制了~点击左右产之周转图标(三角形的百般),就可以看到自己之运行程序了!

extern int socket_addr(const char * ip, uint16_t port, sockaddr_t * addr);
extern int socket_connecto(socket_t s, const sockaddr_t * addr, int ms);
extern socket_t socket_connectos(const char * host, uint16_t port, int ms);


//
// gcc -g -O2 -Wall -o main.exe main.c
//
int main(int argc, char * argv[]) {
    socket_start();

    socket_t s = socket_connectos("127.0.0.1", 80, 10000);
    if (s == INVALID_SOCKET) {
        fprintf(stderr, "socket_connectos is error!!\n");
        exit(EXIT_FAILURE);
    }
    puts("socket_connectos is success!");

    return EXIT_SUCCESS;
}

int 
socket_addr(const char * ip, uint16_t port, sockaddr_t * addr) {
    if (!ip || !*ip || !addr) {
        fprintf(stderr, "check empty ip = %s, port = %hu, addr = %p.\n", ip, port, addr);
        return -1;
    }

    addr->sin_family = AF_INET;
    addr->sin_port = htons(port);
    addr->sin_addr.s_addr = inet_addr(ip);
    if (addr->sin_addr.s_addr == INADDR_NONE) {
        struct hostent * host = gethostbyname(ip);
        if (!host || !host->h_addr) {
            fprintf(stderr, "check ip is error = %s.\n", ip);
            return -1;
        }
        // 尝试一种, 默认ipv4
        memcpy(&addr->sin_addr, host->h_addr, host->h_length);
    }
    memset(addr->sin_zero, 0, sizeof addr->sin_zero);

    return 0;
}

季、数据定义及初始化

咱俩先曾分析了咱们用的多寡了,这有开始下代码的定义这些组织。

打开我们/*唯一的*/头文件mianwindow.h,添加需要的变量,小喵就径直把自己的峰文件复制下来了:

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 #include <QMainWindow>
 5 #include <vector>
 6 #include <string>
 7 namespace Ui {
 8 
 9 class MainWindow;
10 }
11 
12 class MainWindow : public QMainWindow
13 {
14     enum AnnoState {
15         UNKNOWN = 0,  // 未标注
16         YES = 1,      // 匹配
17         NO = 2,       // 不匹配
18         UNSURE = 3    // 不确定
19     };
20 
21 public:
22     explicit MainWindow(QWidget *parent = 0);
23     ~MainWindow();
24 
25 private:
26     Ui::MainWindow *ui; // 自带的,ui界面的接口
27     std::vector<std::string> image_list_1;  // 用来存放左边的图片的list
28     std::vector<std::string> image_list_2;  // 用来存放右边的图片的list
29     int current_idx;                        // 当前图片对的id
30     int total_pair_num;                     // 总共的图片对的数目
31     std::vector< AnnoState > annotation_list;  // 标注的结果
32 };
33 
34 #endif // MAINWINDOW_H

可看看,小喵添加了一个enum的类,用来代表标注结果的路。虽然只有来4个状态,我们还可以直接约定几只int值来代表,但相信自己,为这么4个状态定义一个枚举类型是了有必不可少之。

后咱们有的成员变量都是private的。具体意思,注释中为起写明。

下同样步就是是初始化了。初始化的长河当然得写在构造函数里,这里,小喵于初始化的时节逼用户挑选一个标的list,如果未这样做,会生出成千上万底意外状况。请见谅小喵的怠惰。。。

 1 MainWindow::MainWindow(QWidget *parent) :
 2     QMainWindow(parent),
 3     ui(new Ui::MainWindow)
 4 {
 5     ui->setupUi(this);
 6 
 7     // 选择输入文件
 8     while (1) {
 9         QString file_name = QFileDialog::getOpenFileName(this, "choose a file to annotate", ".");
10         if (file_name.isEmpty()) {
11             int ok = QMessageBox::information(this, "choose a file to annotate", "Don't want to work now?", QMessageBox::Ok | QMessageBox::Cancel);
12             if (ok == QMessageBox::Ok) {
13                 exit(0);
14             }
15             continue;
16         }
17         std::ifstream is(file_name.toStdString());
18         std::string image_name;
19         bool is_odd = true;
20         while (is >> image_name) {
21             if (is_odd) {
22                 this->image_list_1.push_back(image_name);
23             } else {
24                 this->image_list_2.push_back(image_name);
25             }
26             is_odd = !is_odd;
27         }
28         is.close();
29 
30         if (image_list_1.size() != image_list_2.size()) {
31             QMessageBox::information(this, "choose a file to annotate", "this image list is not even", QMessageBox::Ok);
32             continue;
33         }
34         if (0 == image_list_1.size()) {
35             QMessageBox::information(this, "choose a file to annotate", "this image list is empty", QMessageBox::Ok);
36             continue;
37         }
38         break;
39     }
40 
41     assert(image_list_1.size() == image_list_2.size());
42     // 初始化其他参数
43     this->total_pair_num = image_list_1.size();
44     this->current_idx = 0;
45     std::vector<AnnoState> annotation_list(this->total_pair_num, AnnoState::UNKNOWN);
46     this->annotation_list.swap(annotation_list);
47 
48     display();
49 }

这边用了简单单QT的零件:

QFileDialog:这个组件是一个文本对话框,其中起有限单很卓有成效之函数:getOpenFileName用于选择一个文件,并回到文件称;getSaveFileName用于选择一个文本来保存数据,并回一个文书称。这简单个函数的参数很多,小喵只所以到了前头的3独,用到之参数依次是:父组件,标题,初始目录。其他的参数的效力,喵粉可以错过公共网查一下。

QMessageBox::information,这个函数的效应是亮一个音讯窗口。四单参数分别代表:父组件,标题,内容,按钮样式。

深信大家明白一点点C++的知的话,很易看明白就段代码。

此虽是采用了一个循环,让用户选择文件,如果选择成功了,则读取数据到我们的list中,最终初始化了任何的参数,在调用display函数来显示。这个display函数是咱团结一心编辑的,后面会说到。另外,assert函数是预言,他管了断言的数码的合法性,如果不合法,程序会离。想利用此函数,需要包含头文件assert.h。

 

五,添加事变响应

小喵之前了解及,QT使用的是同种植信号和槽的波机制,是同一栽死尖端的体制。那么闹没有出啊简单的方式,为我们的每个控件绑定好的之事件呢?

当界面编辑界面下,右击得丰富事件之半空中,然后择转到槽。这时候会生多选项,这里一直选择clicked就可。然后您晤面发现我们的mainwindow类中,多矣一个pivate
slot的函数(也便是槽函数)。

咱得以让各一个亟需添加事件的函数都因此这种艺术来绑定事件,最终头文件中会出现这样的扬言(函数名称的条条框框是:on_控件名_信号类型):

1 private slots:
2     void on_pushButton_save_clicked();
3     void on_pushButton_ok_clicked();
4     void on_pushButton_no_clicked();
5     void on_pushButton_unsure_clicked();
6     void on_pushButton_next_clicked();
7     void on_pushButton_prev_clicked();
8     void on_pushButton_switch_clicked();
9     void on_horizontalSlider_progress_sliderReleased();

于来源文件中,也会见生成空的函数定义。我们只有待协调完成函数定义就是充分功告成!

下面为闹底是除save的装有的函数的概念。

要办事是,给每个事件编写修改数据的代码,而休去当任何界面相关的片。各个控件可以经this->ui来安及落。使用Qt
Creator的时段,要充分利用智能提醒。

 1 /**
 2  * @brief MainWindow::on_pushButton_ok_clicked
 3  * 标注为"匹配"
 4  */
 5 void MainWindow::on_pushButton_ok_clicked()
 6 {
 7     this->annotation_list[this->current_idx] = MainWindow::AnnoState::YES;
 8     ++ this->current_idx;
 9     display();
10 }
11 
12 /**
13  * @brief MainWindow::on_pushButton_no_clicked
14  * 标注为"不匹配"
15  */
16 void MainWindow::on_pushButton_no_clicked()
17 {
18     this->annotation_list[this->current_idx] = MainWindow::AnnoState::NO;
19     ++ this->current_idx;
20     display();
21 }
22 
23 /**
24  * @brief MainWindow::on_pushButton_unsure_clicked
25  * 标注为"不确定"
26  */
27 void MainWindow::on_pushButton_unsure_clicked()
28 {
29     this->annotation_list[this->current_idx] = MainWindow::AnnoState::UNSURE;
30     ++ this->current_idx;
31     display();
32 }
33 
34 /**
35  * @brief MainWindow::on_pushButton_next_clicked
36  * 移动到下一组
37  */
38 void MainWindow::on_pushButton_next_clicked()
39 {
40     ++ this->current_idx;
41     display();
42 }
43 
44 /**
45  * @brief MainWindow::on_pushButton_prev_clicked
46  * 移动到上一组
47  */
48 void MainWindow::on_pushButton_prev_clicked()
49 {
50     -- this->current_idx;
51     display();
52 }
53 
54 /**
55  * @brief MainWindow::on_pushButton_switch_clicked
56  * 交换两边的图片
57  */
58 void MainWindow::on_pushButton_switch_clicked()
59 {
60     std::string tmp = this->image_list_1[this->current_idx];
61     this->image_list_1[this->current_idx] = this->image_list_2[this->current_idx];
62     this->image_list_2[this->current_idx] = tmp;
63     display();
64 }
65 
66 /**
67  * @brief MainWindow::on_horizontalSlider_progress_sliderReleased
68  * 拖放进度条,控制进度
69  */
70 void MainWindow::on_horizontalSlider_progress_sliderReleased()
71 {
72     int pos = this->ui->horizontalSlider_progress->value();
73     this->current_idx = pos;
74     this->display();
75 }

时至今日,我们的大致的法力逻辑就是编写了了。

这就是说怎么让界面上展示我们的网状态呢?注意到了咱地方的各国一个函数都调用了display这个函数了呢?这个函数正式承担绘制界面的效果。

一些至关重要介绍三个函数:

 1 const std::string UNSURE_FILE = ":Fileunsure.png";
 2 const std::string YES_FILE = ":Fileyes.gif";
 3 const std::string NO_FILE = ":Fileno.gif";
 4 const std::string UNKNOWN_FILE = ":Fileunknown.png";
 5 
 6 /**
 7  * @brief set_image 将图像设置到label上,图像自动根据label的大小来缩放
 8  * @param label
 9  * @param image
10  */
11 void set_image(QLabel *label, const QPixmap &image) {
12     float ratio(0.);
13     ratio = 1. * label->width() / image.width();
14     ratio = fmin( 1. * label->height() / image.height(), ratio );
15     QPixmap m = image.scaled(static_cast<int>(image.width() * ratio), static_cast<int>(image.height() * ratio));
16     label->setPixmap(m);
17 }
18 
19 void set_image(QLabel *label, const std::string image_path) {
20     QPixmap image(image_path.c_str());
21     set_image(label, image);
22 }
23 
24 /**
25  * @brief MainWindow::display \n
26  * 根据系统中的所有的变量来设置当前界面中的各个部分的内容
27  */
28 void MainWindow::display() {
29 
30     if (this->current_idx >= this->total_pair_num) {
31         QMessageBox::information(this, "annotation over", "Congratulations! You've finished all the job! Please save your work :)", QMessageBox::Ok);
32         this->current_idx = this->total_pair_num - 1;
33     }
34     if (this->current_idx < 0) {
35         QMessageBox::information(this, "annotation warning", "You must start at 0 (not a negative position, I konw you wanna challenge this app) :)", QMessageBox::Ok);
36         this->current_idx = 0;
37     }
38 
39     // 进度条
40     this->ui->horizontalSlider_progress->setRange(0, this->total_pair_num - 1);
41     this->ui->horizontalSlider_progress->setValue(this->current_idx);
42 
43     // 状态栏
44     this->ui->statusBar->showMessage(QString((std::to_string(this->current_idx + 1) + " / " + std::to_string(this->total_pair_num)).c_str()));
45 
46     // 文件名
47     std::string image_name_1 = this->image_list_1[this->current_idx];
48     std::string image_base_name_1 = image_name_1.substr(image_name_1.find_last_of("/") + 1);
49     std::string image_name_2 = this->image_list_2[this->current_idx];
50     std::string image_base_name_2 = image_name_2.substr(image_name_2.find_last_of("/") + 1);
51     this->ui->label_image_name_1->setText(image_base_name_1.c_str());
52     this->ui->label_image_name_2->setText(image_base_name_2.c_str());
53 
54     // 显示图像
55     set_image(this->ui->label_image_view_1, image_name_1);
56     set_image(this->ui->label_image_view_2, image_name_2);
57 
58     // 显示标注结果
59     std::string show_image_name = UNKNOWN_FILE;
60     switch (this->annotation_list[this->current_idx]) {
61     case AnnoState::UNKNOWN:
62         show_image_name = UNKNOWN_FILE;
63         break;
64     case AnnoState::YES:
65         show_image_name = YES_FILE;
66         break;
67     case AnnoState::NO:
68         show_image_name = NO_FILE;
69         break;
70     case AnnoState::UNSURE:
71         show_image_name = UNSURE_FILE;
72         break;
73     }
74     set_image(this->ui->label_image_compare_status, show_image_name);
75 
76 }

太开始我们定义了4个图片的门路。这可以是绝对路径或者相对路径。我们这边的路子设置的较奇怪,在脚我们见面讲话到。

set_image负责将加的图样绘制到QLabel上,为了展示的尴尬,图像会按照QLabel的尺码来动态的缩放。这样虽不见面出现有个图像太特别还是极端小的动静了。

display则是负责各个区域之绘图。

还不同一步是保留结果:

 1 /*
 2  * @brief MainWindow::on_pushButton_save_clicked \n
 3  * 保存结果文件
 4  */
 5 void MainWindow::on_pushButton_save_clicked()
 6 {
 7     QString file_name = QFileDialog::getSaveFileName(this, "choose a file to save", ".");
 8     if (file_name.isEmpty()) {
 9         QMessageBox::information(this, "choose a file to save", "please enter a legal file name", QMessageBox::Ok);
10         return;
11     }
12     std::ofstream os(file_name.toStdString());
13     for (int idx = 0; idx < static_cast<int>(this->annotation_list.size()); ++ idx) {
14         os << this->image_list_1[idx] << " " << this->image_list_2[idx] << " " << this->annotation_list[idx] << "\n";
15     }
16     os.close();
17     QMessageBox::information(this, "save", "save result success", QMessageBox::Ok);
18 }

此处才是您要是之合, 确的跨平台的客户端非阻塞 connect.

六、添加资源

鉴于我们的次第是索要publish出去的,因此图片文件等资源,必须含有在先后中。那么Qt怎么长文件资源为?

每当类型视图下,右键项目->添加新文件->Qt->Qt Resource
File。就得创造一个qrc文件了。

本身此给此文件取名为image。

此后,建议以列之绝望目录里新建一个文书夹,用来存放在资源。小喵的组织是这个法的:

小喵的种根本目录新建了一个文书夹images,并将图像资料放入了此文件夹。

后回到QT,我们恰好砌好之image.qrc文件->Open in Editor。

预先补充加前缀,这里描绘上/File。之后点击新建的/File目录,再点击添加->添加文件,选择我们的素材文件。最终的效应图如下:

然后,我们虽得以次中直接访问这些资源了。这吗尽管是我们事先的那四单意外的门道的由来了。

int
socket_connecto(socket_t s, const sockaddr_t * addr, int ms) {
    int n, r;
    struct timeval to;
    fd_set rset, wset, eset;

    // 还是阻塞的connect
    if (ms < 0) return socket_connect(s, addr);

    // 非阻塞登录, 先设置非阻塞模式
    r = socket_set_nonblock(s);
    if (r < 0) {
        fprintf(stderr, "socket_set_nonblock error!\n");
        return r;
    }

    // 尝试连接一下, 非阻塞connect 返回 -1 并且 errno == EINPROGRESS 表示正在建立链接
    r = socket_connect(s, addr);
    if (r >= 0) goto __return;

    // 链接不再进行中直接返回, linux是 EINPROGRESS,winds是 WASEWOULDBLOCK
    if (errno != ECONNECTED) {
        fprintf(stderr, "socket_connect error r = %d!\n", r);
        goto __return;
    }

    // 超时 timeout, 直接返回结果 ErrBase = -1 错误
    r = -1;
    if (ms == 0) goto __return;

    FD_ZERO(&rset); FD_SET(s, &rset);
    FD_ZERO(&wset); FD_SET(s, &wset);
    FD_ZERO(&eset); FD_SET(s, &eset);
    to.tv_sec = ms / 1000;
    to.tv_usec = (ms % 1000) * 1000;
    n = select((int)s + 1, &rset, &wset, &eset, &to);
    // 超时直接滚 or linux '异常'直接返回 0
    if (n <= 0) goto __return;

    // 当连接成功时候,描述符会变成可写
    if (n == 1 && FD_ISSET(s, &wset)) {
        r = 0;
        goto __return;
    }

    // 当连接建立遇到错误时候, winds 抛出异常, linux 描述符变为即可读又可写
    if (FD_ISSET(s, &eset) || n == 2) {
        socklen_t len = sizeof n;
        // 只要最后没有 error那就 链接成功
        if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&n, &len) && !n)
            r = 0;
    }

__return:
    socket_set_block(s);
    return r;
}

socket_t
socket_connectos(const char * host, uint16_t port, int ms) {
    int r;
    sockaddr_t addr;
    socket_t s = socket_stream();
    if (s == INVALID_SOCKET) {
        fprintf(stderr, "socket_stream is error!\n");
        return INVALID_SOCKET;
    }

    // 构建ip地址
    r = socket_addr(host, port, &addr);
    if (r < 0)
        return r;

    r = socket_connecto(s, &addr, ms);
    if (r < 0) {
        socket_close(s);
        fprintf(stderr, "socket_connecto host port ms = %s, %u, %d!\n", host, port, ms);
        return INVALID_SOCKET;
    }

    return s;
}

七、发布

现阶段,相信各一个喵粉的次第都能于自己的微处理器上快的游乐了。这么有意思的先后,怎么分享给其他人也?

同Windows上常用之VS类似,Qt Creator的左下角有个公布选项:

分选Release,然后构建整个项目即好了。之后找到我们的次第,双击就足以运作。

此刻若会欢欣鼓舞的管这个次关你的好伙伴,得到的反响得是:这是吗!我由不上马!

缘何也?

虽说Qt是一个跨平台的界面库,但假如对方的处理器上从不设置Qt,那么就非能够运行。不过不用失落,Qt中已于来一个美好的解决办法。

小喵的电脑是Mac的,所以找到的解决方案吧是Mac的,Windows和Linux上吧产生近似之法子,大家好活动检索。

http://www.cnblogs.com/E7868A/archive/2012/12/02/2798225.html

参考上述博客,我们采取macdeployqt这个家伙来处理一下release的先后即使搞定。这时候若会意识原100k的次序成为了22M。但是一直发给别人的时,是得一直运行的!

 

时至今日,本次的博客结束了。

总体的花色于github上得下载:

https://github.com/miaoerduo/Anno_pro

 

使您觉得本文对而有帮扶,那请小喵喝杯茶吧O(∩_∩)O

转载请注明出处~

各一样软突破还来之不易. 如果需要以工程中落实同客 nonblocking select
connect. 可以直接用者思路.

着力就是不同平台的select api 的用罢了. 你懂得了说不定就少次点坑,
多无可奈何些~

 

后记 – 感悟

  代码还是丢失接触注释好, 那些老人说之代码即注释好像有点道理