challenge echoクライアント

TCPのechoクライアントを書いてください。

  • サーバのホスト名ないしIPアドレス、およびポートはコマンドライン引数で指定します。
  • 標準入力からユーザの入力を受け取り、echoサーバに送信します。
  • echoサーバから受信したデータを標準出力に出力します。

Windowsなら、Simple TCP/IP Servicesを起動してやれば、ローカルの確認用echo サーバとして使えます。

my_program localhost 7 < input_file > result_file

のようにしてリダイレクトを行った場合にも、result_fileがinput_fileの内容と一致するようにしてみてください。

Posted feedbacks - Nested

Flatten Hidden

こんな感じでしょうか。

1
2
3
4
5
6
7
8
9
echo.client <- function(host.name="localhost", port.number=9999){
  sock <- socketConnection(host=host.name, port=port.number)
  repeat{
    writeLines(readLines(), sock)
    writeLines(readLines(sock, n=1))
  }
}
argv <- commandArgs(trailingOnly=T)
echo.client(argv[1], argv[2])
久しぶりの投稿
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
require 'socket'

host = ARGV[0]
port = ARGV[1].to_i

TCPSocket.open(host, port) { |s|
  while $stdin.gets
    s.write $_
    print s.gets
  end
}
IO::* モジュールを使って。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
use strict;
use warnings;

use IO::Socket::INET;
use IO::Handle;

my $s = IO::Socket::INET->new(
  PeerAddr => $ARGV[0],
  PeerPort => $ARGV[1],
) or die "failed to open $ARGV[0]:$ARGV[1]";

my $i = IO::Handle->new_from_fd(*STDIN, q/r/);
my $o = IO::Handle->new_from_fd(*STDOUT, q/w/);
my $buf;
while ( my $bytes = $i->read($buf, 256) ) {
  $s->syswrite($buf);
  $s->read($buf,$bytes);
  $o->syswrite($buf);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.IO;
using System.Net.Sockets;

class P
{
    static void Main(string[] args)
    {
        TcpClient c = new TcpClient(args[0], int.Parse(args[1]));
        NetworkStream s = c.GetStream();
        TextWriter w = new StreamWriter(s);
        TextReader r = new StreamReader(s);
        string l = null;
        while ((l = Console.ReadLine()) != null)
        {
            w.WriteLine(l);
            w.Flush();
            Console.WriteLine(r.ReadLine());
        }
    }
}

Squeak Smalltalk で。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
| serverAddress portNumber socket string |
serverAddress := NetNameResolver addressFromString: '127.0.0.1'.
portNumber := 9999.
socket := Socket newTCP.
socket connectTo: serverAddress port: portNumber.
World findATranscript: nil.
[(string := FillInTheBlank request: 'string:') notEmpty] whileTrue: [
    socket sendData: string, String crlf.
    Transcript show: socket receiveData].
socket close

$ python client_echo.py localhost 7 < input_file > result_file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/usr/bin/python
# -*- coding: utf8 -*-
import sys, socket

host, port = sys.argv[1:3]
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, int(port)))
sf = s.makefile()

for line in sys.stdin:
    sf.write(line)
    sf.flush()
    print sf.readline(),

PHP 5.1.6

$ php -q client_echo.php localhost 7 < input_file > result_file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/usr/bin/env php
<?php
$host = $argv[1];
$port = $argv[2];

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, $host, $port);

while ($line = fgets(STDIN, 1024)) {
    socket_write($sock, $line, strlen($line));
    echo socket_read($sock, 1024);
}
?>

クライアント機能に限定したnetcatコマンドを作りなさい、ということかしら?

題意が分かりにくかったのでしょうか。申し訳ありません。

自分が想定していたのは、Pythonで書くなら以下のようなコードだったのですが、題意はご自由に解釈していただいて構いません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python
import sys
import os
import socket
from threading import Thread

class Receiver(Thread):
    def __init__(self, sock):
        Thread.__init__(self)
        self.src = sock
    def run(self):
        for data in iter(lambda: self.src.recv(8192), ""):
            os.write(sys.stdout.fileno(), data)

def main(args):
    if len(args) < 3:
        print >>sys.stderr, "usage: %s host port" % args[0]
        sys.exit(1)
    if sys.platform == 'win32':
        sys.stdin.setmode(os.O_BINARY)
        sys.stdout.setmode(os.O_BINARY)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((args[1], int(args[2])))
    receiver = Receiver(sock)
    receiver.start()
    for data in iter(lambda: os.read(sys.stdin.fileno(), 8192), ""):
        sock.send(data)
    sock.shutdown(socket.SHUT_WR)
    receiver.join()
    sock.close()
    return 0

if __name__ == '__main__':
    main(sys.argv)

ええと、さっきのは#7138さんへのレスです。それと、コードが間違っていたので修正します。 setmode()はmsvcrtの関数でした。

「netcatのクライアント部分と同等」であるかは、netcatコマンドの仕様に詳しくないので、申し訳ありませんが何ともいえません。

echoはR.Stevensのunpvのようなネットワークプログラミングの教科書の最初のサンプルとして出てくることが多いでしょうから、その多言語版クックブックという観点を考えていました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env python
import sys
import os
import socket
from threading import Thread
import msvcrt

class Receiver(Thread):
    def __init__(self, sock):
        Thread.__init__(self)
        self.src = sock
    def run(self):
        for data in iter(lambda: self.src.recv(8192), ""):
            os.write(sys.stdout.fileno(), data)

def main(args):
    if len(args) < 3:
        print >>sys.stderr, "usage: %s host port" % args[0]
        sys.exit(1)
    if sys.platform == 'win32':
        msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((args[1], int(args[2])))
    receiver = Receiver(sock)
    receiver.start()
    for data in iter(lambda: os.read(sys.stdin.fileno(), 8192), ""):
        sock.send(data)
    sock.shutdown(socket.SHUT_WR)
    receiver.join()
    sock.close()
    return 0

if __name__ == '__main__':
    main(sys.argv)
あえて無限リストを使って。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(use gauche.net)
(use util.stream)

(call-with-client-socket
    (make-client-socket 'inet (car *argv*) (x->integer (cadr *argv*)))
  (lambda (i o)
    (stream-for-each
     (lambda (l)
       (display #`",|l|\n" o)
       (print (read-line i)))
     (port->stream (current-input-port) read-line))))

ocamlfind c -package unix -linkpkg -o echoc echoc.ml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
open Unix

let conn host port =
  let hostent = gethostbyname host in
  open_connection (ADDR_INET(hostent.h_addr_list.(0), port))

let main () =
  let (ich, och) = conn Sys.argv.(1) (int_of_string Sys.argv.(2)) in
  let rec loop () =
    let str = read_line () in
    Printf.fprintf och "%s\r\n" str; flush och;
    print_endline (input_line ich);
    loop () in
  loop ()

let _ = main ()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
module Main where                                                                                                            

import Control.Arrow
import Control.Exception (bracket)
import System.Environment (getArgs)
import Network (connectTo, PortID(..))
import System.IO

func :: Kleisli IO (Handle,String) ()
func = arr ((first hPutStr >>> app)
          &&& ((hGetLines *** length . lines) >>> app))
    >>> Kleisli join'
    >>> Kleisli (snd >>> mapM_ putStrLn)
  where join' :: (Monad m) => (m a,m b) -> m (a,b)
        join' (mx,my) = mx >>= \x -> my >>= \y -> return (x,y)

hGetLines handle n
    | n == 0 = return []
    | otherwise = hGetLine handle
                    >>= \l -> hGetLines handle (pred n)
                    >>= return . (:) l

echo :: String -> Handle -> IO ()
echo s handle = do
    hSetBuffering handle LineBuffering
    runKleisli func (handle,s)

main = do
    (h:p:_) <- getArgs
    s <- getContents
    bracket (connectTo h (port p)) hClose (echo s)
  where port = PortNumber . fromIntegral . read

 Javaがまだの様なので。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import    java.io.InputStream;
import    java.io.OutputStream;
import    java.net.Socket;

class Echo {
    Socket    socket;
    OutputStream    out;
    InputStream    in;
    
    public Echo(String host,int port) throws Exception {
        socket = new Socket(host,port);
        out = socket.getOutputStream();
        in = socket.getInputStream();
    }
    
    public void echo(int size,byte[] request) throws Exception {
        byte[] response= new byte[size];
        out.write(request,0,size);
        out.flush();
        in.read(response,0,size);
        System.out.write(response,0,size);
    }
    
    public void close() throws Exception {
        socket.close();
    }
    
    public static void main(String[] args) {
        try {
            Echo    echo = new Echo(args[0],Integer.parseInt(args[1]));
            byte[]    request = new byte[2048];
            int    size;
            while ((size = System.in.read(request)) > 0) {
                echo.echo(size,request);
            }
            echo.close();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
try {
  host = args[0] 
  port = args[1].toInteger()
} catch (e) {
  println "usage: groovy ${this.class.name} host port"
  System.exit 1
}

new Socket(host, port).withStreams { is, os ->
  dis = new DataInputStream(is)
  buf = new byte[1024]
  while ((len = System.in.read(buf)) >= 0) {
    os.write(buf, 0, len)
    dis.readFully(buf, 0, len)
    System.out.write(buf, 0, len)
  }
}

echoのプロトコル(RFC862)に詳しくないのですが、サーバ側からデータの終わり(eofなど)が返ってこないようなので、タイムアウト(10秒)を設定してみました(汗

送信したデータと比較して、同じならcloseする条件もつけました。

escriptが利用可能な環境であれば、以下のようにコマンドラインから実行可能です。

$ ./スクリプト名 localhost 7 < testfile > newfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env escript
main([Hostname, Port])->
        Str = loop(io:get_line(standard_io,''), []),
        Res = gen_tcp:connect(Hostname,
                        list_to_integer(Port),
                        [binary, {packet, 0}]),
        case Res of
                {ok, Socket} ->
                        ok = gen_tcp:send(Socket, Str),
                        receive_data(Socket, [], lists:flatten(Str));
                {error, Why} -> io:format("error ~p~n", [Why]), exit(eError)
        end.

receive_data(Socket, _Original, _Original) ->
                gen_tcp:close(Socket);
receive_data(Socket, SoFar, Original) ->
        receive
                {tcp, Socket, Bin} ->
                        Lines = binary_to_list(Bin),
                        io:format("~s", [Lines]),
                        receive_data(Socket, lists:flatten(SoFar ++ [Lines]), Original);
                {tcp_closed, Socket} ->
                        ok
        after 10000 ->
                gen_tcp:close(Socket)
        end.

loop(eof, SoFar)->
    lists:reverse(SoFar);
loop(Data, SoFar) ->
    loop(io:get_line(''), [Data|SoFar]).
Boost.Asio@C++でやってみました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
#include <boost/asio.hpp>

int main(int argc, char** argv)
{
    if (argc <= 2)
    {
        return 2;
    }
    try
    {
        boost::asio::ip::tcp::iostream ss(argv[1], argv[2]);
        std::string send, recv;
        while (getline(std::cin, send))
        {
            ss << send << std::endl;
            getline(ss, recv);
            std::cout << recv << std::endl;
        }
    }
    catch(std::exception const& e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    }
    return 0;
}

Index

Feed

Other

Link

Pathtraq

loading...