情報システム科
Java インターネットプログラミング
目次
Javaのインストール
アプリケーションプロトコル
TCPソケットインターフェース
UDPソケットインターフェイス
DAYTIME
ECHO
POP3
SMTP
HTTP
TELNET
DNS
Javaのインストール
J2SE(Java 2 Platform Standard Edition)
SDK(Standard Developer's Kit)と---SDKを使用
JRE(Java 2 Runtime Environment)--実行だけしか出来ない
API
SDK
http://java.sun.com/j2se/1.4.1/ja/
j2sdk-1_4_1_02-windows-i586.exe
J2SDK: Java2 Software Development Kit
非推奨 depricated
強引に -deprication
アプリケーションプロトコル
インターネットの4層構造
アプリケーション層(サービス)application layer
トランスポート層(UDP/TCP)transport layer
ネットワーク層(IP)network layer
データリンク層(物理ネットワーク層)datalink layer
ピア・トゥ・ピア(peer-to-peer)通信
同じ層同士の通信において、あたかも他の層が存在しないかのように
互いに通信できる有様。
ポートの割り当て
類別 ポート番号
ウェルノン(wel-known) 0〜1023
登録済み(registered) 1024〜49151
個人使用(dynamic/private) 49192〜65535
RFC(Request for Comments)
IETF(Internet Engineering Task Force)が発行するインターネットの仕様書
ドキュメントの位置付けを示すステータス
Internet Standard(標準)
Drsft Standerd(標準目前)HTTP
Proposed Standerd(標準化途上)Cookie
Informational(参考情報)
Best Current Practice(非技術文書)
RFCとの関連
Update(追加)
Obsoleted(無効)
RFCの仕様記述
RFC2119 Key word for use in RFCs to Indicate Requirements Levels
(要求度合いを示すRFC中の用語)
用語 意味
MUST/REQUIRED 必須
MUST NOT/SHALL NOT 絶対にやってはいけない
SHOULD/RECOMMENDED したほうが進められる
SHOULD NOT/NOT RECOMMENDED しない方が身のためである
MAY/OPTIONAL 好みに応じて(実装依存)
ウェルノンポート
http://www.iana.org/assignments/port-numbers
RFC
ftp://ftp.nic.ad.jp/rfc/
日本語RFCリスト
http://www5d.biglobe.ne.jp/~stssk/rfcjlist.html
http://www.se.hiroshima-u.ac.jp/~isaki/rfc/list.html
TCPソケットインターフェース
アプリケーション層に属するクライアントとサーバーは、トランスポート層(TCP/IP)とのインターフェースを経由して相互にメッセージの送受を行っている。
このアプリケーションインターフェース(API)をTCP/IP界では、「ソケット」(socket)と呼ぶ機構で実装している。
●TCPクライアントソケット
java.net.Socket soc = new java.net.Socket(String host,int port);
クライアントはサーバーにTCP接続され、ソケットを通じて
メッセージの読み書きが出来る。
host:ドメイン名(yahoo.co.jp)でも、アドレス(123.456.779.123)でもいい。
port:ポート番号
java.io.InputStream is = soc.getInputStream();
java.io.InputStream os = soc.getOutputStream();
ソケットに対して入出力
但し、バイト(byte)指向。
java.io.BufferedReader in =new java.io.BufferedReader
(new java.io.InputStreamReader(is));
java.io.PrintWriter out = new java.io.PrintWriter(os);
行単位指向の入出力
***TCPクライアント用クラス
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TcpClient {
Socket soc = null;
PrintWriter out = null;
BufferedReader in = null;
public TcpClient (String host, int port)
throws java.net.UnknownHostException,
java.io.IOException
{
soc = new Socket (host, port);
out = new PrintWriter (soc.getOutputStream(), true);
in = new BufferedReader (new
InputStreamReader(soc.getInputStream()) );
}
public void close ()
throws java.io.IOException
{
if (out != null)
out.close ();
if (in != null)
out.close ();
if (soc != null)
soc.close ();
System.out.println ("Connection closed");
}
//***TCPClient用の例
public static void main(String[] args)
{
//クライアントソケットとI/O回り
TcpClient myTcp = new TcpClient(host,port);
//サーバから一行受信する
String recv = myTcp.in.readLine();
//サーバーに1行送信する
myTcp.out.Println("Hello World");
//コネクションを切断する
myTcp.close();
}
}
●TCPサーバーソケット
java.net.ServerSocket serv = new java.net.ServerSocket(int port);
サーバーにはソケット作成後、クライアントからの接続要求が来るまで待機、
来ればそれを受託する仕組みとして ServerSocketのaccept()メソッド使用。
Socket soc = serv.accept();
クライアントが接続してくるまで、プログラムはそこで停止。
クライアントから接続要求を送ると、accept()によって作成されたソケットsoc
に対して入出力を行う。
***TCPサーバー用クラス
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main (String[] args)
throws Exception
{
ServerSocket serv = new ServerSocket (Integer.parseInt(args[0]));
while (true) {
System.out.println ("Waiting for client...");
Socket soc = serv.accept ();
// クライアントから受信
BufferedReader in = new BufferedReader
(new InputStreamReader (soc.getInputStream ()));
String recv = in.readLine ();
// クライアントに送信
PrintWriter out = new PrintWriter (soc.getOutputStream());
out.println (recv);
// クライアントをクローズ
out.close ();
in.close ();
soc.close ();
}
}
}
実行
java TcpServer 80
●待ち行列(backlog)
java.net.ServerSocket serv = new java.net.ServerSocket
(int port,int backlog);
同時に複数のクライアントから接続要求があると、あとのクライアントは
待ち行列に入る。デフォルトは50。backlogで指定。
例
java.net.ServerSocket serv = new java.net.ServerSocket
(int port,2);
現在接続中以外に、2,3番目まで、待ち行列に入る。4番目は接続を拒否される。
●反復サーバ、並行サーバ
反復サーバ:1つづつ。
並行サーバ:並行に。
スレッドを使って並行サーバを。
public class TcpServer2 extends Thread
Threadを継承。拡張(extend)。
public void run(){ //特別なメソッドで、このスレッドの処理を表す
xxx.start();
スレッドをスレッドスケジューラに登録し、実行の順番待ちにする。
***並行サーバ
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer2 extends Thread {
private Socket soc = null;
public TcpServer2 (Socket s) {
this.soc = s;
}
public void run () {
try {
// クライアントから受信
BufferedReader in = new BufferedReader
(new InputStreamReader (this.soc.getInputStream ()));
String recv = in.readLine ();
// クライアントに送信
PrintWriter out = new PrintWriter (this.soc.getOutputStream());
out.println (recv);
// クライアントをクローズ
out.close ();
in.close ();
this.soc.close ();
}
catch (Exception e) {
}
}
public static void main (String[] args)
throws Exception
{
ServerSocket serv = new ServerSocket (Integer.parseInt(args[0]));
while (true) {
System.out.println ("Waiting for client...");
Socket s = serv.accept ();
new TcpServer2 (s).start ();
}
}
}
●ソケット・アソシエーション
クライアント サーバ
ServerSocketのソケット(仮想)→ServerSocketのソケット(実態)
OSが割り振るソケット(実態) ←acceptのソケット(仮想)
OSが割り振った自機のポート番号をクライアント側が知る
Socket.getLocalPort() //自機のポート番号を知る
Socket.getLocalAddress() //自機のアドレスを知る
String LocalHost = soc.getLocalAddress().getHostName();
int LocalPort = soc.getLocalPort();
System.out.pringln("MySocket="+LocalHost+";"+LocalPort);
サーバ側でクライアントの正体を知る
String CliantHost = soc.getInetAddress().getHostName();
int CliantPort = soc.getPort();
System.out.println("Cliant="+CliantHost+";"+CliantPort);
をaccept の後に。
自機(Local)のアドレスとポート、相手(Server)のアドレスとポート、
これにトランスポート層のプロトコルの合計5つを組にして
「アソシエーション」(association)と言う。
{prot.LocalAddless,LocalPort,foreignAddr,foreignPort}
{TCP,flog.com,80,cay.com,1022}
.
UDPソケットインターフェイス
UDPはTCPと異なり、データ配送に信頼性が無いため、
アプリケーションレベルに信頼性を組み込まねばならない。
手間がかかるが、その分高速で、軽い。
サービス比較
機能 IP UDP TCP
誤り正義 チェックサム × △ ○
受信確認 × × ○
タイムアウト再送 × × ○
順序制御 順序保障 × × ○
重複検出 × × ○
流量制御 × × ○
効率がいい
TFTP(Trivial File Transfer Protocol)ディスクレスシステムがブート時、
サーバーからカーネルを取得するとき用いる。
DHCP(Dynamic Host Configuraation Protocol)
SNMP(Simple Network Management Protocol)ネットワーク機器を監視
タイミング
NTP(Network Time Protocol)ホストの時間を同期させる
RADIUS(Remote Access Dial-InUser Sever)pppダイヤルアップでユーザが
ISP(プロバイダ)に接続すると、(NAS)がアカウントデータベースを用いて
認証を行うときのプロトコル。
●UDPクライアント(non-connected)
DatagramSocket
相手のアドレスとポートは指定しない。
(host,port)指定する時は、自分のアドレスとポート。
java.net.DatagramSocket soc = java.net.DatagramSocket();
送信先はデータグラム(パケット)にメッセージを格納する時に、
各パケットごとに書き込む。
java.net.DatagramPacket packet = new java.net.DatagramPacket
(byte[] message,int messageLength,
java.net.InetAddress host,int port);
例
java.net.DatagramPacket packet = new java.net.DatagramPacket
("Hello World!".getBytes(),12,
java.net.InetAddress.getByName("cornas.com"),7);
soc.send(packet);
で送信
受信
空のパケットをDatagramPacketを用いて作成
messageは受信するメッセージよりも大きく
int len = 256; //例えば256バイトまで受信する
byte[] message = new byte[len];
DatagramPacket message = new DatagramPacket (message, len);
soc.receive (message); //受信する
System.out.println(message.getLength()+" received"); //メッセージサイズ
***UDPクライアント
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpClient {
DatagramSocket soc = null;
InetAddress addr = null;
int port = 0;
public UdpClient (String host, int port)
throws java.net.UnknownHostException,
java.io.IOException
{
this.port = port;
this.addr = InetAddress.getByName (host);
this.soc = new DatagramSocket ();
}
public void send (String sendMessage)
throws java.io.IOException
{
byte[] bSend = sendMessage.getBytes();
DatagramPacket pSend = new DatagramPacket
(bSend, bSend.length, this.addr, this.port);
this.soc.send (pSend);
}
public String recv ()
throws java.io.IOException
{
int len = 256;
byte[] bRecv = new byte[len];
DatagramPacket pRecv = new DatagramPacket (bRecv, len);
soc.receive (pRecv);
return new String(pRecv.getData(), 0, pRecv.getLength());
}
public void close ()
throws java.io.IOException
{
if (soc != null)
soc.close ();
}
}
●バイナリの取り扱いーーーネットワークバイトオーダー
ネットワークバイトオーダー(network byte order)
最初に到着したバイトが上位(MSB:Most Significant Byte)
最後が最も最下位(LSB:Least Significant Byte)
大きい方が先に到着するので、ビッグエンディアン(big endian)
JavaのByte(8ビット)は符号付なのでDatagramPcketからbyte[]を直接読んで
そのまま演算子してはいけない場合が多い。
バイナリ指向のプロトコルでは、大半符号なしでバイト解釈をするから。
unsigned系のメソッドjava.io.DataInputStream,
java.io.DataOutputStreamクラスを使う
java.io.DataInputStream dis = new java.io.DataInputStream
(new ByteArrayInputStream(byte[] input));
int c = dis.readUnsignedByte(); //1バイトを[0,255]で解釈
int i = dis.readUnsignedShort(); //2バイトを[0,65535]で解釈
書き込み
cjava.io.DataOutputStream baos = new cjava.io.DataOutputStream();
java.io.DataOutputStream dos =new java.io.DataOutputStream(baos);
dos.writeByte(int i); //1バイト書く
dos.writeShort(int i); //2バイト書く
byte[] output = baos.toByteArray(); //byte[]として読む
●バーチャルサーキットとコネクティッドUDP
TCPでは、ソケットが作成されるとクライアント/サーバ両端のソケット
そのものにアドレスとポートが結合(bind)され、両者が目に見えない直通
回線で結び付けられる。
UDPにはこのような回線はない。届け先は手紙そのものに書かねばならない。
大量に送るときは、擬似的に接続を行う。
DatagramSocketのconnected()メソッドを用いる。
DatagramSocket socket = new DatagramSocket();
socket.connect(InternetAddress addr,int port);
これで送信パケットはアドレスなしで作成できる
DatagramPacket packet = new DatagramPacket
(byte[] message,int messageLength);
●UDPでの大きなデータの取り扱い
定義からは65,508バイトだが、
実際は4,096バイトあたりが妥当な安全ライン。
磐石を期すならば、567バイト。
これを単位にアプリ側で分割する。
●UDPサーバ
サーバもクライアントと同じDatagramPacket()クラスを使う。
返信先のクライアントを知るためには、
DatagramPacketのgetAddress()とgetPort()を使う。
これをDategramPacketの第3引数、第4引数に書き込み、
メッセージとその長さを第1引数、第2引数に指定する。
例
DatagramPaacket recPacket;
socket.receive(recPacket);
DatagramePacket sendPacket = new DatagramPcket
("Nice to see you.".getbytes(),16,
recPacket,getAddress(),recPacket.getPort());
●UDPにおける並行サーバ
TCPはバーチャルサーキットだが、UDPは回線が占有されることの無い
コネクションレスである。
1つのソケット、1本の処理システムだけで、複数のクライアントからの
パケットを処理できる。
DAYTIME
サービス名 :DAYTIME
ポート番号 :13
トランスポート:TCP/UDP
RFC :867(May 1983)
サービス内容 :ホストの時刻を通知する
***DAYTIME TCP クライアント
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;
public class DaytimeTcpClient {
/** Daytime Service port */
public static final int port = 13;
/** Main function */
public static void main (String args[])
throws Exception
{
if (args.length <= 0) {
System.out.println ("DaytimeTcpClient hostname");
System.exit (1);
}
Socket soc = new Socket (args[0], port); //クライアントソケット作成
BufferedReader in = new BufferedReader (new
InputStreamReader(soc.getInputStream()) );
String response = in.readLine(); //1行読む
System.out.println ("Time is: " + response);
in.close ();
soc.close ();
System.exit (0);
}
}
java DaytimeTcpClient abc.com
***DAYTIME UDP クライアント
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class DaytimeUdpClient {
/** Daytime Service port */
public static final int port = 13;
/** Main routine */
public static void main (String[] args)
throws Exception
{
if (args.length <= 0) {
System.out.println ("TcpDaytimeClient hostname");
System.exit (1);
}
InetAddress addr = InetAddress.getByName (args[0]);
DatagramSocket socket = new DatagramSocket ();
// create & send dummy message
byte[] bSend = "Dymmy message".getBytes(); //ダミーのデータを送信
DatagramPacket pSend = new DatagramPacket
(bSend, bSend.length, addr, port);
socket.send (pSend);
// receive date/time and dumps to tty
int len = 64;
byte[] bRecv = new byte[len];
DatagramPacket pRecv = new DatagramPacket (bRecv, len);
socket.receive (pRecv);
System.out.println
(new String(pRecv.getData(), 0, pRecv.getLength()));
socket.close ();
System.exit (0);
}
}
DAYTIME 反復型 TCPサーバ
時刻生成
Date d = new Date();
Locale loc = new Locale ("en", "NZ");
TimeZone tz = TimeZone.getTimeZone ("JST");//日本時間に変更
SimpleDateFormat sdf = new SimpleDateFormat (
"yyyy/MM/dd HH:mm:ss (E), zzz", loc);
sdf.setTimeZone (tz);
String Date = sdf.format (d); //システム時刻の取得
国:NZ(New Zealand) 言語:en(English)
→AD 2001/March/10(Sat)03:36:55 AM GMT+13:00
国:JP(日本) 言語:ja(日本語)
→西暦 2001/3月/10(土)03:36:55 午後 GMT+09:00
***DAYTIME 反復型 TCPサーバ
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class DaytimeTcpServer {
/** port number (Daytime = 13) */
public static int port = 13;
/** main routine */
public static void main (String[] args)
throws Exception
{
ServerSocket serv = new ServerSocket (port);
Locale loc = new Locale ("en", "NZ");
SimpleDateFormat sdf = new SimpleDateFormat (
"yyyy/MM/dd HH:mm:ss (E), zzz", loc);
TimeZone tz = TimeZone.getTimeZone ("JST");//日本時間に変更
sdf.setTimeZone (tz);
while (true) {
System.out.println ("Waiting for client...");
Socket soc = serv.accept ();
PrintWriter out = new PrintWriter (soc.getOutputStream());
String now = sdf.format (new Date()); //システム時刻の取得
out.print (now + "\r\n");
out.flush ();
out.close ();
soc.close ();
}
} // end of main
} // end of class
ECHO
サービス名 :ECHO
ポート番号 :7
トランスポート:TCP/UDP
RFC :862(May 1983)
サービス内容 :クライアントの送出したメッセージをそのまま返送する
ECHOクライアント ECHOサーバ
接続(hpst,7) −−−−−−>
メッセセージ1−−−−−−>
<−−−−−−メッセセージ1
メッセセージ2−−−−−−>
<−−−−−−メッセセージ2
・・・・・・
切断 −−−−−−>
クライアント側が送信したメッセージを、サーバ側が「やまびこ」のようにそ
のまま送り返す。
ホスト間でTCP/IPが正常に動作しているかどうかを確かめるための、重要なツ
ール。
***Echo クライアント TCP 版
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class EchoTcpClient {
/** Echo Service port */
public static final int port = 7;
/** Main function */
public static void main (String args[])
throws Exception
{
if (args.length <= 0) {
System.out.println ("EchoTcpClient hostname");
System.exit (1);
}
Socket soc = new Socket (args[0], port);
BufferedReader in = new BufferedReader (new
InputStreamReader(soc.getInputStream()) );
PrintWriter out = new PrintWriter (soc.getOutputStream(), true);
for (int i=0; i<10; i++) {
out.println ("Hello World " + i); //送信
//PrintWriterのflushメッセージがtrueなので、
//バッファされずに送出される。
System.out.println ("Server says: " + in.readLine());//受信
}
}
} // end of class
***Echo クライアント UDP 版
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class EchoUdpClient {
/** Echo Service port */
public static final int port = 7;
public static void main (String args[])
throws Exception
{
if (args.length <= 0) {
System.out.println ("EchoUdpClient hostname");
System.exit (1);
}
InetAddress addr = InetAddress.getByName (args[0]);//サーバアドレス
DatagramSocket soc = new DatagramSocket (); //ソケット作成
for (int i=0; i<10; i++) {
// Sending message
String message = "こんにちは " + i;
DatagramPacket pSend = new DatagramPacket
(message.getBytes(), message.length(), addr, port);
soc.send (pSend);
System.out.println ("=> " + message);
// Receiving message
int len = 512;
byte[] bRecv = new byte[len];
DatagramPacket pRecv = new DatagramPacket (bRecv, len);
soc.receive (pRecv);
System.out.println
("<= " +
new String(pRecv.getData(), 0, pRecv.getLength()) );
}
soc.close ();
System.exit (0);
}
} // end of class
***Echo 並行サーバ TCP版
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.net.Socket;
public class EchoTcpServer extends Thread {
public static final int port = 7;
private Socket soc = null;
private int bufferSize = 512;
public EchoTcpServer (Socket s) {
this.soc = s;
}
public void run () {
try {
InputStream is = this.soc.getInputStream ();
OutputStream os = this.soc.getOutputStream ();
int len = 0;
byte message[] = new byte[bufferSize];
while ( (len=is.read (message)) >= 0 ) {
os.write (message, 0, len);
}
os.close ();
is.close ();
this.soc.close ();
}
catch (Exception e) {
e.printStackTrace ();
}
}
public static void main (String[] args)
throws Exception
{
ServerSocket serv = new ServerSocket (port);
while (true) {
System.out.println ("Waiting for client...");
Socket s = serv.accept ();
System.out.println
("Client connected." +
" Addr=" + s.getInetAddress().getHostName() +
" port=" + s.getPort() );
new EchoTcpServer (s).start ();
}
}
} // end of class
***Echo サーバ UDP版
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class EchoUdpServer {
/** Echo Service port */
public static final int port = 7;
public static void main (String args[])
throws Exception
{
DatagramSocket soc = new DatagramSocket (port);
while (true) {
int bufferSize = 512;
// Receiving message
byte[] bRecv = new byte[bufferSize];
DatagramPacket pRecv = new DatagramPacket (bRecv, bufferSize);
soc.receive (pRecv);
InetAddress clientAddr = pRecv.getAddress ();
int clientPort = pRecv.getPort ();
int len = pRecv.getLength ();
System.out.println
("Received: address=" + clientAddr.getHostName() +
" port=" + clientPort +
" len=" + len);
// Sending message
DatagramPacket pSend = new DatagramPacket
(pRecv.getData(), len, clientAddr, clientPort);
soc.send (pSend);
}
}
} // end of class
POP3
サービス名 :POP3
ポート番号 :110
トランスポート:TCP/UDP(実装上TCPのみが用いられている)
RFC :1939(May 1996)
サービス内容 :メール取得サービス
RFC1939(HTTP)日本語訳
http://www.se.hiroshima-u.ac.jp/~isaki/rfc/rfc1939-jp.txt
POP3: Post Office Protocol Version 3
メール配信━━→メールサーバ
(SMTP) ↓格納
maildrop
↑読み込み
POPサーバ ━━━━━→ POP3クライアント
配送(POP3)
●POP3セッション
POP3は、典型的なクライアント/サーバ型のプロトコル。
サーバはクライアントを受動的に待っていて、クライアントのコネクション発
生とともに活性化する。
クライアントは要求(request)を送り、サーバはそれに応答(respons)をする。
クライアントがTCP接続してきた時点で、POP3サーバはまず1行の
挨拶文(greeting)を送信する。
------------------
応答:OK barbarrexco.it POP3 Server (Version 1.006d) ready at
------------------
POP3セッションの段階
認証段階(authentication)
┃ ↓
┃処理段階(transaction)
↓ ↓
更新段階(update)
●POP3要求/応答フォーマット
POP3要求 := [コマンド]{[引数1][引数2]・・・}[CRLF]
コマンドと引数の間は必ず1つだけスペース(0x20)で区切られ、
リクエストの末尾はCRLF(0x0D 0x0A)である。
リクエストはCRLFも含めて、最大255文字まで。
POP3応答 := [ステータス]{[その他情報]・・・}[CRLF]
ステータス(+OK)か(-ERR) 大文字
POP3複数行応答 :=
[ステータス]{[その他情報]・・・}[CRLF]
行1[CRLF]
・・・
行N[CRLF]
.[CRLF] ←終了
エスケープ
途中に.[CRLF]があると、終了と勘違いするので
..[CRLF]に変換して送る。受信側で.[CRLF]に直す。
例
4c 31 0d 0a 2e 0d 0a 4c 32 0d 0a 2e 0d 0a
L 1 CR LF . CR LF L 2 CR LF . CR LF
は
4c 31 0d 0a 2e 2e 0d 0a 4c 32 0d 0a 2e 0d 0a
L 1 CR LF . . CR LF L 2 CR LF . CR LF
と送る。
●POP3必須コマンド
必須(required)と、オプショナル(optional)がある。
認証段階ーーUSER,PASS
USER account ID
引数にアクセスするmaildrop(アカウント名)を指示して、認証を懇請。
この後、PASSコマンドが続かなければならない。
PASS パスワード
USER発行後、引数にパスワードを平文で送信する。
処理段階ーーSTAT,LIST,RETR,DELE,NOOP,RSET
STAT
その時点で、maildropに格納されているメールの数と、総バイト数を
知ることが出来る。
サーバからの返答は、+OKに続いて、メール数、バイト数で、
それぞれは1個のスペースで区切られた1行。
要求: STAT
応答 :+OK 3 1205 ←3通計1,205バイト
LIST [メール番号]
メールそれぞれの大きさ
要求: LIST 1 ←メール1のサイズ
応答 :+OK 1 412 ←メール1のサイズは412バイト
要求: LIST ←引数なしだと複数行で通知
応答 :+OK
応答 :1 412 ←メール1のサイズは412バイト
応答 :2 393 ←メール2のサイズは393バイト
応答 :3 400 ←メール3のサイズは400バイト
応答 :. ←複数行応答の最後 "."
RETR メール番号
メールを読み込む。引数はメール番号。メールはサーバから消去されない。
+OKのあと、複数行からなるメール本体が送られてくる。
DELE メール番号
メールを消去。引数はメール番号。
(消去するようにマーク。実際の消去は更新段階時)
返答は、+OK の1行。
NOOP
なにもしない。+OKだけが返される。デバッグ用。
RSET
DELEでマークされたメールから、マークを取り外す。
引数が無いので、個々のメール毎には出来ない。すべてのマークを取る。
例
要求 : STAT ←現在の状態を調べる
応答 : +OK 3 1205 ←3通計1,205バイト
要求 : DELE 2 ←メール2番を消去マーク
応答 : +OK Message 2 markd
要求 : STAT ←現在の状態を調べる
応答 : +OK 2 812 ←2通計812バイト
要求 : RSET ←マークを取り外す
応答 : +OK
要求 : STAT ←現在の状態を調べる
応答 : +OK 3 1205 ←3通計1,205バイト
更新段階ーーQUIT
QUIT
いずれの段階でも使用できる。認証の段階でも、処理段階でも。
DELEで消去マークのついたメールをmaildropから実際に消去し、
POP3セッションを終了させる。
●POP3オプショナルコマンド
APOP account ID,MD5
パスワードも平文なので、MD5と呼ばれる、暗号方式を利用。
APOPは、一つでUSER/PASSを兼ねている。
TOP メール番号
RETRの亜種で、
全文の変わりに、メールのヘッダ部分と本文を指定行数だけ読み込む。
UIDL [メール番号]
UIDL(Unique ID List)は、LISTの拡張版。
各メッセージにユニークなIDが併せて表示される。
例ーーAPOPで用いられるMD5でメールをダイジェストする実装
要求 : UIDL
応答 : +OK 1 messages
応答 : 1 7a52f1861456f9a0c55469a03b400b0c ←メール1のID
応答 : ←複数行の終端
●要求・応答の処理
***POP3 クライアント共通クラス
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.StringTokenizer;
import java.util.Vector;
public class Pop3Client {
private static final int port = 110;
public Socket soc = null;
private PrintWriter out = null;
private BufferedReader in = null;
public Pop3Client (String server)
throws Exception
{
this.soc = new Socket (server, port);
this.out = new PrintWriter (soc.getOutputStream(), true);
this.in = new BufferedReader
(new InputStreamReader (soc.getInputStream()));
}
public void close ()
throws Exception
{
this.in.close ();
this.out.close ();
this.soc.close ();
}
//リクエストをオープンしたソケットに書き込む
public void sendRequest (String request) {
String CRLF = "\r\n";
this.out.print (request + CRLF); //強制的にCRLFを付ける
this.out.flush ();
}
//応答をスペースごとに分割(parse)して、String配列を返す。
//STAT応答中のメール数のような特定部分だけを参照できる
//スペースをデリミタにしてStringを分割は、
//java.util.StringTokenizerクラスを用いる
public String[] recvParseResponse ()
throws java.io.IOException
{
StringTokenizer st = new StringTokenizer (recvResponse());
String[] ret = new String [st.countTokens()];
for (int i=0; i
タイプスタンプは末尾の<189.988533230@barolo>
まずは、<>を付けたままパスワードのbuonoRossoを付け加え、基のメッセージ
を作成する。
<189.988533230@barolo>buonoRosso
MD5でこれのダイジェストを生成する。
ASCII表示にすると次のようになる。
d60996e2328fa632d8ad44c4df805f47
1バイト(0x00-0xff)を16進数表記にすると2文字になるので、
16バイトは、計32バイトの文字列になる。
要求 : APOP gaji d60996e2328fa632d8ad44c4df805f47
応答 : +OK
MD5は、java.securityパッケージに実装されている
***MD5ダイジェスト作成
import java.security.MessageDigest;
public class MD5 {
public static String getDigest(String key)
throws Exception
{
String digest = null;
MessageDigest md5 = null;
md5 = MessageDigest.getInstance ("MD5");//アルゴリズム指定
md5.update (key.getBytes());
byte[] b = md5.digest ();//digest()で16バイトを生成
digest = b2s(b);
return (digest);
}
//16進数表記のASCII文字列にする。
public static String b2s (byte[] b) {
StringBuffer sb = new StringBuffer ();
for (int i=0; i");
if (end < 0)
return false;
String key = greeting.substring (begin, end+1);
// Get MD5 digest
String digest = MD5.getDigest (key + pass);
String response[];
this.sendRequest ("APOP " + user + " " + digest);
response = this.recvParseResponse ();
if (response[0].equals ("-ERR") == true)
return false;
return true;
}
SMTP
サービス名 :SMTP
ポート番号 :25
トランスポート:TCP
RFC :2821(April 2001)
サービス内容 :メール配信とリレー
SMTP(Simple Mail Transfer Protocol)
●SMTPコマンドとコマンドシーケンス
サーバ クライアント
<−−−TCP接続(new Socket(host,25))
220挨拶文−−−>
<−−−EHLO<ドメイン名>
250挨拶と使用可能なコマンド−−−>
<−−−MAIL From:<送信者>
250 OK−−−>
複数回┏ <−−−RCPT To:<受信者>
┗250 OK−−−>
<−−−DATA
354 データ受信準備 OK−−−>
<−−−データ(メール)
・・・
<−−−.CRLF(データ終端記号)
250データ受信 OK−−−>
<−−−QUIT
250 OK−−−>
<−−−TCP切断(Soket.close())
MAIL→RCPT→DATAは同じセッションの中で何度繰り返してもよい。
MAIL→RCPT→DATAの順序は必ず踏襲されなければならない。
コマンドと引数はスペース(0x20)で区切られ、CRLF(0x0A 0x0D)で終端される。
必須コマンド
EHLO クライアントが自ドメイン名を通知する。
HELO 同上。旧版
MAIL 送信元を伝え、メールセッションを開始する
RCPT 受信先を指定する
DATA メール本体を送る(RFC 2822形式の文)
RSET 状態をリセットする
NOOP 何もしない
QUIT セッションの終了
VRFY 指定ユーザが存在するか確かめる
オプショナルコマンド
EXPN 指定メーリングリストを展開する
HELP ヘルプメッセージを表示する
例
要求 : EHLO abc.com
応答 : 250-Hello,pleased to meet you
応答 : 250-ENHANCEDSTATUSCODES
応答 : 250-EXPN
応答 : 250-VERB
応答 : 250-8BITMIME
応答 : 250-SIZE
応答 : 250-DSN
応答 : 250-ONEX
応答 : 250-ETRN
応答 : 250-XUSR
応答 : 250 HELP
●SMTP応答
3桁の数字で返される(ASCII readable表記で、バイナリではない)
xyz 文字列 [CRLF]
例ーーMAILコマンドが送信され、サーバに受託された
要求 : MAIL From: SIZE=3894 [CRLF]
応答 : 250 ...Snder ok [CRLF]
後に続く応答行があるとき、応答ステータスの数字と文字列の間のスペースが
"-"(0x2D)に置き換わる。
xyz-応答行その1 [CRLF]
xyz-応答行その2 [CRLF]
・・・・
xyz 最終応答行 [CRLF]
xyzのxが1-3でok,4と5がエラー
1yz コマンドは受託されたが、続行するかいなかの確認待ち
2yz コマンドは受託された
3yz コマンドは受託されたが、次のアクション待ち
4yz 修復可能なエラー
5yz 修復不可能なエラー
●メールメッセージ
メッセージヘッダ(message header)と、メッセージボディ(message body)
とに別れる。
メッセージ := [メッセージヘッダ][空行][メッセージボディ]
ヘッダ := [フィールド名]": "[フィールド値][CRLF]
例
Subject: Una furtive Lagrime [CRLF]
複数行の場合、「折り返し」(folding)。続く行の先頭が空白で始める。
例
Subject: Una furtive Lagrime [CRLF]
Neglchi suoi [CRLF]
●SMTPクライアント
***SMTP応答解析ルーチン
public Vector recvResponse ()
throws java.lang.NumberFormatException,
java.io.IOException
{
Vector vec = new Vector (1);
vec.add (0, (Object)(new Integer(0)));
while (true) {
String response = this.in.readLine ();
if (response == null)
return null;
//最初の要素に3桁の数字の応答コード
Integer resCode = new Integer (response.substring (0, 3));
vec.set (0, (Object)resCode);
//応答文字列を格納
String message = response.substring (4);
vec.add ((Object)message);
//4番目をチェックして"-"以外ならループを抜ける
String cont = response.substring (3, 4);
if (cont.equals ("-") == false)
break;
}
return (vec);
}
//Vectorに格納された応答中の応答ステータスを調べる。
//xyzの値が400以下が成功
public static boolean isSuccess (Vector v) {
if (v == null)
return false;
if ( ((Integer)v.get(0)).intValue() < 400)
return true;
return false;
}
HTTP
サービス名 :HTTP
ポート番号 :80
トランスポート:TCP
RFC :2616(June 1999)
サービス内容 :World Wide Web
RFC2616(HTTP)日本語訳
http://www.studyinghttp.net/rfc_ja/2616/rfc2616_ja.html
RFC2396(URI)日本語訳
http://hp.vector.co.jp/authors/VA014833/rfc2396J.html
●URL(Uniform Resource Locator)のフォーマット
[scheme]://[authority]/[path]#[fragment]?[query]
scheme(スキーム)
プロトコル
例
ftp File Transfer Protocol RFC1738
http Hypertext Transfer Protocol RFC2068
gopher The Gopher Protocol RFC1738
https Hypertext Transfer Protocol Secure RFC2818
authority(権限元)
アクセスするホスト
ウェルノンポートと異なるポート番号を用いる場合は
:
www.abc.com:8080
path(パス)
ホスト中のファイルの位置を示す。
fragment(フラグメント)
ファイル中の位置を示す。
query(クエリー)
サーバに対して、URL以外の付加的な指示を与えたいとき用いる。
例
http://www.abc.com/1961/index.html#chapter2
www.abc.comが権限元で、プロトコルがhttpで、
リソースのローカルパスが/1961/index.htmlで、
index.htmlに埋め込まれた < A NAME="chapter2">というタグの位置まで移動する。
●URLで用いられるキャラクターコード
予約済みキャラクタ
; / ? : @ & = + $ ,
用いたい場合は、16新コードの前に"%"を付けて表記
例
my wine?.html -> my%20wine%3F.html
●URLクラス
URL部分 取得メソッド
スキーム(プロトコル) : getProtocol()
権限元 : getAuthority()
ホスト(権限元の一部) : getHost()
ポート(権限元の一部) : getPort()
パス : getPath()
フラグメント : getRef()
クエリー : getQuery()
例ーースキームを得る
String urlString ="http://www.abc.com";
java.net.URL url = new java.net.URL (urlString);
String scheme = url.getProtocol();
●HTTPの要求/応答メッセージ
(要求/応答)メッセージ :=
[メッセージヘッダ]
[CRLF]
[メッセージボディ]
メッセージヘッダ :=
[要求コマンド行]または[応答ステータス行][CRLF]
[ヘッダフィールド][CRLF]
・・・・・
要求コマンド行 :=
[コマンド][リソース][HTTPバージョン]
応答ステータス行 :=
[HTTPバージョン][応答コード][応答メッセージ]
ヘッダフィールド :=
[フィールド名]:[フィールド値]
HTTPのコード体系
1yz 情報のみ(informational)
2yz コマンド受託(Successful)
3yz 要求のリダイレクション(Reditection)
4yz クライアント側のエラー(client error)
5yz サーバ側のエラー(server error)
HTTPの応答コード
100 Continue ...要求が中途であり、サーバは後に続く要求を待っている
101 Switching Protocols ...
200 OK ... 要求は受理された
201 Created ... サーバー側に新規のリソースが作成された
202 Accepted ...
・・・・・
HTTPのコマンドセット(メソッド method)
OPTIONS ...指定リソースに対して用いることの出来るコマンドを問い合
わせる(POP3のCAPAコマンドに相当)
GET ...指定のリソースを取得する(FTP/POP3のRETRコマンドに相当)
HEAD ...指定のリソースに対して操作を行ったときにサーバが返してく
るはずの返答ヘッダを帰す
POST ...
PUT ...
DELETE ...
TRACE ...
CONNECT ...
例
要求
OPTION * HTTP/1.1
Request-URI: *
Host:www.abc.com
応答
HTTP/1.1 200 OK
Date:Fri,26 Apr 2002 05:17:18 GMT
Server:Apache/1.3.19(Unix)
Content-Length:0
Allow:GET,HEAD,OPTIONS,TRACE
GET,HEAD,OPTIONS,TRACEが使用可能であることが分かる
要求/応答フィールド
HTTPのヘッダはデータ(リソース)そのものではなく、
HTTPの動作を指示している。
フィールド分類 数 用途
要求ヘッダ 19 要求メッセージの中にのみ現れる
応答ヘッダ 9 応答メッセージの中にのみ現れる
エンティティヘッダ 10 リソース(エンティティ)に関する情報
汎用ヘッダ 9 要求/応答どちらにも共通
Accept ...
Accept-Charset ...
Accept-Encoding ...
Accept-Language ...
・・・・・
●ボディ
どこからどこまでがボディか
1.旧版HTTPの場合は、FTPと同様、コネクションのクローズが終端
2.Content-Lengthエンティティフィールドがある場合、
そこに示されたバイト長(10進表記)までがボディ
3.ボディが全く無い場合もある
4.Transfer-Encoding汎用フィールドに"chunked"とある場合は、数値により
ボディ長を示す。
チャンク形式(Transfer-Encoding:chunked)
HTTPでリソース(データ)を搬送するとき、おおもとのリソースを
別の形式に変換することがある。この場合Transfer-Encoding汎用フィールドに
その変換方式が示される。
gzip,compress,deflate,chunked
chunkedを除いて、いずれもデータを圧縮する方式である。
チャンク(chunked)は、中身には変更を加えずに複数のリソースを
くっつけ合わせた物。
チャンク形式メッセージボディ:=
[チャンク1サイズ][CRLF]
[チャンク1][CRLF]
[チャンク2サイズ][CRLF]
[チャン2][CRLF]
・・・
0[CRLF]
[チャンク1][CRLF]
チャンクサイズは、16進数字(10進数字ではない)
主としてエラー発生時の応答メッセージに見られる。
例ーーGETが失敗したときの、ヘッダも含む応答メッセージ
HTTP/1.1 404 Not Found
Date:Web,01 May 2002 09:24:12 GMT
Transfer-Encording:chunked
Content-Type:text/html;charset=iso-8859-1
111
< !DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
< HTML>
< TITLE>404 Not Found< /TITLE>
< /HEAD>
< H1>Not Found
The requested URL /xzas was not found on this server.< P>
< HR>
< ADDRESS>Apache/1.3.19 Server at haut-brion.bordeaux.fr. Port 80< /ADDRESS>
< /BODY>< /HTML>
←注意:ここにはチャンクの末尾の空行がある
0
←注意:ここにはボディの末尾の空行がある
チャンク形式デコーダ
import java.net.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
Socket soc; //オープンされたソケット
BufferedReader in = new BufferedReader
(new InputStreamReader(soc.getInputStream()));
while (true) {
String chunkSize = in.readLine (); //チャンクサイズ
int length = Integer.parseInt (chunkSize, 16);
if (length <= 0)
break;
char[] c = new char[length];
in.read(c);
System.out.print (new String(c));
in.readLine (); // チャンク末尾の CRLF
}
in.readLine(); // 全体最後の CRLF
チャンクサイズは内容部分のサイズであって、末尾に付加されたCRLF
の2バイトは含んでいないので、別に読み込む。
●旧版HTTP(バージョン0.9)による単一要求
HTTPクライアント HTTPサーバ
接続(hpst,80) −−−−−−>
要求 −−−−−−>
<−−−−−−URLの内容
<−−−−−−切断
●HTTP/1.1による複数要求
HTTPクライアント HTTPサーバ
接続(hpst,80) −−−−−−>
要求1 −−−−−−>
<−−−−−−要求1への応答
要求2 −−−−−−>
<−−−−−−要求2への応答
・・・・・・
<−−−−−−切断
コネクション切断要求は、Connection汎用フィールドで示す。
Connection: close
サーバ側のタイムアウト
Apache WEBサーバでは、"KeepAliverTimeout"という
設定ディレクティブで指定、デフォルトは15秒。
サーバ側で1回のセッションで行える要求の数を限る
Apache WEBサーバでは、"MaxKeepAliveRequests"という
設定ディレクティブで指定、デフォルトは100。
100回目の応答ヘッダにConnection: closeを付けてくる。
●範囲指定
リソースの部分をバイト数により限定。
Range: bytes=0-99
サーバからの応答
HTTP/1.1 206 Partial Content
Accept-Ranges:bytes
Content-Length: 100
Content-Range: bytes 0-99/1493
Accept-Rangesは、範囲指定の単位。現行はbytesのみ。
1493は、Range要求が無ければ、返されるはずの、リソース全体のサイズ。
末尾100倍との時の指定方法
Range: bytes=-100
最初の100バイトから200バイトまでと、300バイトから400バイト
Range: bytes=100-200,300-400
実際のファイルサイズから外れた範囲指定をすると、エラーが返される
HTTP/1.1 416 Requested Range Not Satisfiable
Accept-Ranges:bytes
Content-Length: 1493
Content-Range: bytes */1493
●キャッシュの制御
有効期限による制御
Expiresエンティティフィールド
例
HTTP/1.1 200 OK
Date: Wed,01 May 2002 21:50:51 GMT
Last-Modified: Wed, 01 May 2002 00:00:00 GMT
Expires: Wed, 20 June 2002 00:00:00 GMT
6月20日まではキャッシュされた内容で代用できると期待できる。
内容確認
クライアントからサーバに、指定日時以降に内容が変更されていればリソース
を返信、そうでなければ返信せずともよいと、条件付要求を行う。
"If-Modified-Since"要求ヘッダフィールド
If-Modified-Since: Wed, 01 May 2002 00:00:00 GMT
新規に変更があれば、サーバは通常通り返信してくる。
アップデートが無ければ、"304"応答が送られ、リソースは送られてこない。
GET /index.html HTTP/1.1
Host: www.abc.com
If-Modified-Since: Wed, 01 May 2002 00:00:00 GMT
HTTP/1.1 304 Not Modified
Date: Wed, 01 May 2002 22:19:40 GMT
キャッシュの許可
Cache-Control: public ←キャッシュ可
Cache-Control: private ←個人用に限りキャッシュ可
Cache-Control: no-cache ←キャッシュ不可
●HTTPプロキシ
プロキシ :http://proxy.def.com:8080/
取得URL :http://abc.com/index.html
まず、telnetでproxy.def.comの8080ポートに接続し、要求を投入する。
GET http://abc.com/index.html HTTP/1.1
Host: abc.com
GETが、パスだけでなくURL全文となる。
多段プロキシ
GET http://abc.com/index.html HTTP/1.1
Host: proxy.ghi.com:8080/
クライアント→proxy.def.com(直接のプロキシ)→
→proxy.ghi.com(次段のプロキシ)→abc.com(リソースの権限元)
●HTTPの認証機構
チャレンジー応答(challenge-response)方式:RFC 2617
HTTPクライアント HTTPサーバ
リソース要求 −−−−−−>
<−−−−−−チャレンジ(401 WWW-Authenticate)
認証応答(Authorization)−−−−−−>
<−−−−−−リソース
Basic(標準)方式ーーユーザ名:パスワード を、Based64でエンコード
(安全な方式ではない)
Digest(ダイジェスト)方式ーーMD5などのメッセージ縮約方式で。
サーバからクライアントへ
HTTP/1.1 401 Authorization Required
Transfer-Encoding : chunked
WWW-Authenticate : Basic realm="Restricted Area"
Basic : 認証方式を表す。Basic
realm= : 認証が及ぶ領域。認証領域名が"Restricted Area"
クライアントからサーバへ
Authrization: Basic Z3Vlc3Q6Z3Vlc3Q=
Based64デコードーーRFC2045
7ビット指向(ASCII指向)のインターネットメールにおいて、
8ビットデータ(非ASCII)を安全に送受するためのデータ変換機構。
8ビットを途切れなく連結させた後で、それを6ビットごとに区切る、
6ビットは10進で言えば[0,63]の64個。名前はここから来ている。
64個のコードはASCIIreadableの中から選んだた物を用いる。
たとえば、0にはAを、63には/を当てはめる。
元キャラクタ 元キャラクタ 元キャラクタ
765432107654321076543210
543210543210543210543210
コード コード コード コード
apache.orgが Base64の javaクラスを公開
http://xml.apache.org/xmlrpc/
パッケージ名称 XML-RPC1.1
クラス名 org.apache.xmlrpc.Base64
staticメソッドのBase64.ebcode()でbyte[]に変換した文字列をエンコード。
decode()で、デコード。
***Base64クラス
import org.apache.xmlrpc.Base64;
public class Base64Test{
public static void main (String args[])
throw java.io.Exception
{
byte codesd[] = Base64.encode(args[0].getBytes());
String codeString = new String(coded);
System.out.println("Encode: "+codeString+"'");
byte decoded[] = Base64.decode(coded);
String decodeString = new String(decoded);
System.out.println("Decoded: "+decodeString);
System.exit(0);
}
}
認証セッション例
URLはhttp://www.abc.com/secret/secret.htmlで、この/secret以下の
ディレクトリは、サーバ認証により保護。
------------------
GET /secret/secret.html HTTP/1.1
Host: www.abc.com
------------------
これに対して、サーバーは401を返す。
------------------
HTTP/1.1 401 Authorization Required
Transfer-Encoding : chunked
WWW-Authenticate : Basic realm="Restricted Area"
1de
・・・・・・
------------------
認証が必要と分かれば、クライアントは、Authrizationで証明書を添付して、
同じ要求をする。
以下は、guest:guestをBase64エンコードした例
------------------
GET /secret/secret.html HTTP/1.1
Host: www.abc.com
Authrization: Basic Z3Vlc3Q6Z3Vlc3Q=
------------------
プロキシの場合
プロキシ用のヘッダフィールド
Proxy-AuthenticateがWWW-Authenticateに、
Proxy-AuthrizationがAuthrizationに、対応。
401 Authorization Requiredは、407 Proxy Authenticate Required になる。
●Cookie
RFC 2965
RFC日本語訳
http://www.studyinghttp.net/rfc_ja/rfc2965_ja.html
HTTPクライアント HTTPサーバ
<−−−−−−Set-Cookie2
Cookie−−−−−−>
Set-Cookie2応答フィールドフォーマット
Set-Cookie2 応答フィールド =
Set-Cookie2: [NAME=VALUE];{[属性情報]・・・・}
例 Set-Cookie2: sum=3000;Comment="your purchase";Max-Age=3600;
Version=1;Domain=abc.com
sum=3000が NAME=VALUE ペア
Max-Age=3600 cookieの有効期限3,600秒
Version=1 必須属性情報
Domain このCookieが正当であるドメイン名
Discard クライアントが終了したら、Cookieを破棄するように指示
Set-Cookie2は2000年10月。それ以前はSet-Cookieを使用。
Set-Cookie:sum=3000
Cookie要求フィールドフォーマット
$Version,$Path,$Domain,$Port
Set-Cookie2と違い、NAME=VALUEは幾つ現れてもよい。
例 Cookie: name="Forner";Address="St-Laurent";class="5eme";
$Domain=abc.com;$Version=1
Cookie セッション例
val1 クライアントが入力する数値1(Cookieで返す)
val2 クライアントが入力する数値2(Cookieで返す)
sum サーバがSet-Cookieで返信する数値
クライアントが要求するURL http://abc.com/cgi-bin/cookie
------------------
GET /cgi-bin/cookie HTTP/1.1
Host: abc.com
------------------
サーバから次を送ってくる。足し算がなされる前なのでsumには0が入っている)
------------------
HTTP/1.1 200 OK
Set-Cookie2: sum=0;Version=1;Domain=abc.com;
Max-Age=600;Discard;
Transfer-Encoding: chunked
Content-Type: text/html;charset=ISO-2022-JP
317
・・・・
------------------
クライアントはPOSTコマンドを用いてサーバに送出
------------------
POST /cgi-bin/cookie HTTP/1.1
Host: abc.com
Cookie: val1=2; vl2=4; $Version=1; $Domain=abc.com
------------------
サーバの応答
------------------
HTTP/1.1 200 OK
Set-Cookie2: sum=6;Version=1;Domain=abc.com;
Max-Age=600;Discard;
Transfer-Encoding: chunked
Content-Type: text/html;charset=ISO-2022-JP
------------------
クライアント側でCookieを操作するには、JavaScriptを用いるとよい。
JavaScriptでは、HTMLページ(document)のCookieにはdocument.cookieと
言う変数でアクセスできる。テキストボックスに入力イベントがあれば
入力された値を参照して、document.cookieに代入すればいい。
サーバ側では、CGIならば、HTTP_COOKIE環境変数からCookieにアクセスできる。
●URLクラスを用いたHTTPクライアント
***URLConnection クラスを用いた簡易 HTTP ク ライアント
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
public class HttpClient {
public static void main (String[] args)
throws java.net.MalformedURLException,
java.io.IOException
{
if (args.length < 1) {
System.err.println ("HttpClient URL");
System.exit (1);
}
URL url = new URL (args[0]); //URLオブジェクトの生成
URLConnection conn = url.openConnection ();
//openConnection () ホストにTCP接続する
//TCPコネクションを管理するURLConnectionクラスのオブジェクトを返す
//URLConnectionは受信したストリームを自動的に解析してくれる
//行番号を指定することで getHeaderField()メソッドで取得
// コネクションの設定
conn.setDoInput (true); // for GET
conn.setDoOutput (false); // for POST
conn.setUseCaches (false); // disable cache
conn.setRequestProperty ("Connection", "close");//最後の要求
conn.connect (); //接続
//ヘッダの出力
String value = null;
System.out.println ("Headers: ");
for (int i=0; (value=conn.getHeaderField(i))!=null; i++) {
String key = conn.getHeaderFieldKey (i);
System.out.println (" " + key + ": " + value);
}
System.out.println ("--------------------------------------------");
//ボディの出力
int len = conn.getContentLength (); // get the length
InputStream is = conn.getInputStream ();
byte b[] = new byte[256];
int s = 0;
while (s < len) {
int l = is.read (b);
s += l;
System.out.print (new String (b, 0, l));
}
System.exit (0);
}
} // end of class
URLConnectionクラスは、getOutStream()もあるので、サーバに対し、
POP/PUT操作も行うことが出来る。
このとき、conn.setDoOutput (true);で明示的に、出力を可能に指示する。
TELNET
サービス名 :TELNET
ポート番号 :23
トランスポート:TCP/UDP
RFC :854(May 1983)
サービス内容 :ネットワークの仮想端末の提供
●ネットワーク仮想端末(NVT)
NVT:Network Virtual Terminal
リモートとローカルの差を吸収のための、一種の「共通言語」。
┏━━━━┓ ┏━━━━━━┓ ┏━━━┓ ┏━━━━━━┓
┃リモート┃ ┃TENNET ┃ ┃TENNET┃ ┃ローカル ┃
┃端末 ┃←→┃クライアント┃←→┃サーバ┃←→┃サブシステム┃
┗━━━━┛ ┗━━━━━━┛ ┗━━━┛ ┗━━━━━━┛
リモート表現 NTV表現 ローカル表現
NVTのデータ表現は、ASCIIコードのサブセット。ASCIIコードは7ビットなので、
0から127までの値(コード)にキャラクタが割り振られる。
1字分下げる作業は、CRLFが用いられる。
●TELNET制御コマンド
コマンド コード 意味
IAC 255 Interrupt As Command
GA 249 Go Ahead
EL 248 Erase LIne
EC 247 Erace Character
AYT 246 Are Tou There
AO 245 About Output
IP 244 Interrupt Process
BRK 243 Break
DM 242 Data Mark(Sync)
NOP 241 NoOperation
すべてのコマンドの前には、必ずIAC(Interrupt As Command)が置かれる。
制御コマンドは、IACを入れて、2バイト長になる。
TELNETコマンドは必ずIACから始まるので、255だけエスケープすれば、
たはデータとして正しく解釈される。
例えば、255 244を送るときは、255 255 244と送る。
1文字消去
IAC EC → 255 247
リモート端末からプロセスを終了
IAC IP → 255 244
通信相手が正常に接続されているか確認のコマンド
IAC AYT → 255 246
コマンドを受けた側は、"Yes,I'm Up"等を返す。
●TELNETオプション
2段階
1.オプション打ち合わせーー互いに使用可能オプションの打ち合わせ
2.サブネゴシエーション(sub-negotiation)ーーパラメータを送り合う
オプション打ち合わせ
IAC オプションコマンド オプション番号
オプションコマンド
コマンド コード 意味
DON'T 254 オプションを使用しない
DO 253 オプションを使用する
WON'T 252 オプションを使用しない
WILL 251 オプションを使用する
SB 250 サブネゴシエーション開始(Begin)
SE 240 サブネゴシエーション終了(End)
要求と応答
コマンド 応答
IAC WILL X IAC DO X (了解通知)
IAC DON'T X(拒否通知)
IAC DO X IAC WILL X (了解通知)
IAC WON'T X(拒否通知)
オプション
オプション名 コード RFC 用途
ECHO 1 857 受け取ったキャラクタのエコーバックのオン/オフ
SUPPRESS-GO-AHEAD 3 858 GAコマンドを抑止する
STATUS 5 859 TELNETオプションの状態を通知
TERMINAL-TYPE 24 1091 端末名の通知
NAWS 31 1073 ウィンド(端末)のコラム数・行数の通知
TERMINAL-SPEED 32 1079 端末速度の通知
TOGGLE-FLOW-CONROL 33 1372 流量制御をオン/オフする
LINMODE 34 1184 行単位でのやり取りをする
X-DISPLAY-LOCATION 35 1096 Xディスプレイ名の通知
NEW-ENVIRON 39 1572 環境変数の通知
RANDOMLY-LOSE 256 748 システムクラッシュを許可する
SUBLIMINAL-MESSAGE 257 1097 サブリミナルメッセージを表示する
例ーー端末の通信速度の送受を可能にする
要求 : IAC WILL TERMINAL-SPEED → 255 251 32
応答(了解) : IAC DO TERMINAL-SPEED → 255 253 32
サブネゴシエーション
例えば、ウィンドを使うときの、縦横のサイズの情報交換。
開始コマンド(SB=Subnegotiation Begin,250)と
終了コマンド(SE=Subnegotiation End,240)に情報を組み込む
IAC SB 情報 IAC SE
例ーークライアントがサーバにウィンドサイズを通知
NAWS=Nogotiation About Window Size,31
NAWSパラメータは、最初の2バイトがコラム数、後ろの2バイトが行数
IAC SB NAWS 80x56 IAC SE → 255 250 31 0 80 0 56 255 240
DNS
サービス名 :domain
ポート番号 :53
トランスポート:UDP/TCP
RFC :1035(November 1987)/1034(コンセプト)
サービス内容 :ドメイン名をドット表記のIPアドレスに変換
DNS:Domain Name Server
正当はバイナリプロトコル
abc.com → 123.456.567.678
リソースレコード:問い合わせの対象となるデータ
リゾルバ(resolver)と言うOSの内部機構が提供
●DNSの仕組み
特定ホストをまとめた領域をゾーン(zone)。
管理しているサーバは権限元(authority)。
参考資料
Java インターネットプログラミング
豊沢聡著
カットシステム発行
2002年7月20日
ホームへ