자바

자바 Socket 데이터 통신과 스레드 병렬 처리

알통몬_ 2017. 4. 11. 09:48
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 

2017/04/11 - [자바] - 자바 TCP 네트워킹, ServerSocket과 Socket / ServerSocet의 생성 및 연결 수락


이전 포스팅과 이어집니다~


클라이어트가 연결 요청을 하고 서버가 연결 수락을 했다면?

=> 양쪽의 Socket으로부터 각각 입력 스트림과 출력 스트림을 얻을 수 있습니다.


InputStream inputStream = socket.getInputStream();

OutputStream outputStream = socket.getOutputStream();


데이터를 보내려면 보낼 데이터를 byte[] 배열로 생성하고 이 배열을 매개 값으로 해서

OutputStream 의 write()로 호출하면 됩니다.


상대방이 보낸 데이터를 받으려면 받은 데이터를 저장할 byte[] 배열을 하나 생성하고,

이 배열을 매개값으로 InputStream 의 read()를 호출하면 됩니다.


예제)

서버

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.net.Socket;


public class ServerExample {

public static void main(String[] args) {

ServerSocket serverSocket = null;

try {

serverSocket = new ServerSocket();

serverSocket.bind(new InetSocketAddress("localhost", 5001));

while(true) {

System.out.println( "[[연결 기다립니다]]");

Socket socket = serverSocket.accept();

InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();

System.out.println("[[연결 수락합니다]] " + isa.getHostName());

byte[] bytes = null;

String message = null;

InputStream is = socket.getInputStream();

bytes = new byte[100];

int readByteCount = is.read(bytes);

message = new String(bytes, 0, readByteCount, "UTF-8");

System.out.println("[[데이터 받기 성공]]: " + message);

OutputStream os = socket.getOutputStream();

message = "Hello Client";

bytes = message.getBytes("UTF-8");

os.write(bytes);

os.flush();

System.out.println( "[[데이터 보내기 성공]]");

is.close();

os.close();

socket.close();

}

} catch(Exception e) {}

if(!serverSocket.isClosed()) {

try {

serverSocket.close();

} catch (IOException e1) {}

}

}

 

}


클라이언트

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.InetSocketAddress;

import java.net.Socket;


public class ClientExample {

public static void main(String[] args) {

Socket socket = null;

try {

socket = new Socket();

System.out.println( "[[연결 요청합니다]]");

socket.connect(new InetSocketAddress("localhost", 5001));

System.out.println( "[[연결 성공입니다]]");

byte[] bytes = null;

String message = null;

OutputStream os = socket.getOutputStream();

message = "Hello Server";

bytes = message.getBytes("UTF-8");

os.write(bytes);

os.flush();

System.out.println( "[[데이터 보내기 성공]]");

InputStream is = socket.getInputStream();

bytes = new byte[100];

int readByteCount = is.read(bytes);

message = new String(bytes, 0, readByteCount, "UTF-8");

System.out.println("[[데이터 받기 성공]]: " + message);

os.close();

is.close();

} catch(Exception e) {}

if(!socket.isClosed()) {

try {

socket.close();

} catch (IOException e1) {}

}

}

 

}



데이터를 받기 위해서 InputStream 의 read()를 호출하면 상대방이 데이터를 보내기 저까지 블로킹됩니다.



스레드 병렬 처리란 : 

연결 수락을 위해 ServerSocket의 accept()를 실행하거나 서버 연결 요청을 위해 Socket 생성자 또는

connect()를 실행할 경우 해당 작업 완료 시까지 블로킹됩니다. 데이터 통신도 마찬가지입니다.

만약 서버를 실행시키는 main 스레드가 직접 입출력까지 담당하게 된다면

입출력이 완료될 때까지 다른 작업을 할 수 없는 상태가 됩니다.

서버 프로그램은 지속적으로 클라이언트의 연결 수락이 가능해야 하는데

입출력에서 블로킹이 된다면 이런 작업을 할 수 없게 됩니다.

그리고 클라이언트 1과 입출력하는 동안은 클라이언트 2와 아무것도 할 수 없게 됩니다.

때문에 accept(), connect(), read(), write()는 별도의 작업 스레드를 생성해 병렬적으로 처리하는 것이 좋습니다.


클라이언트가 연결을 요청하면 서버의 스레드풀에서 연결을 수락하고 Socket을 생성하고,

클라이언트가 작업 처리 요청을 하면 서버의 스레드풀에서 요청을 처리하고 응답을 클라이언트에 보냅니다.

스레드풀은 스레드 수를 제한하여 사용할 수 있으므로 갑작스런 클라이언트의 폭증은 작업 큐의 작업량만

증가시키고 스래드 수는 변함이 없으므로 서버의 성능이 완만하게 저하됩니다.

하지만 대기하는 작업량이 많다면 개별 클라이언트에서는 응답을 늦게 받을 수도 있습니다.


이상입니다.

반응형