(강의 요약본입니다.)
클라이언트의 대화상대. 클라이언트가 Socket을 생성하려면 연결할 인터넷 상의 호스트가 있어야 한다.
-> 서버
클라이언트의 연결 요청을 받아들이기 위한 메소드
클라이언트의 연결 요청이 들어 올 때까지 블록된다.
클라이언트의 연결 요청이 들어오면 Socket 객체를 리턴한다.
<참고>
ServerSocket으로부터 발생하는 예외는 서버를 종료시켜야 하지만, Socket이 발생시킨 예외는 활성화된 Socket만 종료시켜도 괜찮다.
로컬 호스트와 원격 호스트의 연결이 해지된다.
서비스를 끝내면 Socket 객체를 닫아야 한다. (보통 finally 구문에서 연결을 종료한다.)
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);
}
}
}
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시에 갈 예정이야
Server는 하나의 요청만을 처리하고 있을 수 없다!
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;
}
}
}
}
}
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;
}
}
}
말 그대로 클라이언트가 보낸 메시지 그대로 응답한다.
// . 문장을 입력하면 종료
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
// . 문장을 입력하면 종료
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
단, 한 클라이언트에 대한 서비스가 종료되어야 다른 클라이언트에게 서비스할 수 있다
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
}
일반 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) {}
}
}
}
}
}
저번 소켓의 사용이 끝나면 ServerSocket 객체의 close() 메소드를 호출하여 서버 소켓을 닫아야 한다.
점유하고 있던 port를 반환하여 다른 서버가 바인드 할 수 있게 함
서버 소켓의 accept()를 통해 열린 모든 Socket도 삭제한다
프로그램이 끝나면 자동으로 닫힌다
서버 소켓이 연결된 적이 있는가를 정확히 체크하려면 다음과 같은 식으로 처리해야 한다.
public static boolean isOpen()(ServerSocket ss){
return ss.isBound() && ! ss.isClosed();
}