免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 8928 | 回复: 2
打印 上一主题 下一主题

[C] glib库实现socket功能的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-03 09:34 |只看该作者 |倒序浏览
本帖最后由 lz_fine 于 2011-11-03 09:36 编辑

我用glib库实现了一个socket的接收程序,流程是这样的,先是初始化一个socket:sfd然后对sfd进行listen,然后得到sfd的iochannel,接着对它的输入进行监视,用g_io_add_watch,回调函数是callback,如果有客户端进行了连接,会调用callback,callback里会根据传递进来的iochannel得到sfd,然后进行accept,再对新得到的connfd的输入进行监视,回调函数是callback1,同时增加一个超时回调函数,回调函数是timeoutCallback,如果这个客户端有数据传入,那么会调用callback1,这个函数中会用g_io_channel_read_chars读取传入的数据,如果接受到eof,或者超时,那么会关闭连接,回收资源,对应的函数是freeTerminalData,这个函数内会用g_source_remove remove接受source和超时source,close连接的fd,g_io_channel_shutdown shutdown对应的iochannel,最后再对iochannel进行g_io_channel_unref

现在有几个疑问:
1.这样的流程对不对,是否有什么问题
2.我看到一篇文章上说不能在回调函数中shutdown iochannel,请问这样说对不对,为什么
原文网址:http://blog.chinaunix.net/space.php?uid=796091&do=blog&id=2035372
3.g_io_channel_read_chars这个如果每次读一个字节,需要读的次数太多了,如果读很多字节的话没有读到这么多字节前这个函数会阻塞不会返回,请问怎么做才能像recv那样一次把所有缓冲区的数据都读出来
4.还有glib库上有一个结构体GIOFuncs,包含有很多函数指针,如io_read,io_write,io_seek,io_close,io_create_watch等等,请问这个应该怎么用,在什么地方用,同样对应于timeout,有一个结构体GSourceFuncs包含有很多函数指针,如prepare,check,dispatch,finalize这个应该怎么用呢?
5.g_io_channel_ref和g_io_channel_unref应该怎么用
  1. #include <glib.h>

  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netdb.h>
  8. #include <unistd.h>

  9. #define SERVER_PORT        "30001"
  10. #define BUF_SIZE        1024
  11. #define TIME_OUT_SECONDS        (5)

  12. GMainLoop * loop;

  13. struct channelfd_s
  14. {
  15.         GIOChannel * channel;
  16.         gint fd;
  17.         gint timeout;
  18.         guint recvSourceId;
  19.         guint timeoutSourceId;
  20. };

  21. //struct channelfd_s globalchannelfd[10];

  22. /*
  23.         终端需要关闭时释放相关的资源
  24. */
  25. static void freeTerminalData(struct channelfd_s * newTerminal)
  26. {
  27.         printf("%s:%p\n", __func__, newTerminal);
  28.         g_source_remove(newTerminal->recvSourceId);
  29.         g_source_remove(newTerminal->timeoutSourceId);
  30.         close(newTerminal->fd);
  31.         g_io_channel_shutdown(newTerminal->channel, TRUE, NULL);
  32.         g_io_channel_unref(newTerminal->channel);
  33. }

  34. /*
  35.         用来收发数据的socket fd的超时回调函数
  36.         超时后关闭连接
  37. */
  38. gboolean timeoutCallback(gpointer data)
  39. {
  40.         struct channelfd_s * newTerminal;
  41.         
  42.         newTerminal = (struct channelfd_s *)data;
  43.         
  44.         newTerminal->timeout++;
  45.         printf("timeout:%d\n", newTerminal->timeout);
  46.         if(newTerminal->timeout > TIME_OUT_SECONDS){
  47.                 freeTerminalData(newTerminal);
  48.                 return FALSE;
  49.         }
  50.    
  51.         return TRUE;
  52. }

  53. /*
  54.         用来收发数据的socket fd的接收回调函数
  55. */
  56. gboolean callback1(GIOChannel * source, GIOCondition condition, gpointer data)
  57. {
  58.     int retInt;
  59.     char buf[BUF_SIZE];
  60.         gsize bytes_read;
  61.         GIOStatus gIoStatus;
  62.         gsize offset = 0;
  63.         gboolean breakFlag = FALSE;
  64.         struct channelfd_s * newTerminal;
  65.         
  66.         newTerminal = (struct channelfd_s *)data;
  67.    
  68.     while(1)
  69.     {
  70.                 gIoStatus = g_io_channel_read_chars(source, buf + offset, 1, &bytes_read, NULL);
  71.                 offset++;
  72.                 switch (gIoStatus)
  73.                 {
  74.                         case G_IO_STATUS_ERROR:
  75.                                 //出错,关闭连接
  76.                                 printf("recv error\n");
  77.                                 breakFlag = TRUE;
  78.                                 freeTerminalData(newTerminal);
  79.                                 break;
  80.                         case G_IO_STATUS_NORMAL:
  81.                                 //正常,存储到数据区
  82.                                 printf("recv normal\n");
  83.                                 if(buf[offset - 1] == '\0'){
  84.                                         printf("recv:%s\n", buf);
  85.                                         breakFlag = TRUE;
  86.                                 }
  87.                                 break;
  88.                         case G_IO_STATUS_EOF:
  89.                                 //结束,关闭连接
  90.                                 printf("recv eof\n");
  91.                                 freeTerminalData(newTerminal);
  92.                                 breakFlag = TRUE;
  93.                                 break;
  94.                         case G_IO_STATUS_AGAIN:
  95.                                 //重试,什么都不做
  96.                                 printf("recv again\n");
  97.                                 break;
  98.                         default:
  99.                                 break;
  100.                 }
  101.                 if(breakFlag == TRUE)
  102.                         break;
  103.         }
  104.         printf("exit func:%s\n", __func__);
  105.         return TRUE;
  106. }

  107. /*
  108.         sfd的回调函数
  109.         如果该函数被调用,说明有新的socket接入
  110. */
  111. gboolean callback(GIOChannel * source, GIOCondition condition, gpointer data)
  112. {
  113.         struct sockaddr addr;
  114.         socklen_t addrlen = sizeof(struct sockaddr);
  115.         struct channelfd_s * newTerminal;
  116.         
  117.         int listenfd;
  118.         int connfd;
  119.         
  120.         //得到用来listen的fd
  121.         listenfd = g_io_channel_unix_get_fd(source);
  122.         
  123.         //accept新的socket fd
  124.         connfd = accept(listenfd, &addr, &addrlen);
  125.         if(connfd <= 0){
  126.                 perror("accept");
  127.                 return FALSE;
  128.         }
  129.         printf("accept success:%d\n", connfd);
  130.         
  131.         //初始化一个channelfd结构
  132.         newTerminal = g_new(struct channelfd_s, 1);
  133.         //超时时间初始化为0
  134.         newTerminal->timeout = 0;
  135.         
  136.         newTerminal->fd = connfd;
  137.         newTerminal->channel = g_io_channel_unix_new(connfd);
  138.         //设置iochannel的编码为NULL
  139.         g_io_channel_set_encoding(newTerminal->channel, NULL, NULL);
  140.         
  141.         //增加对新的socket fd的监视
  142.         newTerminal->recvSourceId = g_io_add_watch(newTerminal->channel, G_IO_IN, callback1, newTerminal);

  143.         //增加对新的socket fd超时的监视
  144.         newTerminal->timeoutSourceId = g_timeout_add_seconds(1, timeoutCallback, newTerminal);
  145.         
  146.         return TRUE;
  147. }

  148. int main(void)
  149. {
  150.     /*
  151.         socket init
  152.     */
  153.     struct addrinfo hints;
  154.     struct addrinfo *res, *rp;
  155.     int retInt;
  156.     int reuse;
  157.         int sfd;
  158.         int cfd;

  159.     //get socket address information
  160.     memset(&hints, 0, sizeof(hints));
  161.     hints.ai_flags = AI_PASSIVE;
  162.     hints.ai_family = AF_UNSPEC;
  163.     hints.ai_socktype = SOCK_STREAM;
  164.     hints.ai_protocol = 0;
  165.     hints.ai_canonname = NULL;
  166.     hints.ai_addr = NULL;
  167.     hints.ai_next = NULL;

  168.     retInt = getaddrinfo(NULL, SERVER_PORT, &hints, &res);
  169.     if(retInt != 0)
  170.     {
  171.         printf("getaddrinfo:%s\n", gai_strerror(retInt));
  172.         return -1;
  173.     }

  174.     //get socket & bind
  175.     for (rp = res; rp != NULL; rp = rp->ai_next)
  176.     {
  177.             sfd = socket(rp->ai_family, rp->ai_socktype,
  178.                        rp->ai_protocol);
  179.         if (sfd < 0)
  180.                 continue;

  181.         //set socket fd reuse
  182.         reuse = 1;
  183.         retInt = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse,
  184.                                 sizeof(reuse));
  185.         if (retInt < 0)
  186.         {
  187.             perror("setsockopt");
  188.             return -2;
  189.         }

  190.         if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
  191.                 break;
  192.                
  193.         close(sfd);
  194.     }
  195.     printf("sfd:%d\n", sfd);

  196.     if (rp == NULL)
  197.     {
  198.             printf("could not bind\n");
  199.             return -3;
  200.     }

  201.     printf("bind success\n");
  202.     freeaddrinfo(res);

  203.     //listen
  204.     retInt = listen(sfd, 10);
  205.     if (retInt < 0)
  206.     {
  207.             perror("listen");
  208.             return -4;
  209.     }
  210.     printf("listen success\n");
  211.    
  212.         GIOChannel * channel;
  213.         
  214.         g_thread_init(NULL);
  215.         
  216.         //生产iochannel
  217.         channel = g_io_channel_unix_new(sfd);
  218.         //设置iochannel的编码为NULL
  219.         g_io_channel_set_encoding(channel, NULL, NULL);
  220.         //对sfd的iochannel监视
  221.         g_io_add_watch(channel, G_IO_IN, callback, NULL);

  222.         loop = g_main_loop_new(NULL, FALSE);
  223.         g_main_loop_run(loop);
  224.    
  225.         return 0;
  226. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2011-11-03 15:31 |只看该作者
没有人会吗?自己坐沙发

论坛徽章:
0
3 [报告]
发表于 2011-11-04 09:36 |只看该作者
人工置顶一下,希望有人给个解答啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP