引言

  这篇博文可能有点水,主要将自己libuv的学习过程和理解. 简单谈方法. 有点杂. 那我们开始吧.

首先介绍 github . 这个工具特别好用. 代码托管. 如果不翻墙可能有点卡. 但是应该试试. 这里扯一点, github

对代码水平提高 太重要了.还有一个解决疑难问题的论坛 stackoverflow  http://stackoverflow.com/.

真的屌的不行.

  附赠

  github 简易教程, 不用谢   http://www.nowcoder.com/courses/2

  国内还有一个 逼格特别高的论坛, 哪天你nb了, 也可以上去装逼, 以其中一个帖子为例

  知乎epoll讨论   http://www.zhihu.com/question/21516827

到这里关于 引言就结束了.

前言

  现在我们开始说libuv, 这是个网络跨平台的库,是C库.比其它同类的网络库, 多了个高效编程.不需要考虑太多细节.

是node.js的底层. 自己学习了一两周,发现, 功能挺强大的.通用性好. 但总觉得有点恶心.后面有时间说. 总的而言很优秀,很好,

但不喜欢.

  下面我来分享怎么学习libuv 首先 你要去 官网下载libuv 代码.

libuv github 源码   https://github.com/libuv/libuv  这时候你需要在你的linux上编译安装.

参照步骤就是 readme.md

这时候你肯定会出故障. 怎么做呢. 去 stackoverflow 上 找答案. google搜一下,都能解决. 我当时遇到一个问题是网关超时. 修改网关就可以了. 自己尝试,提高最快.

安装折腾你半天. 那我们 测试一下. 按照 libuv 中文版最后一个demo 为例

#include <stdio.h>
#include <string.h>
#include <uv.h> uv_tty_t g_tty;
uv_timer_t g_tick;
int g_width, g_height, g_pos; static void __update(uv_timer_t* req)
{
uv_write_t wreq;
char data[];
const char* msg = " Hello TTY ";
uv_buf_t buf;
buf.base = data;
buf.len = sprintf(data, "\033[2J\033[H\033[%dB\033[%luC\033[42;37m%s",
g_pos, (g_width - strlen(msg))/, msg);
uv_write(&wreq, (uv_stream_t*)&g_tty, &buf, , NULL); if(++g_pos > g_height){
uv_tty_reset_mode();
uv_timer_stop(&g_tick);
}
} // 主函数检测
int main(void)
{
uv_loop_t* loop = uv_default_loop(); uv_tty_init(loop, &g_tty, , );
uv_tty_set_mode(&g_tty, ); if(uv_tty_get_winsize(&g_tty, &g_width, &g_height)){
puts("Could not get TTY information");
uv_tty_reset_mode();
return ;
} printf("Width %d, height %d\n", g_width, g_height);
uv_timer_init(loop, &g_tick);
uv_timer_start(&g_tick, __update, , ); return uv_run(loop, UV_RUN_DEFAULT);
}

测试的时候,运行会看见动画. 控制台动画

gcc -g -Wall -o uvtty.c uvtty.c -luv

运行截图是

运行看出来Hello TTY 会一直向下移动知道移动到底了.

好到这里,表示libuv 基本环境是好了,是可以开发了. 来上大头戏.国人有几个人翻译了一本 libuv 开发的书籍 ,

地址

  libuv中文编程 拿走不谢    http://www.nowx.org/uvbook/

这里再扯一点, 对于别人的劳动成果, 还是表示感谢.没有他们我们只能是干等着 闭门造车. 外国技术至少领先国内5年.

你看上面书的时候需要对照下面代码看

  libuv中文编程 演示代码   https://github.com/nikhilm/uvbook/tree/master/code

你至少需要看完那本书, 有问题翻libuv 源码, 对于书中的 demo code都需要敲一遍. 后面至少遇到libuv不在陌生.

上面能练习code都敲了一遍,临摹并且优化修改了.

到这里关于libuv 的学习思路基本就确定了. 就是 写代码.

好了简单提一下对libuv的理解.

  1. libuv 最好的学习方法 看懂源码. ........

    (源码能看懂的似懂非懂,目前还是写不出来.)

  2.libuv 网络开发确实简单, 网络层 100-200行代码就可以了, 但是它提供了 例如线程池, 定时器揉在一起源码看起来就难一点了, 跨平台的终端控制.

  3.libuv 开发全局变量 和 隐含的包头技术 太泛滥不好.....

总而言之C开发中没有一劳永逸的轮子. 否则就成为标准库了. 都有优缺点. 看自己应用领域. 喜欢看网络库的 强烈推荐libuv 比libevent和libuv要

封装的好写. 好久没用也都忘记了. .......

  这里也快结束了. 最好的 还是 思想和 设计......

正文

  到这里我想了一下,网络库看了有一些了, 但是还是封装不出来. 感觉基础还是不好. 说的太玄乎还是从基础开始吧. 这里就相当了epoll. 还是epoll做起吧.

对于socket 基础开发, 请参照的我的 博文资料 http://www.cnblogs.com/life2refuel/p/5240175.html

简单讲解socket开发 最后还举了个epoll的案例.

  对于epoll 其实就 4个函数 man epoll_create 在linux系统上查看就可以了. 对于它怎么入门. 搜索10篇比较不错的epoll博文,看完写完.基本上

就会开发了.其它的就慢慢提升了. 这里我们 不细说epoll 是什么. 就举个简单例子帮助我和大家入门. epoll 本质就是操作系统轮询检测通知上层可以用了.

第一个例子监测 stdin输入

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h> #define _INT_BUF (255) // epoll helloworld 练习
int main(void)
{
int epfd, nfds, i, len;
char buf[_INT_BUF];
struct epoll_event ev; epfd = epoll_create(); //监听一个描述符与. stdin
ev.data.fd = STDIN_FILENO;
ev.events = EPOLLIN; //使用默认的LT条件触发
epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev); // 10 表示等待 30s,过了直接退出
for(;;){
nfds = epoll_wait(epfd, &ev, , -);
for(i=; i<nfds; ++i){
if(ev.data.fd == STDIN_FILENO){
len = read(STDIN_FILENO, buf, sizeof buf - );
buf[len] = '\0';
printf("%s" ,buf);
}
} //强加一个结束条件吧
if(random() % >= )
break;
} puts("Epoll Hello world is end!");
// 只要是文件描述符都要释放
close(epfd);
return ;
}
// 编译
gcc -g -Wall -o epoll_stdin.out epoll_stdin.c

运行结果是

当用户输入的时候,再读取输出一次.

这里再扯一点,关于 我们使用的 类vi 配置

在根目录, touch .vimrc写入下面信息

"设定默认解码
set fenc=utf-
"设置默认字符集
set fencs=utf-,usc-bom,euc-jp,gb18030,gbk,gb2312,cp936
" 用于关闭VI的兼容模式, 采用纯VIM, vi还是比较难搞
set nocompatible
"显示行号
set number
"vim使用自动对齐,也就是把当前行的对齐格式应用到下一行
set autoindent
"依据上面的对齐格式,智能的选择对齐方式
set smartindent
"设置tab键为4个空格
set tabstop=
"设置当行之间交错时使用4个空格
set shiftwidth=
"设置在编辑过程中,于右下角显示光标位置的状态行
set ruler
"设置增量搜索,这样的查询比较smart
set incsearch
"高亮显示匹配的括号
set showmatch
"匹配括号高亮时间(单位为 1/10 s) set ignorecase "在搜索的时候忽略大小写
set matchtime=
"高亮语法
syntax on

还是比较好用的配合.

最后我们举一个简单的 epoll + pthread 案例, 有时候觉得 从底层做起, 一辈子就是水比. 太难搞了.上代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/epoll.h> //4.0 控制台打印错误信息, fmt必须是双引号括起来的宏
#define CERR(fmt, ...) \
fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
__FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__) //4.1 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
#define CERR_EXIT(fmt,...) \
CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE)
//4.2 检查一行代码,测试结果
#define IF_CHECK(code) \
if((code) < ) \
CERR_EXIT(#code) // 监听队列要比监听文件描述符epoll少一倍
#define _INT_EPL (8192)
#define _INT_BUF (1024)
#define _INT_PORT (8088)
#define _STR_IP "127.0.0.1"
// 待发送的数据
#define _STR_MSG "HTTP/1.0 200 OK\r\nContent-type: text/plain\r\nI am here, heoo...\r\n\r\n" // 线程执行的函数
void* message(void* arg);
// 设置文件描述符为非阻塞的, 设置成功返回0
extern inline int setnonblocking(int fd);
// 开启服务器监听
int openserver(const char* ip, unsigned short port); // 主逻辑,开启线程和epoll 监听
int main(int argc, char* argv[])
{
int nfds, i, cfd;
struct sockaddr_in caddr;
socklen_t clen = sizeof caddr;
pthread_t tid;
struct epoll_event ev, evs[_INT_EPL];
int sfd = openserver(_STR_IP, _INT_PORT);
int efd = epoll_create(_INT_EPL);
if(efd < ) {
close(sfd);
CERR_EXIT("epoll_create %d is error!", _INT_EPL);
} ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sfd;
if(epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev) < ){
close(efd);
close(sfd);
CERR_EXIT("epoll_ctl is error!");
} // 这里开始等待操作系统通知文件描述符是否可以了
__startloop:
if((nfds = epoll_wait(efd, evs, _INT_EPL, -)) <= ){
if(nfds == || errno == EINTR)
goto __startloop;
// 这里出现错误,直接返回
CERR("epoll_wait is error nfds = %d.", nfds);
goto __endloop;
}
// 这里是事件正确
for(i=; i<nfds; ++i) {
if(evs[i].data.fd == sfd) { // 新连接过来
// clen做输入和输出参数
cfd = accept(sfd, (struct sockaddr*)&caddr, &clen);
if(cfd < ) {
CERR("accept is error sfd = %d.", sfd);
goto __startloop; //继续其它服务
}
CERR("[%s:%d] happy connected here.", inet_ntoa(caddr.sin_addr), htons(caddr.sin_port));
// 这里开始注册新的文件描述符过来
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = cfd;
setnonblocking(cfd);
if(epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &ev) < ) {
CERR("epoll_ctl add cfd : %d error.", cfd);
// 这里存在一个cfd没有释放问题, 指望 exit之后帮我们释放吧
goto __endloop;
}
}
else { // 这里是处理数据可读可写
// 速度太快,也存在数据异常问题
if(pthread_create(&tid, NULL, message, &evs[i].data.fd) < ) {
CERR("pthread_create is error!");
goto __endloop;
}
}
} goto __startloop;
__endloop: CERR("epoll server is error, to exit...");
close(efd);
close(sfd);
return ;
} // 线程执行的函数
void*
message(void* arg)
{
char buf[_INT_BUF];
int cfd = *(int*)arg, rt; //得到文件描述符 // 设置线程分离属性,自己回收
pthread_detach(pthread_self()); // 数据循环读取, 非阻塞随便搞
for(;;) {
rt = read(cfd, buf, _INT_BUF - );
if(rt < ){
if(errno == EINTR) //信号中断继续
continue;
// 由于非阻塞模式,当缓冲区已无数据可以读写的时候,触发EAGAIN信号
if(errno == EAGAIN){
rt = ; //标志客户端连接没有断
break;
}
// 下面就是错误现象
CERR("read cfd = %d, is rt = %d.", cfd, rt);
break;
}
// 需要继续读取客户端数据
if(rt == _INT_BUF - )
continue; // 下面表示客户端已经关闭
CERR("read end cfd = %d.", cfd);
break;
} // 给客户端 发送数据
if( rt > ) {
// 给客户端发送信息, 多个'\0'吧
write(cfd, _STR_MSG, strlen(_STR_MSG) + );
} // 这里是处理完业务,关闭和服务器连接
close(cfd);
return NULL;
} // 设置文件描述符为非阻塞的, 设置成功返回0
inline int
setnonblocking(int fd)
{
int zfd = fcntl(fd, F_GETFD, );
if(fcntl(fd, F_SETFL, zfd | O_NONBLOCK) < ){
CERR("fcntl F_SETFL fd:%d, zfd:%d.", fd, zfd);
return -;
}
return ;
} // 开启服务器监听
int
openserver(const char* ip, unsigned short port)
{
int sfd, opt = SO_REUSEADDR;
struct sockaddr_in saddr = { AF_INET };
struct rlimit rt = { _INT_EPL, _INT_EPL }; //设置每个进程打开的最大文件数
IF_CHECK(setrlimit(RLIMIT_NOFILE, &rt));
// 开启socket 监听
IF_CHECK(sfd = socket(PF_INET, SOCK_STREAM, ));
//设置端口复用, opt 可以简写为1,只要不为0
IF_CHECK(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt));
// 设置bind绑定端口
saddr.sin_addr.s_addr = inet_addr(ip);
saddr.sin_port = htons(port);
IF_CHECK(bind(sfd, (struct sockaddr*)&saddr, sizeof saddr));
//开始监听
IF_CHECK(listen(sfd, _INT_EPL >> )); // 这时候服务就启动起来并且监听了
return sfd;
}

这个服务器监测客户端连接发送报文给客户端

编译的时候需要加上 -lpthread

运行结果如下

客户端

上面的关于epoll案例,有机会一定要自己学学. 都挺耗时间的. 但是 不学也不见有什么更有意思的事. 到这里有机会继续分享那些开发中用到的基础

模型.网络开发确实不好搞, 细节太多, 但也容易都是套路...到这里说再见了,希望本文提供一些关于libuv的学习方法和epoll基础案例能够让你至少听过

,有了装逼的方向.

后记

  错误是难免的,有问题再交流....

网络开发库从libuv说到epoll的更多相关文章

  1. Winpcap网络开发库入门

    原文链接地址:http://www.cnblogs.com/phinecos/archive/2008/10/20/1315176.html Winpcap是一个强大的网络开发库,可以实现许多功能:获 ...

  2. 利用epoll写一个"迷你"的网络事件库

    epoll是linux下高性能的IO复用技术,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率.另一点原因就是获取 ...

  3. State Threads之网络架构库

    原文: State Threads for Internet Applications 介绍 State Threads is an application library which provide ...

  4. 浅论Android网络请求库——android-async-http

    在iOS开发中有大名鼎鼎的ASIHttpRequest库,用来处理网络请求操作,今天要介绍的是一个在Android上同样强大的网络请求库android-async-http,目前非常火的应用Insta ...

  5. swift中第三方网络请求库Alamofire的安装与使用

    swift中第三方网络请求库Alamofire的安装与使用 Alamofire是swift中一个比较流行的网络请求库:https://github.com/Alamofire/Alamofire.下面 ...

  6. [转]Android各大网络请求库的比较及实战

    自己学习android也有一段时间了,在实际开发中,频繁的接触网络请求,而网络请求的方式很多,最常见的那么几个也就那么几个.本篇文章对常见的网络请求库进行一个总结. HttpUrlConnection ...

  7. Android之网络请求库

    自己学习android也有一段时间了,在实际开发中,频繁的接触网络请求,而网络请求的方式很多,最常见的那么几个也就那么几个.本篇文章对常见的网络请求库进行一个总结. HttpUrlConnection ...

  8. Android进阶笔记02:Android 网络请求库的比较及实战(二)

    一.Volley        既然在android2.2之后不建议使用HttpClient,那么有没有一个库是android2.2及以下版本使用HttpClient,而android2.3及以上版本 ...

  9. Android进阶笔记01:Android 网络请求库的比较及实战(一)

    在实际开发中,有的时候需要频繁的网络请求,而网络请求的方式很多,最常见的也就那么几个.本篇文章对常见的网络请求库进行一个总结. 一.使用HttpUrlConnection: 1. HttpUrlCon ...

随机推荐

  1. Bug管理工具的使用介绍(Bugger 2016)

    1. Bugger 2016 介绍 Bugger 2016 is the version of Bugger adding support fot Team Foundation Server bug ...

  2. 带你快速了解CODESOFT 2015

    CODESOFT是知名的条码标签设计打印软件,现在又推出了最新版CODESOFT 2015,其新功能.大改进让人为之一振.下面跟随小编的脚步,走进CODESOFT 2015,看一看CODESOFT 2 ...

  3. OpenGL: 渲染管线理论

    http://blog.csdn.net/augusdi/article/details/19934463 学习着色器,并理解着色器的工作机制,就要对OpenGL的固定功能管线有深入的了解. 首先要知 ...

  4. tornado框架之路二

    二.路由系统 路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类. #!/usr/bin/env pyth ...

  5. php curl 提交 总结

    1.post https提交 方法 function curl_post2($url='', $postdata='', $options=array()){ $ch = curl_init($url ...

  6. pb中创建连接webservice对象实例方法

    try soapConnection conn // Define SoapConnectionHospitalServiceSoap in_hhzswebser // Declare proxyin ...

  7. RAW格式

    一.什么是RAW文件?RAW文件主要是一种记录了数码相机传感器的原始信息,同时伴随着一些由相机所产生的一些元数据(metadata,诸如IS0的设置.快门速度.光圈值.白平衡等)的文件.不同的相机制造 ...

  8. java 命令行操作

    打包命令 例如:将testproject下的文件打包成test.war jar cvf test.war ./testproject

  9. 教你快速写出多线程Junit单元测试用例 - GroboUtils

    摘自: http://mushiqianmeng.blog.51cto.com/3970029/897786/ 本文出自One Coder博客,转载请务必注明出处: http://www.coderl ...

  10. 一步一图:从SQLSERVER2005中导出insert语句

    1.为什么要导出insert语句,我电脑装的是SQL Server Express免费版的,服务器上装的是正式版,在服务器上备份的数据库文件在本机上还原的时候 因为版本不一样,总是不成功.如果能直接使 ...