转载:http://arksea.iteye.com/blog/700032
为了避免与port进程的通信受一些printf调试信息的影响,通常都要关闭或者替换标准输入输出。
Unix平台下,open_port只要指定nouse_stdio参数就可以让port进程使用fd 3、4作为通信信道,
而在Windows平台下并没有所谓的3、4 fd可用,调用fdopen(3,“rb”)将会出错,这时可以使用
dup和dup2手动进行替换,例子如下:
port测试进程C代码
- #include "stdafx.h"
- #include <stdio.h>
- #include <iostream>
- #include <io.h>
- #include <fcntl.h>
- #include <windows.h>
- int main(int argc, char* argv[])
- {
- //复制stdio fd,之后我们将使用复制的句柄与Erlang通信
- FILE* f0 = fdopen(dup(0),"rb");
- FILE* f1 = fdopen(dup(1),"wb");
- //Windows下默认为Text模式,按需要设置为BINARY模式,
- //否则\n将会被替换为\r\n,这通常不是我们想要的
- _setmode (_fileno(f0), _O_BINARY);
- _setmode (_fileno(f1), _O_BINARY);
- //用一个临时文件替换stdio,并关闭之
- FILE* ftmp = tmpfile();
- dup2(fileno(ftmp), 0);
- dup2(fileno(ftmp), 1);
- fclose(stdin);
- fclose(stdout);
- fclose(ftmp);
- //测试一些输出
- fwrite("hello",1,5,f1);
- fputc(' ',f1);
- fflush(f1);
- printf("world");
- fflush(stdout);
- std::cout << "!!!!!\n";
- std::cout.flush();
- Sleep(5000);
- return 0;
- }
测试用erlang代码:
- -module(test).
- -compile(export_all).
- start() ->
- P = open_port({spawn, "./Debug/teststdio.exe"},[binary]),
- loop(P).
- loop(P) ->
- receive
- {P, {data, Data}} ->
- io:format("~p~n",[Data]),
- loop(P)
- after 3000 ->
- timeout
- end.
测试结果:
1、替换stdio后:
- Eshell V5.7.5 (abort with ^G)
- 1> c(test.erl).
- {ok,test}
- 2> test:start().
- <<"hello ">>
- timeout
- 3>
2、如果没有替换stdio,将会收到
- test:start().
- <<"hello ">>
- <<"world">>
- <<"!!!!!\r\n">>
- timeout
我们调用第三方库时很难确认是否会有stdio输出,这种方式很好的避免了printf信息对port通信的影响
注意没有替换stdio部分的输出,stdin和stdout没有指定BINARY模式,\n被替换成\r\n了,
再看看前面dup2部分的调用,虽然指定了"rb"和"wb",但这样是无效的,必须手动调用setmode进行设置才会有效
最后说说windows下使用异步stdio的注意点,需要2个设置:
1、在open_port中指定overlapped_io参数
2、用_get_osfhandle函数得到替换后的io的HANDLE,以供ReadFile和WriteFile操作:
- HANDLE hin = (HANDLE)_get_osfhandle(_fileno(f0));
- HANDLE hout= (HANDLE)_get_osfhandle(_fileno(f1));
在C程序里使用异步io虽然麻烦,但有时候却是必须的。比如windows系统下通过port访问串行口时,如果不使用overlapped模式,读取串口与读取stdio是不能同时进行的。