Java Network Programming_Server socket


(コースの概要)

Sockets for Server


サーバとは


お客様の対話対象.クライアントがsocketを生成するには、接続するインターネット上のホストが必要です.
->サーバ

Java Socketクラスはサーバを作成できないため、Server Socketクラスを使用します。


オブジェクトのライフサイクル


1.ServerSocket()ジェネレータを使用して、特定のポートに対してServerSocketオブジェクトを作成します。


2.サーバソケットオブジェクトのaccept()メソッドを呼び出してクライアントの接続要求を待つ accept()メソッドは、クライアントがサーバに接続しようとするまでブロックされます。 クライアントが要求を発行すると、Socketオブジェクトが返されます。


3.クライアントと通信するために、SocketオブジェクトのgetInputStream()とgetOutputStream()を使用してIOストリームオブジェクトを生成します。


4.接続が完了する前に、クライアントとサーバはアプリケーションプロトコルに従って対話する。 インタラクションは通常Threadとして処理される。


5.サーバー、クライアント、または両方の接続を閉じる


6.Step 2に戻り、次の接続要求を待つ


public Socket accept() throws IOException


クライアント接続要求を受け入れる方法
クライアントの接続要求がブロックされるまで.
クライアントが接続要求を発行すると、Socketオブジェクトが返されます.
<コメント>
ServerSocketからの例外はサーバーをシャットダウンする必要がありますが、Socketの例外はアクティブなSocketをシャットダウンするだけです.

socketオブジェクトのclose()メソッド


ローカルホストとリモートホストの接続が切断されます.
サービスが終了したら、Socketオブジェクトを閉じる必要があります.(通常はfinally構文で接続を終了します.)

例)DayTime Server

import java.net.*;
import java.io.*;
import java.util.Date;
 
public class DaytimeServer {
 
  public final static int PORT = 13;

  public static void main(String[] args) {  
   try (ServerSocket server = new ServerSocket(PORT)) {
     while (true) {  
       try (Socket connection = server.accept()) {
         Writer out = new OutputStreamWriter(connection.getOutputStream());
         Date now = new Date();
         out.write(now.toString() +"\r\n");
         out.flush();      
         connection.close();
       } catch (IOException ex) {}       
     }
   } catch (IOException ex) {
     System.err.println(ex);
   } 
  }
} 

例2)友人サーバとクライアント


サーバコード:

import java.io.*;
import java.net.*;
public class FriendServer {
	public static void main(String[] args) {
		Socket socket = null; 
		InputStreamReader isr = null;
		OutputStreamWriter osw = null;
		StringBuilder message = null;
		
		try (ServerSocket serverSocket = new ServerSocket(7009);) {
			System.out.println("-- 서버가 접속을 기다린다 -- ");
			socket = serverSocket.accept(); 
			System.out.println("-- 클라이언트와 접속 성공 -- ");
			
			// Message from Client to Server
			isr = new InputStreamReader(
					socket.getInputStream(), "MS949"); 
			message = new StringBuilder();
			for (int c = isr.read(); c != -1; c = isr.read()) {
				message.append((char) c);
			} 
			socket.shutdownInput();
			System.out.println("클라이언트 친구로부터 받은 메세지 : " + message);
			// Message from Server to Client
			osw = new OutputStreamWriter(
					socket.getOutputStream() ); 
			osw.write("나 9시에 갈 예정이야" + "\r\n");
			System.out.println("클라이언트 친구에게 메시지를 보내다");
				
			osw.flush(); 
			socket.shutdownOutput();
			osw.close(); 
			isr.close(); 
			socket.close(); 
		} catch (Exception e) { 
			e.printStackTrace();
		}
	}
}

クライアントコード:

import java.io.*;
import java.net.*;
public class FriendClient {

	public static void main(String[] args) {
		
		InputStreamReader isr = null;
		OutputStreamWriter osw = null;
		StringBuilder reply = null;


		try (Socket socket = new Socket("localhost", 7009)) {

			socket.setSoTimeout(2000);

			// Message from Client to Server
			osw = new OutputStreamWriter(
					socket.getOutputStream() ); 
			osw.write("내일 학교에 몇 시에 올래" + "\r\n");
			System.out.println("서버 친구에게 메시지를 보내다");
				
			osw.flush(); 
			socket.shutdownOutput();

			// Message from Server to Client
			isr = new InputStreamReader(
					socket.getInputStream(), "MS949"); 
			reply = new StringBuilder();
			for (int c = isr.read(); c != -1; c = isr.read()) {
				reply.append((char) c);
			} 
			System.out.println("서버 친구로부터 받은 메세지 : " + reply);


			osw.close(); 
			isr.close(); 
		} catch (Exception e) { 

			e.printStackTrace();

		}

	}

}

サーバを開き、クライアントから問い合わせます.明日何時に学校に来ますか.
サーバが回答します.9時に行くつもりです.

Multithreaded Server


サーバは1つのリクエストのみを処理できません.

import java.net.*;
import java.io.*;
import java.util.Date;
 
public class MultithreadedDaytimeServer {
 
  public final static int PORT = 13;

  public static void main(String[] args) {   
   try (ServerSocket server = new ServerSocket(PORT)) {
     while (true) {  
       try {
         Socket connection = server.accept();
         Thread task = new DaytimeThread(connection);
         task.start();
       } catch (IOException ex) {}
     } 
    } catch (IOException ex) {
      System.err.println("Couldn't start server");
    }
  }
private static class DaytimeThread extends Thread {
    
    private Socket connection;
    
    DaytimeThread(Socket connection) {
      this.connection = connection;
    }
    
    public void run() {
      try {
        Writer out = new OutputStreamWriter(connection.getOutputStream());
        Date now = new Date();
        out.write(now.toString() +"\r\n");
        out.flush(); 
      } catch (IOException ex) {
        System.err.println(ex);
      } finally {
        try {
          connection.close();
        } catch (IOException e) {
          // ignore;
        }
      }
    }
  }
}

socket値を返します。


#+Threadpoolを使用したサーバ
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
 
public class PooledDaytimeServer {
 
  public final static int PORT = 13;

  public static void main(String[] args) {  
   
   ExecutorService pool = Executors.newFixedThreadPool(50);

   try (ServerSocket server = new ServerSocket(PORT)) {
     while (true) {  
       try {
         Socket connection = server.accept();
         Callable<Void> task = new DaytimeTask(connection);
         pool.submit(task);
       } catch (IOException ex) {}
     } 
    } catch (IOException ex) {
      System.err.println("Couldn't start server");
    }
  }
private static class DaytimeTask implements Callable<Void> {
    
    private Socket connection;
    
    DaytimeTask(Socket connection) {
      this.connection = connection;
    }
    
   public void call() {
      try {
        Writer out = new OutputStreamWriter(connection.getOutputStream());
        Date now = new Date();
        out.write(now.toString() +"\r\n");
        out.flush(); 
      } catch (IOException ex) {
        System.err.println(ex);
      } finally {
        try {
          connection.close();
        } catch (IOException e) {
          // ignore;
        }
      }
      return null;
    }
  }
}

Echo Server


前述したように、クライアントから送信されたメッセージに従って応答する.

クライアントコード:

// . 문장을 입력하면 종료
import java.net.*;
import java.io.*;
public class EchoClient {
  public static void main(String[] args) {
    String hostname = "localhost";
    if (args.length > 0) {
      hostname = args[0];
    }
    PrintWriter networkOut = null;
    BufferedReader networkIn = null;
    try {
      Socket theSocket = new Socket(hostname, 7);
      networkIn = new BufferedReader(new InputStreamReader(theSocket.getInputStream()));
      BufferedReader userIn = new BufferedReader(new InputStreamReader(System.in));
      networkOut = new PrintWriter(theSocket.getOutputStream());
      System.out.println("Connected to echo server");

      while (true) {
        String theLine = userIn.readLine();
        if (theLine.equals(".")) break;
        networkOut.println(theLine);
        networkOut.flush();
        System.out.println(networkIn.readLine());
      }
    }  // end try
    catch (IOException ex) {
      System.err.println(ex);
    }
    finally {
      try {
        if (networkIn != null) networkIn.close(); 
        if (networkOut != null) networkOut.close(); 
      }
      catch (IOException ex) {}
    }
  }  // end main
}  // end EchoClient

Networkin:サーバからデータを受信するストリームネットワーク出力:サーバからデータを送信するストリームuserin:ユーザからサーバにメッセージを受信するストリームtryブロックとfinallyブロックは別々のブロックである.2つのブロックで変数を共有するには、ブロックから除外して宣言する必要があります。この番組の次の部分はここに相当します。BufferedReader networkIn=null;PrintWriter networkOut=null;Ecoサーバはクライアントから受信したデータをクライアントに直接送信する.したがって、通常の場合、PrintWriterやReadLine()など、正常な処理が期待できない機能を使用してもよい。Networkinnの現在のDefault Encodeはオペレーティングシステムの基本的な符号化方法である。


Echo Server


クライアントがメッセージを送信するまで、サーバはクライアントにメッセージを送信しません。クライアントとサーバの間で送信および受信されるメッセージには、改行(改行)を含める必要があります。


ソケットはInputStream/OutputStreamを返しますが、InputStreamはInputStreamReader、BufferedReader、OutputStreamはPrintWriterに変換する必要があります。


送信側はPrintWriterのprintln()に送信され、受信側はBufferedReaderのreadlin()に読み出される。


行を開く


メッセージを受信するサーバまたはエンコードされたメッセージを受信するクライアントはreadline()に読み込まれるため、ローの末尾を識別する必要があります。送信者がメッセージの末尾に開文字を含める場合はprintln(msg)またはwrite(msg+"rn")で出力する必要があります。


readLine()メソッドは、書き換え文字を削除してデータを取得するので、そのデータを返しても書き換え文字を再追加する必要があります。


ネットワークプログラムからマルチバイトデータを出力するとflush()を呼び出す必要があります。


サーバ・オペレータが接続しているリモート・クライアントが誰であるかを通知するには、次のコードが必要です。InetAddress inetaddr = sock.getInetAddress(); System.out.println(「クライアント:」+inetaddr.getHostAddress();


クライアントが接続を閉じると、BufferedReaderのreadline()はnullを返します。


クライアントが異常に終了した場合、サーバのクライアント相対コードも終了する必要があります。


クライアント"."終了セクションをexit文に変更すると、クライアントは終了できますが、サーバは終了しません。


例)1つのクライアントのみにサービスし、実行中のEcoサーバを終了する

// . 문장을 입력하면 종료
import java.net.*;
import java.io.*;
public class EchoClient {
  public static void main(String[] args) {
    String hostname = "localhost";
    if (args.length > 0) {
      hostname = args[0];
    }
    PrintWriter networkOut = null;
    BufferedReader networkIn = null;
    try {
      Socket theSocket = new Socket(hostname, 7);
      networkIn = new BufferedReader(new InputStreamReader(theSocket.getInputStream()));
      BufferedReader userIn = new BufferedReader(new InputStreamReader(System.in));
      networkOut = new PrintWriter(theSocket.getOutputStream());
      System.out.println("Connected to echo server");

      while (true) {
        String theLine = userIn.readLine();
        if (theLine.equals(".")) break;
        networkOut.println(theLine);
        networkOut.flush();
        System.out.println(networkIn.readLine());
      }
    }  // end try
    catch (IOException ex) {
      System.err.println(ex);
    }
    finally {
      try {
        if (networkIn != null) networkIn.close(); 
        if (networkOut != null) networkOut.close(); 
      }
      catch (IOException ex) {}
    }
  }  // end main
}  // end EchoClient

複数のクライアントを順番に処理するサーバ


ただし、別のクライアントにサービスを提供するには、1つのクライアントのサービスを終了する必要があります.
import java.net.*;
import java.io.*;

public class EchoServer2 {

	public static void main(String[] args) {
		try {
			ServerSocket server = new ServerSocket(7);
			while (true) {
				System.out.println("에코 서버가 접속을 기다립니다.");
				Socket sock = server.accept();
				System.out.println(sock.getInetAddress() + "- port" + sock.getPort() + " 이 접속하였습니다.");

				BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
				PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));

				String line = null;
				line = br.readLine();
				while (line != null) {
					System.out.println("클라이언트로 부터 전송 받은 문자열 : " + line);
					pw.println(line);
					pw.flush();
					line = br.readLine();
				}
				System.out.println("입출력 완료, 클라이언트와의 스트림 및 소켓 닫기");
				pw.close();
				br.close();
				try {
					if(sock != null)
							sock.close();
				}
				catch(IOException e) {
				}
			}
		} catch (Exception e) {
			System.out.println(e);
		}
	} // main
}

マルチスレッドに変更するには?

新規入出力(NIO)を使用したEcoサーバ


普通のIOとは少し違います.
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.io.IOException;

public class EchoServer9_5 {
    
  public static int DEFAULT_PORT = 7;
  
  public static void main(String[] args) {
  
    int port;
    try {
      port = Integer.parseInt(args[0]);
    } catch (RuntimeException ex) {
      port = DEFAULT_PORT;   
    }
    System.out.println("Listening for connections on port " + port);

    ServerSocketChannel serverChannel;
    Selector selector;
    try {
      serverChannel = ServerSocketChannel.open();
      ServerSocket ss = serverChannel.socket();
      InetSocketAddress address = new InetSocketAddress(port);
      ss.bind(address);
      serverChannel.configureBlocking(false);
      selector = Selector.open();
      serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    } catch (IOException ex) {
      ex.printStackTrace();
      return;   
    }
    
    while (true) { 
      try {
        selector.select();
      } catch (IOException ex) {
        ex.printStackTrace();
        break;
      }
        
      Set<SelectionKey> readyKeys = selector.selectedKeys();
      Iterator<SelectionKey> iterator = readyKeys.iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        iterator.remove();
        try {
          if (key.isAcceptable()) {
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel client = server.accept();
            System.out.println("Accepted connection from " + client);
            client.configureBlocking(false);
            SelectionKey clientKey = client.register(
                selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
            ByteBuffer buffer = ByteBuffer.allocate(100);
            clientKey.attach(buffer);
          }
          if (key.isReadable()) {
            SocketChannel client = (SocketChannel) key.channel();
            ByteBuffer output = (ByteBuffer) key.attachment();
            client.read(output);
          }
          if (key.isWritable()) {
            SocketChannel client = (SocketChannel) key.channel();
            ByteBuffer output = (ByteBuffer) key.attachment();
            output.flip();
            client.write(output);
            output.compact();
          }
        } catch (IOException ex) {
          key.cancel();
          try {
            key.channel().close();
          } catch (IOException cex) {}
        }
      }
    }
  }
}

public void close() throws IOException


最後のソケットの使用が完了すると、サーバソケットを閉じるには、サーバソケットオブジェクトのclose()メソッドを呼び出す必要があります.
他のサーバがバインドできるように、占有しているポートを返します.
サーバソケットのaccept()で開いているすべてのソケットも削除

サーバースロットをオフにする必要がありますか?


プログラム終了後自動クローズ

関連メソッド


public boolean isClosed()


public boolean isBound()


isBound()は、サーバソケットが閉じていてもtrueを返します。


サーバーソケットが接続されているかどうかを確認するには、次の手順に従います.
public static boolean isOpen()(ServerSocket ss){
	return ss.isBound() && ! ss.isClosed();
}