">

CLOSE_WAIT



  그림에서 Client가 연결을 종료하기 위해 FIN을 Server로 보낸다. 그리고 FIN에 대한 ACK가 있기 전까지 Server은 CLOSE_WAIT 상태가 된다. 

CLOSE_WAIT 상태가 사라지지 않고 계속 쌓이게 되면 서버에서는 더 이상 서비스 처리를 못하는 상태가 된다. 

CLOSE_WAIT 상태를 해결하는 방법을 시스템의 설정을 바꾸는 방법에 대한 설명이 많았는데 이렇게는 근본적인 원인을 해결할 수 없다. 


  여러 방법들이 있겠지만, 개인적으로 겪은 부분만 정리해 본다. 문제의 원인은 'DefaultHttpClient' 때문이었다. 

사용하는 부분에서 잘못된 것은 없었지만 CLOSE_WAIT 현상이 발생했다. 

그리고 같은 소스로 여러 서버에서 테스트를 진행했을 때 이 현상이 발생하는 곳도 있고, 

어느정도 CLOSE_WAIT가 쌓이다가 없어지는 서버도 있었다. 그래서 변경한 부분은 'DefalutHttpClient'를 '

CloseableHttpClient'로 변경했다. 거짓말처럼 말끔하게 CLOSE_WAIT가 사라졌다. 

DefaultHttpClient를 꼭 사용해야 한다면 아래 참고 자료 중 2번에 해당하는 링크를 참조하면 된다. 

그리고 읽어보면 좋을 것 같아서 추가한 것도 있다. 아래 참고 자료 3번에 해당하는 링크를 참조하면 된다. 

  


문제: CLOSE_WAIT가 쌓이는 현상 발생


해결: DefaultHttpClient -> CloseableHttpClient



참고 자료

[1] http://docs.likejazz.com/close-wait/

[2] http://pchun.tistory.com/60

[3] http://d2.naver.com/helloworld/377316

'프로그래밍 > JAVA' 카테고리의 다른 글

RSA 암호화 및 복호화  (0) 2016.12.26
자바로 크롤링 구현  (0) 2016.09.13
문자열을 나누거나 합치거나  (0) 2016.09.13

개발을 하면서 암호화에 대해 깊이 있게 생각하지 않고 필요한 스펙 대로만 구현 했었는데, 이번에 시간이 좀 있어 블로그에 정리를 해봅니다. 

이 글은 Java 기준으로 작성하였습니다. 


여러 글을 보면서 검색을 하다 보니 타 업체와 협의 할 때 적어도 아래와 같은 스펙을 서로 교환하는게 좋다고 합니다. (http://redutan.github.io/2015/11/20/about-crypto)



암호화 스펙

    • 알고리즘: RSA-256
    • 암호화 키: 2017201820192020wow2021202220232024202520262027202820292030
    • 인코딩: UTF-8


public class RSAEncrypto {
	private static final String algorithm = "RSA/None/NoPadding";
	private static final String provider = "BC";

	public static void main(String[] args) {
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

		String plainText = "2017201820192020wow2021202220232024202520262027202820292030";

		try {
			Cipher serverCipher = Cipher.getInstance(algorithm, provider);
			SecureRandom random = new SecureRandom();
			KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
			generator.initialize(512, random);
			
			KeyPair pair = generator.generateKeyPair();
			Key publicKey = pair.getPublic();  
			Key privateKey = pair.getPrivate();

			// 개인키로 암호화
			serverCipher.init(Cipher.ENCRYPT_MODE, privateKey);
			byte[] cipherText = serverCipher.doFinal(plainText.getBytes());
			System.out.println("cipher: ("+ cipherText.length +")"+ new String(cipherText));

			// 공개키로 복호화
			serverCipher.init(Cipher.DECRYPT_MODE, publicKey);
			byte[] plainTextB = serverCipher.doFinal(cipherText);

			System.out.println("plain: " + new String(plainTextB));
			
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		}
	}
}


이렇게 하면 암호화 하고, 복호화 되는 것을 확인할 수 있습니다. 하지만 실무에서는 이렇게 사용하는 경우는 거의 없습니다. 그래서 추가로 필요한 부분이 있습니다. 바로 Base64로 인코딩을 해야 되는 것입니다. 이상한 문자로 나온 암호문을 통신 할 때나, DB에 저장해서 사용할 수 없기에 Base64로 인코딩 합니다. 그리고 복호화 할 때는 다시 디코딩을 해서 복호화를 진행합니다. 


참고로 Base64는 8비트 바이너리 데이터를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 스트링으로 바꾸는 인코딩 방식을 말합니다. 아래 그림을 보면 Base64로 인코딩 된 것은 우리가 알아볼 수 있는 문자로 출력 됩니다. 




인코딩을 사용하여 적용한 소스는 다음과 같습니다. 

public class RSAEncrypto {
	private static final String algorithm = "RSA/None/NoPadding";
	private static final String provider = "BC";

	public static void main(String[] args) {
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

		String plainText = "2017201820192020wow2021202220232024202520262027202820292030";

		try {
			Cipher serverCipher = Cipher.getInstance(algorithm, provider);
			SecureRandom random = new SecureRandom();
			KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
			generator.initialize(512, random);
			
			KeyPair pair = generator.generateKeyPair();
			Key publicKey = pair.getPublic();  
			Key privateKey = pair.getPrivate();

			// 개인키를 전달하여 암호화
			serverCipher.init(Cipher.ENCRYPT_MODE, privateKey);
			byte[] cipherText = serverCipher.doFinal(plainText.getBytes("UTF-8"));
			System.out.println("cipher: ("+ cipherText.length +")"+ new String(cipherText));
			
			// Base64로 인코딩
			byte[] encodedBase64 = Base64.encode(cipherText);
			System.out.println("Base64Encoded: " + new String(encodedBase64));
			
			// Base64로 디코딩
			byte[] decodedBase64 = Base64.decode(encodedBase64);
			System.out.println("Base64Decoded: " + new String(decodedBase64));
			
			// 공개키를 가지고있는쪽에서 복호화
			serverCipher.init(Cipher.DECRYPT_MODE, publicKey);
			byte[] plainTextB = serverCipher.doFinal(decodedBase64);
			
			System.out.println("plain: " + new String((plainTextB), "UTF-8"));
			
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


패딩을 사용 하는 이유에 대해서는 다음 링크를 참조하시면 됩니다. 
여러 이유에 대한 설명이 있으니 꼭 읽어보세요. 
https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Padding_schemes

마지막으로 RSA 키를 저장하고 복구 하는 것에 대한 설명은 링크로 대체 하겠습니다. 


도움 되셨다면 아래 '공감' 클릭 한번 해주세요~ 감사합니다. 


'프로그래밍 > JAVA' 카테고리의 다른 글

CLOSE_WAIT 해결 방법  (1) 2017.01.13
자바로 크롤링 구현  (0) 2016.09.13
문자열을 나누거나 합치거나  (0) 2016.09.13
Crawler.zip



  구글 플레이스토어에 있는 앱 이름, 아이콘, 패키지 이름이 필요해서 크롤링해서 사용하려고 만들어 보았습니다. 혹시 필요하신 분은 이거 받으셔서 좀 보강한 뒤에 사용하시면 될 것 같아요. 소스는 압축해서 파일로 올려 놨습니다.  


  우선 URL 클래스와 openStream을 사용해서 지정한 주소의 소스코드를 받아오도록 합니다. 그리고 필요한 부분을 추출하기 위해서 자를 수 있는 부분을 확인 한 다음 문자열을 잘라내면 됩니다. 문자열을 자르고 합치는게 일이지 웹을 긁어오는 건 일이 아니라 생각해요. 문자열을 잘 다루지 못하는 분이면 아래 문자열 자르고 합치는 부분에 대한 설명을 보시면 도움이 될거라 생각합니다.

  

  반복되는 부분이 끝 날때 까지 while이 돌고, while문 안에서 필요한 부분을 추출합니다. 그리고 추출한 다음 다음 문장에서 필요한 것을 추출하기 위해 추출한 부분은 다시 찾지 않도록 문자열의 일부분을 잘라내고 잘라진 문자열에서 패턴으로 필요한 부분을 찾습니다. 


  소스를 돌려보는 편이 빠를거라 생각되요~ 주석도 잘 달아놨으니까 한번 보세요~


2016/09/13 - [프로그래밍/JAVA] - 문자열을 나누거나 합치거나


'프로그래밍 > JAVA' 카테고리의 다른 글

CLOSE_WAIT 해결 방법  (1) 2017.01.13
RSA 암호화 및 복호화  (0) 2016.12.26
문자열을 나누거나 합치거나  (0) 2016.09.13

  문자열로 무엇을 한다고 하면 자르고, 합치고 하는 것이 아닐까요? 웹 크롤링을 하면서 많이 사용되는 부분을 통해 문자열을 자르고 합치는 연습을 해봅시다. 


  우선 문자열을 자르기 위해 어떻게 해야 할까요? 


1. substring(int)


  String str = "good morning"  라는 문자열이 있을 때, substring을 사용하는 것을 보도록 할게요. 

  String sub = str.substring(5) 의 결과는 'moring' 가 됩니다. 




2. substring(int, int)


  동일하게 String str= "good morning"  라는 문자열이 있을 때 어떻게 되는지 보도록 할게요. 

  String sub = str.substring(3, 7) 의 결과는 'd mo' 가 됩니다. 4번째 문자 부터 7번째 문자까지 잘립니다. 



3. indexof 를 사용하여 문자열의 위치 찾기


  동일하게 String str="good morning" 라는 문자열이 있을 때 어떻게 되는지 보도록 할게요. 

  String indexof = str.indexOf("d") 의 결과는 '3'이 나옵니다. 



  substring와 indexOf를 사용하면 크롤링 시 원하는 부분을 잘라 낸 뒤 사용할 수 있습니다. 문자열을 합치는 방법은 간단하게 '+'를 사용하는 분들이 많을거라고 생각해요. 하지만 좋지 않은 방법이라고 말씀드리고 싶습니다. 문자열을 합칠 때는 StringBuilder 나 StringBuffer Class를 사용하여 합치는 것이 좋아요. 


StringBuffer strBuf = StringBuffer();

strBuf.append("I");

strBuf.append(" love");

strBuf.append(" you");


이런 형식으로 사용할 수 있습니다. 



실제 플레이스토어에서 앱 이름, 앱 아이콘, 패키지 이름을 크롤링 하는 부분을 만든게 있는데 확인해보시면 이해하는데 도움이 될것 같네요. 


2016/09/13 - [프로그래밍/JAVA] - 자바로 크롤링 구현


'프로그래밍 > JAVA' 카테고리의 다른 글

CLOSE_WAIT 해결 방법  (1) 2017.01.13
RSA 암호화 및 복호화  (0) 2016.12.26
자바로 크롤링 구현  (0) 2016.09.13

+ Recent posts