[#37674] [Bug:trunk] Socket.getaddrinfo("www.ruby-lang.org", 80) returns only UDP information — Tanaka Akira <akr@...>
1.9 と 1.8 で Socket.getaddrinfo("www.ruby-lang.org", 80) の返り値が違います。
4 messages
2009/01/01
[#37677] [BUG:trunk] Re: [ruby-cvs:28403] Ruby:r21185 (trunk): * thread.c (rb_thread_blocking_region): add a comment. — "U.Nakamura" <usa@...>
あけましておめでとうございます、なかむら(う)です。
4 messages
2009/01/02
[#37679] [FEATURE:trunk] EncDet again — "Yugui (Yuki Sonoda)" <yugui@...>
Yuguiです。
23 messages
2009/01/03
[#39781] [Feature #973] EncDet again
— Yui NARUSE <redmine@...>
2009/11/25
チケット #973 が更新されました。 (by Yui NARUSE)
[#39782] Re: [Feature #973] EncDet again
— Kouhei Sutou <kou@...>
2009/11/25
須藤です。
[#37680] Re: [ruby-cvs:28500] Ruby:r21282 (trunk): * enc/trans/euc-cn.c: renemed from gb2312.c because GB2312 is — Martin Duerst <duerst@...>
At 15:29 09/01/03, you wrote:
3 messages
2009/01/03
[#37681] Re: [ruby-cvs:28501] Ruby:r21283 (trunk): * enc/trans/chinese.trans: added for transcoding EUC-CN and GB12345. — Martin Duerst <duerst@...>
At 15:31 09/01/03, you wrote:
5 messages
2009/01/03
[#37684] Re: [ruby-cvs:28501] Ruby:r21283 (trunk): * enc/trans/chinese.trans: added for transcoding EUC-CN and GB12345.
— "NARUSE, Yui" <naruse@...>
2009/01/03
成瀬です。
[#37694] Re: [ruby-cvs:28501] Ruby:r21283 (trunk): *enc/trans/chinese.trans: added for transcoding EUC-CN and GB12345.
— Martin Duerst <duerst@...>
2009/01/04
At 23:18 09/01/03, NARUSE, Yui wrote:
[#37686] IRB doesn't prompt for MSWin32 — arton <artonx@...>
artonです。
11 messages
2009/01/03
[#37687] Re: IRB doesn't prompt for MSWin32
— "Ayumu Aizawa" <ayumu.aizawa@...>
2009/01/03
ruby-devにははじめてメールします、あいざわです。
[#37688] [Bug #974] Range#max で終了しないことがある — tadayoshi funaba <redmine@...>
Bug #974: Range#max で終了しないことがある
6 messages
2009/01/03
[#37689] Re: [Bug #974] Range#max で終了しないことがある
— Yukihiro Matsumoto <matz@...>
2009/01/03
まつもと ゆきひろです
[#37692] AddrInfo — Tanaka Akira <akr@...>
提案なのですが、ext/socket で AddrInfo クラスを新設するのはどうでしょうか。
8 messages
2009/01/04
[#37737] Encoding.locale_charmap — Hidetoshi NAGAI <nagai@...>
永井@知能.九工大です.
13 messages
2009/01/08
[#37738] Re: Encoding.locale_charmap
— Yukihiro Matsumoto <matz@...>
2009/01/08
まつもと ゆきひろです
[#37748] $LOAD_PATHとバージョンの運用の関係 — akira yamada / やまだあきら <akira@...>
1.9系でのバージョンの運用と$LOAD_PATHの値について質問です。
12 messages
2009/01/09
[#37758] Re: $LOAD_PATHとバージョンの運用の関係
— "NARUSE, Yui" <naruse@...>
2009/01/11
成瀬です。
[#37889] Re: $LOAD_PATHとバージョンの運用の関係
— Takao Kouji <kouji@...7.net>
2009/02/03
高尾宏治です。
[#37890] Re: $LOAD_PATHとバージョンの運用の関係
— Tanaka Akira <akr@...>
2009/02/04
In article <1A717293-1636-4FD3-87FE-388EF5FFF560@takao7.net>,
[#37897] Re: $LOAD_PATHとバージョンの運用の関係
— "U.Nakamura" <usa@...>
2009/02/04
こんにちは、なかむら(う)です。
[#37749] $LOAD_PATHのconfigureによる指定 — akira yamada / やまだあきら <akira@...>
Ruby 1.9.1RC1では$LOAD_PATHの中の
6 messages
2009/01/09
[#37765] AddrInfo.list_ipaddr — Tanaka Akira <akr@...>
提案なんですが、ホストが持っている IP アドレスのリストを返す
4 messages
2009/01/13
[#37783] [Feature:1.9] backlog option for TCPServer and UNIXServer — Nobuyoshi Nakada <nobu@...>
なかだです。
5 messages
2009/01/18
[#37785] Re: [Feature:1.9] backlog option for TCPServer and UNIXServer
— Tanaka Akira <akr@...>
2009/01/19
In article <20090118200251.12428E0D32@mail.bc9.jp>,
[#37823] [Feature #1038] Signal.trapのcommandにSymbol — Kazuhiro NISHIYAMA <redmine@...>
Feature #1038: Signal.trapのcommandにSymbol
4 messages
2009/01/22
[ruby-dev:37750] socket.rb
From:
Tanaka Akira <akr@...>
Date:
2009-01-10 11:11:08 UTC
List:
ruby-dev #37750
Socket を使っていると、生成するところが TCPSocket 等に比べて
不便なので、以下のようなメソッドをつけてもいいでしょうか。
Socket.tcp(host, port, local_host=nil, local_port=nil) {|socket| ... }
Socket.tcp_server_loop(host=nil, port) {|socket, client_addrinfo| ... }
Socket.unix(path) {|socket| ... }
Socket.unix_server_loop(path) {|socket, client_addrinfo| ... }
Socket.tcp と Socket.unix は
TCPSocket.open と UNIXSocket.open に対応します。
Socket.tcp_server_loop と Socket.unix_server_loop は
TCPServer.open, UNIXServer.open に加えて、accept するループ
まで入っています。
accept まで行うのは、TCPServer.open 内で、getaddrinfo が返し
たすべてのアドレスを扱うためです。サーバソケットを呼出元に返
す TCPServer.open の形だと、IPv4 と IPv6 を分けて扱うことが
できません。別々にソケットを作って分けて扱うというのが
RFC 4038 のお薦めなようです。なお、将来的には、引数を拡張し
て port を複数指定できるとかしてもいいかもしれません。
あと、Socket.unix_server_loop は既存のソケットファイルを
(owner の検査をした上で) 消すようにしてあります。one liner
でテストするときにはそっちのほうが便利ですし、まじめにサーバ
を書くならどうせ他のサーバが生きているかどうかは別の手段で確
認しなければならず、他にサーバがいない状況で呼び出すとすれば
消しちゃって問題ないと思うので。
実装は Ruby で書いてありますので、socket.rb の新設になります。
% svn diff --diff-cmd diff -x '-u -p'
Index: ext/socket/lib/socket.rb
===================================================================
--- ext/socket/lib/socket.rb (revision 0)
+++ ext/socket/lib/socket.rb (revision 0)
@@ -0,0 +1,262 @@
+require 'socket.so'
+
+class AddrInfo
+ # iterates over the list of AddrInfo objects obtained by AddrInfo.getaddrinfo.
+ #
+ # Example:
+ #
+ # AddrInfo.foreach(nil, 80) {|x| p x }
+ # #=> #<AddrInfo: 127.0.0.1:80 TCP (:80)>
+ # # #<AddrInfo: 127.0.0.1:80 UDP (:80)>
+ # # #<AddrInfo: [::1]:80 TCP (:80)>
+ # # #<AddrInfo: [::1]:80 UDP (:80)>
+ #
+ def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
+ AddrInfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
+ end
+end
+
+class Socket
+ # creates a new socket object connected to host:port using TCP.
+ #
+ # If local_host:local_port is given,
+ # the socket is bound to it.
+ #
+ # If a block is given, the block is called with the socket.
+ # The value of the block is returned.
+ # The socket is closed when this method returns.
+ #
+ # If no block is given, the socket is returned.
+ #
+ # Example:
+ #
+ # Socket.tcp("www.ruby-lang.org", 80) {|sock|
+ # sock.print "GET / HTTP/1.0\r\n\r\n"
+ # sock.close_write
+ # print sock.read
+ # }
+ #
+ def self.tcp(host, port, local_host=nil, local_port=nil) # :yield: socket
+ last_error = nil
+ ret = nil
+
+ local_addr_list = nil
+ if local_host != nil || local_port != nil
+ local_addr_list = AddrInfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
+ end
+
+ AddrInfo.foreach(host, port, nil, :STREAM).each {|ai|
+ begin
+ sock = self.new(ai.pfamily, ai.socktype, ai.protocol)
+ rescue SystemCallError
+ last_error = $!
+ next
+ end
+ if local_addr_list
+ if local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
+ begin
+ sock.bind(local_addr)
+ rescue SystemCallError
+ last_error = $!
+ next
+ end
+ else
+ next
+ end
+ end
+ begin
+ sock.connect(ai)
+ rescue SystemCallError
+ last_error = $!
+ sock.close
+ next
+ end
+ ret = sock
+ break
+ }
+ if !ret
+ if last_error
+ raise last_error
+ else
+ raise SocketError, "no appropriate local address"
+ end
+ end
+ if block_given?
+ begin
+ yield ret
+ ensure
+ ret.close if !ret.closed?
+ end
+ else
+ ret
+ end
+ end
+
+ # creates a TCP server on _port_ and calls the block for each connection accepted.
+ # The block is called with a socket and a client_address as an AddrInfo object.
+ #
+ # If _host_ is specified, it is used with _port_ to determine the server addresses.
+ #
+ # The socket is *not* closed when the block returns.
+ # So application should close it explicitly.
+ #
+ # This method calls the block sequentially.
+ # It means that the next connection is not accepted until the block returns.
+ # So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
+ #
+ # Note that AddrInfo.getaddrinfo is used to determine the server socket addresses.
+ # When AddrInfo.getaddrinfo returns two or more addresses,
+ # IPv4 and IPv6 address for example,
+ # all of them are used.
+ # Socket.tcp_server_loop succeeds if one socket can be used at least.
+ #
+ # Example:
+ #
+ # # Sequential echo server.
+ # # It services only one client at a time.
+ # Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
+ # begin
+ # IO.copy_stream(sock, sock)
+ # ensure
+ # sock.close
+ # end
+ # }
+ #
+ # # Threaded echo server
+ # # It services multiple clients at a time.
+ # Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
+ # Thread.new {
+ # begin
+ # IO.copy_stream(sock, sock)
+ # ensure
+ # sock.close
+ # end
+ # }
+ # }
+ #
+ def self.tcp_server_loop(host=nil, port) # :yield: socket, client_addrinfo
+ last_error = nil
+ sockets = []
+ AddrInfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
+ begin
+ s = self.new(ai.pfamily, ai.socktype, ai.protocol)
+ rescue SystemCallError
+ last_error = $!
+ next
+ end
+ sockets << s
+ s.setsockopt(:SOCKET, :REUSEADDR, 1)
+ s.setsockopt(:IPV6, :V6ONLY, 1) if ai.ipv6?
+ begin
+ s.bind(ai)
+ rescue SystemCallError
+ last_error = $!
+ next
+ end
+ begin
+ s.listen(5)
+ rescue SystemCallError
+ last_error = $!
+ next
+ end
+ }
+ if sockets.empty?
+ raise last_error
+ end
+ loop {
+ readable, _, _ = IO.select(sockets)
+ readable.each {|r|
+ begin
+ sock, addr = r.accept_nonblock
+ rescue Errno::EWOULDBLOCK
+ next
+ end
+ yield sock, addr
+ }
+ }
+ ensure
+ sockets.each {|s|
+ s.close if !s.closed?
+ }
+ end
+
+ # creates a new socket connected to path using UNIX socket socket.
+ #
+ # If a block is given, the block is called with the socket.
+ # The value of the block is returned.
+ # The socket is closed when this method returns.
+ #
+ # If no block is given, the socket is returned.
+ #
+ # Example:
+ #
+ # # talk to /tmp/sock socket.
+ # Socket.unix("/tmp/sock") {|sock|
+ # t = Thread.new { IO.copy_stream(sock, STDOUT) }
+ # IO.copy_stream(STDIN, sock)
+ # t.join
+ # }
+ #
+ def self.unix(path) # :yield: socket
+ addr = AddrInfo.unix(path)
+ sock = self.new(:UNIX, :STREAM, 0)
+ begin
+ sock.connect(addr)
+ ensure
+ sock.close if $!
+ end
+ if block_given?
+ begin
+ yield sock
+ ensure
+ sock.close if !sock.closed?
+ end
+ else
+ sock
+ end
+ end
+
+ # creates a UNIX socket server on _path_.
+ # It calls the block for each socket accepted.
+ #
+ # If _host_ is specified, it is used with _port_ to determine the server ports.
+ #
+ # The socket is *not* closed when the block returns.
+ # So application should close it.
+ #
+ # This method deletes the socket file pointed by _path_ at first if
+ # the file is a socket file and it is owned by the user of the application.
+ #
+ # Example:
+ #
+ # # Sequential echo server.
+ # # It services only one client at a time.
+ # Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
+ # begin
+ # IO.copy_stream(sock, sock)
+ # ensure
+ # sock.close
+ # end
+ # }
+ #
+ def self.unix_server_loop(path) # :yield: socket, client_addrinfo
+ begin
+ st = File.lstat(path)
+ rescue Errno::ENOENT
+ end
+ if st && st.socket? && st.owned?
+ File.unlink path
+ end
+ serv = self.new(:UNIX, :STREAM, 0)
+ serv.bind(AddrInfo.unix(path))
+ serv.listen(5)
+ loop {
+ sock, addr = serv.accept
+ yield sock, addr
+ }
+ ensure
+ serv.close if serv && !serv.closed?
+ end
+
+end
+
--
[田中 哲][たなか あきら][Tanaka Akira]