QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 648|回复: 1

判断程序是否阻塞的小工具alive

[复制链接]
发表于 2004-8-16 20:29:41 | 显示全部楼层 |阅读模式
声明:作者同意将本文自由传播,但不得删改包括本声明及以下呼吁在内的任何内容。

呼吁:宪法第一条“中华人民共和国是工人阶级领导的、以工农联盟为基础的、
人民民主专政的社会主义国家。”所体现的阶级等级和专政思想与“人权天赋、
约法共和、自主选择、平等竞争”的政治文明常识相违背,呼吁人大将其改为
“中国是全体公民组成的,致力于保障公民基本权利,为公民的生存与发展提供
良好公共环境的法治国家。中国全体公民的基本权利一律平等。”

                       判断程序是否阻塞的小工具alive
                         丁建华 <[email protected]> 2004.8.16

    glib 的网络编程主要有两种模式: "单线程非阻塞" 和 "多线程阻塞". 这一点
在 gnet 项目上有非常明显的体现. 按说还有一种"多线程非阻塞"模式, 但是由于
glib 的 API 有许多是对缺省主循环上下文 (default main context) 进行操作的,
而这个上下文在目前的 2.4.2 版还没有被改成"每个线程一个"的形式, (这一改动
牵扯面太广, 而且似乎动力不足, 用户需求不强烈.) 所以没人采用"多线程非阻塞"
模式. 阻塞模式也受限制, 比如双向随机的数据传输, 用阻塞模式就需要两个线程.
因此目前用的最多的, 还是"单线程非阻塞"模式.

    可是初学者常常把握不住非阻塞, 一不小心就把程序阻塞了. 为此我专门做了
一个小程序 alive, 在指定端口上侦听. 用 telnet 连接到该端口, 就可以不断地
收到点点点. 如果你的程序不幸被阻塞了, 自然就没有点点点了.  )
   
    程序在 Cygwin 下编译通过. 有关 Cygwin/GTK+ 配置问题请到
一塌糊涂(ytht.net)个人文集区找 dingjh 个人文集.
   
    本人水平有限, 欢迎批评指正.

<file "Makefile">
all: alive.exe

SUFFIXES:
SUFFIXES: .exe .o .c

alive.exe : main.o alive.o revscon.o
    gcc -mno-cygwin -o $@ $^ `pkg-config --libs glib-2.0` -lwsock32

c.o:
    gcc -c -Wall -O2 -mno-cygwin -mms-bitfields -o $@ $< \
        `pkg-config --cflags glib-2.0`

clean:
    @rm -f *.exe *.o

PHONY: all
</file>

<file "main.c">
#include <glib.h>
#include "alive.h"
#include "revscon.h"
#include "os_stuff.h"

int main(int argc, char *argv[])
{
    GMainLoop * mloop;
    revs_con * con;
    alive_lsnr * lsnr;

#ifdef G_OS_WIN32
    {
    WSADATA wsa;
    gint err;
    if (0 != (err = WSAStartup(MAKEWORD(2, 2), &wsa)))
        g_error("Winsock 2.2 does not be supported (%d)", err);
    if (2 != LOBYTE(wsa.wVersion) || 2 != HIBYTE(wsa.wVersion))
        g_error("Can't found suitable winsock dll");
    }
#endif

    mloop = g_main_loop_new(NULL, FALSE);
    con = revs_con_new(mloop);
    lsnr = alive_lsnr_new("10.0.1.1", 6767); /* IP, PORT 任意*/

    g_main_loop_run(mloop);

    alive_lsnr_unref(lsnr);
    revs_con_unref(con);
    g_main_loop_unref(mloop);

#ifdef G_OS_WIN32
    WSACleanup();
#endif
    return 0;
}
</file>

<file "alive.h">
#ifndef _alive_h_
#define _alive_h_

#include <glib.h>

typedef struct _alive_lsnr alive_lsnr;

alive_lsnr * alive_lsnr_new(gchar *a_addr, gshort port);
void alive_lsnr_ref(alive_lsnr * lsnr);
void alive_lsnr_unref(alive_lsnr * lsnr);

#endif
</file>

<file "revscon.h">
#ifndef _revscon_h_
#define _revscon_h_

#include <glib.h>

typedef struct _revs_con revs_con;

revs_con * revs_con_new(GMainLoop * mloop);
void revs_con_ref(revs_con * con);
void revs_con_unref(revs_con * con);

#endif
</file>

<file "os_stuff.h">
#ifndef _os_stuff_h_
#define _os_stuff_h_

#ifdef G_OS_WIN32
#include <windows.h>
#include <winsock.h>
#endif

#ifdef G_OS_UNIX
/* #include <...> FIXME: 尚未完成!!! */
#define WSAGetLastError errorno
#define closesocket close
#endif

#endif
</file>

<file "alive.c">
#include <glib.h>
#include "alive.h"
#include "os_stuff.h"

struct _alive_lsnr {
    gint ref_count;
    GIOChannel * channel;
    GSList * connections;
};

typedef struct _alive_conn {
    gint ref_count;
    gint column;
    GIOChannel * channel;
    alive_lsnr * listener;
} alive_conn;

static gboolean alive_lsnr_func(GIOChannel *source, GIOCondition condition,
            gpointer data);

alive_conn * alive_conn_new(gint sock, alive_lsnr *lsnr)
{
    gint written;
    alive_conn * conn = g_new0(alive_conn, 1);
    conn->ref_count = 1;
    conn->column = 0;
    conn->channel = g_io_channel_unix_new(sock);
    g_io_channel_set_encoding(conn->channel, NULL, NULL);
    g_io_channel_set_buffered(conn->channel, FALSE);
    conn->listener = lsnr;
    lsnr->connections = g_slist_prepend(lsnr->connections, conn);
    g_io_channel_write_chars(conn->channel, "Hello, alive-client!\n\n",
                 -1, &written, NULL);
    return conn;
}

void alive_conn_ref(alive_conn * conn)
{
    g_return_if_fail(NULL != conn);
    g_return_if_fail(g_atomic_int_get(&conn->ref_count) > 0);
    g_atomic_int_inc(&conn->ref_count);
}

void alive_conn_unref(alive_conn * conn)
{
    gint sock, written;
    alive_lsnr * lsnr = conn->listener;

    g_return_if_fail(NULL != conn);
    g_return_if_fail(g_atomic_int_get(&conn->ref_count) > 0);
    if (!g_atomic_int_dec_and_test(&conn->ref_count))
        return;
    g_io_channel_write_chars(conn->channel, "\n\nGoodbye, alive-client!",
                 -1, &written, NULL);
    sock = g_io_channel_unix_get_fd(conn->channel);
    g_io_channel_shutdown(conn->channel, TRUE, NULL);
    g_io_channel_unref(conn->channel);
    g_io_channel_unref(conn->channel);
    closesocket(sock);
    lsnr->connections = g_slist_remove(lsnr->connections, conn);
    g_free(conn);
}

gboolean alive_timeout_func(gpointer data)
{
    gint written;
    alive_conn * conn = (alive_conn *)data;

    if (G_IO_STATUS_NORMAL == g_io_channel_write_chars(
            conn->channel, ".", -1, &written, NULL)) {
        if (++ conn->column >= 60) {
            g_io_channel_write_chars(conn->channel, "\n", -1,
                         &written, NULL);
            conn->column = 0;
        }
        return TRUE;
    }
    alive_conn_unref(conn);
    return FALSE;
}

gboolean alive_conn_func(GIOChannel *source, GIOCondition condition,
              gpointer data)
{
    if (condition & G_IO_IN) {
        gchar buff[256];
        gsize read, written;
        GError * error = NULL;

        g_io_channel_read_chars(source, buff, sizeof(buff),
                    &read, &error);
        if (NULL == error && read > 0) {
            g_io_channel_write_chars(source, buff, read,
                         &written, &error);
            if (NULL == error) {
                return TRUE; // read, write OK!
            }
        }
    }
    // something wrong, or closed by peer
    alive_conn_unref((alive_conn *) data);
    return FALSE;
}

alive_lsnr * alive_lsnr_new(gchar *a_addr, gshort port)
{
    alive_lsnr * lsnr = g_new0(alive_lsnr, 1);
    struct sockaddr_in addr;
    gint sock;

    if (INVALID_SOCKET == (sock = socket(PF_INET, SOCK_STREAM, 0)))
        g_error("socket: %d\n", WSAGetLastError());
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(a_addr);
    if (SOCKET_ERROR == bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
        g_error("bind: %d\n", WSAGetLastError());
    if (SOCKET_ERROR == listen(sock, 5))
        g_error("listen: %d\n", WSAGetLastError());
    lsnr->channel = g_io_channel_unix_new(sock);
    lsnr->connections = NULL;
    lsnr->ref_count = 1;
    g_io_add_watch(lsnr->channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
                   &alive_lsnr_func, lsnr);
    return lsnr;
}

void alive_lsnr_ref(alive_lsnr * lsnr)
{
    g_return_if_fail(NULL != lsnr);
    g_return_if_fail(g_atomic_int_get(&lsnr->ref_count) > 0);
    g_atomic_int_inc(&lsnr->ref_count);
}

void alive_lsnr_unref(alive_lsnr * lsnr)
{
    gint sock;

    g_return_if_fail(NULL != lsnr);
    g_return_if_fail(g_atomic_int_get(&lsnr->ref_count) > 0);
    if (!g_atomic_int_dec_and_test(&lsnr->ref_count))
        return;
    sock = g_io_channel_unix_get_fd(lsnr->channel);
    g_io_channel_unref(lsnr->channel);
    g_io_channel_unref(lsnr->channel);
    closesocket(sock);
    while (NULL != lsnr->connections) {
        alive_conn * conn;
        conn = (alive_conn *) lsnr->connections->data;
        alive_conn_unref(conn);
        alive_conn_unref(conn);
    }
    g_free(lsnr);
}

gboolean alive_lsnr_func(GIOChannel *source, GIOCondition condition,
            gpointer data)
{
    struct sockaddr addr;
    gint len = sizeof(addr);
    gint sock, s;
    alive_conn * conn;

    g_return_val_if_fail(condition & G_IO_IN, FALSE);

    sock = g_io_channel_unix_get_fd(source);
    if (INVALID_SOCKET == (s = accept(sock, &addr, &len))) {
        g_printerr("accept: %d\n", WSAGetLastError());
        return FALSE;
    }
    conn = alive_conn_new(s, (alive_lsnr *)data);
    g_timeout_add(500, &alive_timeout_func, conn);
    g_io_add_watch(conn->channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
               &alive_conn_func, conn);
    alive_conn_ref(conn); // _new and ref_ for _timeout and watch_
    return TRUE;
}
</file>

<file "revscon.c">
#include <stdio.h>
#include <glib.h>
#include "revscon.h"
#include "os_stuff.h"

struct _revs_con {
    gint ref_count;
    GIOChannel * in;
    GIOChannel * out;
    GMainLoop * mloop;
};

static gboolean revs_con_func(GIOChannel *source, GIOCondition condition,
              gpointer data);

revs_con * revs_con_new(GMainLoop * mloop)
{
    gint written;
    revs_con * con = g_new0(revs_con, 1);
    con->ref_count = 1;
    con->in = g_io_channel_unix_new(fileno(stdin));
    g_io_channel_set_encoding(con->in, NULL, NULL);
    if (!con->in->is_readable)  /* For Win32 !!! */
        con->in->is_readable = TRUE;
    con->out = g_io_channel_unix_new(fileno(stdout));
    g_io_channel_set_encoding(con->out, NULL, NULL);
    g_io_channel_write_chars(con->out, "Hello, I'm reverse-console!\n"
                             "Type 'quit' to quit.\n\n", -1,
                 &written, NULL);
    g_io_channel_flush(con->out, NULL);
    con->mloop = mloop;
    g_io_add_watch(con->in, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
               &revs_con_func, con);
    return con;
}

void revs_con_ref(revs_con * con)
{
    g_return_if_fail(NULL != con);
    g_return_if_fail(g_atomic_int_get(&con->ref_count) > 0);
    g_atomic_int_inc(&con->ref_count);
}

void revs_con_unref(revs_con * con)
{
    g_return_if_fail(NULL != con);
    g_return_if_fail(g_atomic_int_get(&con->ref_count) > 0);
    if (!g_atomic_int_dec_and_test(&con->ref_count))
        return;
    g_io_channel_unref(con->in);
    g_io_channel_unref(con->out);
    g_free(con);
}

gboolean revs_con_func(GIOChannel *source, GIOCondition condition,
              gpointer data)
{
    gchar * line, ch;
    gint len, crpos, written;
    revs_con * con = (revs_con *)data;

    g_return_val_if_fail(condition & G_IO_IN,
                 (g_main_loop_quit(con->mloop), FALSE));
    if (G_IO_STATUS_NORMAL == g_io_channel_read_line(
               source, &line, &len, &crpos, NULL)) {
        ch = line[crpos];
        line[crpos] = '\0';
        if (0 == g_ascii_strcasecmp(line, "quit")) {
            g_free(line);
            return (g_main_loop_quit(con->mloop), FALSE);
        }
        g_strreverse(line);
        line[crpos] = ch;
        g_io_channel_write_chars(con->out, line, -1, &written, NULL);
        g_io_channel_flush(con->out, NULL);
        g_free(line);
    }
    return TRUE;
}
</file>

<the-end/>
$Id: alive.bbs,v 1.1.1.1 2004/08/15 14:27:38 djh Exp $

--
            支持公民自主,反对为民作主,更反对代民作主。
            支持公民平等,反对城市特权,更反对党派特权。
            支持守法治国,反对以法治国,更反对以德治国。
            支持法权独立,反对行政干预,更反对军权干预。
呼吁人大废除宪法第一条部分人高于其他人的歧视条款,尽快建立独立于行政的
司法体系,实现人人平等、依法共和的现代公民社会。    请一起采用本签名档。
※ 来源:·饮水思源 bbs.sjtu.edu.cn·[FROM: 61.52.247.254]
发表于 2004-8-18 15:31:01 | 显示全部楼层
文章作者真是无知之极, 大家都是工农无产阶级. 如果自认不是就请举起双脚滚到美国去.
这样的东西还是请版主删掉为好
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-7 15:47 , Processed in 0.038143 second(s), 16 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表