태그 보관물: network

[Powershell] 포트 정보 조회

파워쉘(Powershell)을 이용한 포트 정보 조회

파워쉘을 이용하면 네트워크 연결 상태에 보다 효율적인 접근을 할 수 있습니다.

이전 명령 프롬프트(cmd)를 이용하면 아래와 같이 할 수 있습니다.

> netstat -anp tcp | findstr 8080

위의 정보는 TCP 연결중 8080 이 포함된 문자열을 검색하라는 뜻입니다.

TCP 연결 상태 조회

Get-NETTcpConnection cmdlet 을 이용하면 현재 운영체제에서 TCP로 연결된 목록을 출력할 수 있습니다.

PS> Get-NetTCPConnection | Select-Object -First 10

LocalAddress                        LocalPort RemoteAddress                       RemotePort State       AppliedSetting OwningProcess
------------                        --------- -------------                       ---------- -----       -------------- -------------
::                                  60625     ::                                  0          Bound                      15984
::                                  60357     ::                                  0          Bound                      21468
::1                                 60625     ::1                                 3306       Established Internet       15984
::                                  49672     ::                                  0          Listen                     708
::                                  49668     ::                                  0          Listen                     700
::                                  49667     ::                                  0          Listen                     2384
::                                  49666     ::                                  0          Listen                     1388
::                                  49665     ::                                  0          Listen                     1192
::                                  49664     ::                                  0          Listen                     600
::                                  17500     ::                                  0          Listen                     18324

위의 명령은 TCP연결중에서 10개의 데이터만 출력하라는 뜻입니다.

특정 포트 정보 조회

그렇다면 TCP 연결중에 원하는 포트만 찾고 싶은 경우에는 다음과 같이 할 수 있습니다.

PS> Get-NetTCPConnection | Where-Object {$_.LocalPort -eq 3306 -and $_.State -eq 'Listen'}

LocalAddress                        LocalPort RemoteAddress                       RemotePort State       AppliedSetting OwningProcess
------------                        --------- -------------                       ---------- -----       -------------- -------------
::                                  3306      ::                                  0          Listen                     8248

포트 정보를 이용해서 프로세스 정보 조회하기

가끔 서버를 설정하다보면 포트 충돌이 발생하는 경우를 확인할 수 있습니다. 그럴 경우 이전에 해당 포트를 사용하는 프로세스를 검색하고 검색된 프로세스를 죽이는 방식으로 처리를 하곤 합니다.

아래의 명령을 통하면 해당 포트를 사용중인 프로세스가 어떤 프로세스인지 확인할 수 있습니다.

PS> $pid3306 =  Get-NetTCPConnection | Where-Object {$_.LocalPort -eq 3306 -and $_.State -eq 'Listen'} | select-object -ExpandProperty owningprocess

PS>  Get-Process | Where-Object {$_.ID -eq $pid3306}
Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    230      22   445112      79396       0.20   8248   5 mysqld

결과는 3306 포트를 사용중인 프로세스는 mysqld 라는 프로세스입니다.

아직 파워쉘(Powershell)을 사용한지 오래되지 않았지만 확실하게 느낄 수 있는 것은 Batch 프로그램 보다는 효율적인것 같습니다.

OSX(Mac) Port Forwarding

OSX(Mac) Port Forwarding

1. 이슈사항

플래시 플레이어에서 TCP연결을 시도하면 플래시 플레이어 내부에서 TCP 843 포트로 크로스도메인(Cross Domain) 정보 확인을 요청한다.
그렇지만 리눅스와 OSX에서는 1024 포트 아래에 있는 포트는 관리자만 사용이 가능한 포트로 일반 사용자는 해당 포트(843)를 바인딩 할 수 없는 문제가 발생.

2. 해결방법

1024 이상의 특정 포트(1843)로 바인딩을 하고 특정 포트 843번으로 요청이 들어오면 내부의 1843 포트로 포워딩 하는 방식.

2.1 설정방법

Port Map을 사용하는 방법이 사용자 입장에서는 쉬운방법이지만 서버를 운영한다는 생각으로 설정 파일을 변경하여 적용하도록 변경.

2.1.1 네트워크 인터페이스 확인

$ ifconfig

ifconfig 출력 결과

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 60:f8:1d:b4:43:de
inet6 fe80::62f8:1dff:feb4:43de%en0 prefixlen 64 scopeid 0x4
inet 192.168.10.42 netmask 0xffffff00 broadcast 192.168.10.255
nd6 options=1 media: autoselect
status: active
en1: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
options=60<TSO4,TSO6>
ether 72:00:07:97:53:30
media: autoselect
status: inactive

출력 내용중 자신이 사용하는 IP(192.168.10.42)에 해당하는 인터페이스를 확인
위에서는 en0 이 포워딩이 필요한 인터페이스 정보.

2.1.2 포워딩 룰 설정

2.1.2.1 flash.crossdomain.forwarding 파일 생성
$ sudo vim /etc/pf.anchors/flash.crossdomain.forwarding

/etc/pf.anchors/flash.crossdomain.forwarding

rdr pass on en0 inet proto tcp from any to 192.168.10.42 port 843 -> 192.168.10.42 port 1843

네트워크 카드 정보(en0) 와 아이피 포트 정보를 자신이 사용하는 정보로 정확하게 입력한다.

2.1.2.2 pf-flash-crossdomain.conf 파일 생성
$ sudo vim /etc/pf-flash-crossdomain.conf

/etc/pf-flash-crossdomain.conf

rdr-anchor "forwarding"
load anchor "forwarding" from "/etc/pf.anchors/flash.crossdomain.forwarding"

2.1.3 포워딩 룰 활성화

$ sudo pfctl -ef /etc/pf-flash-crossdomain.conf

2.1.4 포워딩 룰 비활성화

포워딩이 필요하지 않게 되면 사용하도록 한다.

$ sudo pfctl -d

3. 참고사이트

NIO Socket을 이용한 Echo 테스트

NIO Socket은 서버 파트만 변경을 하고 클라이언트는 이전 ServerSocket 과 Socket을 이용한 Echo 테스트 의 Client 소스를 그대로 이용하면 됩니다.

package coozplz.example.nio.echo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

public class NIOEchoServer implements Runnable {
	Selector selector; // 어떤 채널이 어떤 IO 를 할 수 있는지 알려주는 클래스
	int port = 9999;
	// 한글 전송 ENCODING 설정
	Charset charset = Charset.forName("EUC-KR");
	CharsetEncoder encoder = charset.newEncoder();

	public NIOEchoServer() throws IOException {
		// 1. Selector 생성
		selector = Selector.open();
		// SeverSocket 에 대응하는 ServerSocketChannel 생성
		ServerSocketChannel channel = ServerSocketChannel.open();
		// 서버 소켓 생성
		ServerSocket socket = channel.socket();
		SocketAddress addr = new InetSocketAddress(port);
		socket.bind(addr);
		// Non-Blocking 상태로 만듬
		channel.configureBlocking(false);
		// 바인딩된 ServerSocketChannel 을 Selector에 등록한다.
		channel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("wait for connecting client");
	}

	public void run() {
		try {
			int socketOps = SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE;
			ByteBuffer buffer = null;
			// 생성된 소켓채널에 대해 accept 상태 일때 알려달라고 selector에 등록 시킨 후
			// 이벤트가 일어날 때 까지 기다린다.
			// 클라이언트가 접속하면 Selector는 미리 등록 했던 SeverSocketChannel에 이벤트가
			// 발생했으므로 Select 메소드에서 1을 돌려준다.
			while (selector.select() > 0) {
				// 현재 Selector에 등록된 채널에 동작이 아니라도 실행 되는 경우 그 채널들을 SelectionKey의
				// Set 에 추가한다. 아래에서는 선택된 채널들의 키를 얻는다. 즉 해당 IO 에 대해 등록해
				// 놓은 채널의 키를 얻는다.
				Set keys = selector.selectedKeys();
				Iterator iter = keys.iterator();
				while (iter.hasNext()) {
					SelectionKey selected = (SelectionKey) iter.next();
					iter.remove();
					// channel() 의 현재 하고 있는 동작(읽기, 쓰기)에 대한 파악
					SelectableChannel channel = selected.channel();
					if (channel instanceof ServerSocketChannel) {
						// ServerSocketChannel이라면 Accept() 를 호출
						// 접속 요청을 해온 상대방 소켓과 연결 될 수 있는 SocketChannel 을 얻는다.
						ServerSocketChannel serverSocketChannel = (ServerSocketChannel) channel;
						SocketChannel socketChannel = serverSocketChannel.accept();
						
						// 현 시점의 ServerSocketChannel은 Non-Blocking IO로 설정 되어 있음
						// 이것은 당장 접속이 없어도 블로킹 되지 않고 바로 NULL 을 던지므로 체크 필요
						if (socketChannel == null) {
							System.out.println("Socket channel is null");
							continue;
						}
						System.out.println("Client is conntected " + socketChannel);
						// 얻은 socketChannel 은 블로킹 소켓이므로 Non-Blocking IO 상태로 설정
						socketChannel.configureBlocking(false);
						// 소켓 채널을 Selector에 등록
						socketChannel.register(selector, socketOps);
					} else {
						// 일반 소켓 채널이 경우 해당 채널을 얻어 낸다.
						SocketChannel socketChannel = (SocketChannel) channel;
						try {
							buffer = ByteBuffer.allocate(100);

							// 소켓 채널의 행동을 검사해서 그에 대응하는 작업을 함
							if (selected.isConnectable()) {
								System.out.println("Client 와의 연결 설정 OK");
								if (socketChannel.isConnectionPending()) {
									System.out.println("Client 와의 연결 설정을 마무리 중입니다.");
									socketChannel.finishConnect();
								}
							} else if (selected.isReadable()) {
								// 읽기 요청 이라면
								socketChannel.read(buffer);
								if (buffer.position() != 0) {
									buffer.clear();
									System.out.println("클라이언트로 전달된 내용:");

									// Non-BLocking Mode 이므로 데이터가 모두 전달될때 까지 기다림
									while (buffer.hasRemaining()) {
										System.out.print((char) buffer.get());
									}
									buffer.clear();
									System.out.println();

									// 쓰기가 간으하다면
									if (selected.isWritable()) {
										String str = "이건 서버에서 보낸 데이터…";
										// 한글 인코딩
										CharBuffer charBuf = CharBuffer.wrap(str);

										socketChannel.write(encoder.encode(CharBuffer.wrap(str+ "\r\n")));
										// socketChannel.write(byteBuffer);
										System.out.println("Send:" + str);
									}
								}

							}

						} catch (Exception ex) {
							Logger.getLogger(NIOEchoServer.class.getName()).log(Level.SEVERE, null, ex);
						}
					}
				}
			}
		} catch (IOException ex) {
			Logger.getLogger(NIOEchoServer.class.getName()).log(Level.SEVERE,null, ex);
		}
	}

	public static void main(String[] args) throws IOException {
		NIOEchoServer s = new NIOEchoServer();
		new Thread(s).start();
	}
}

ServerSocket 과 Socket을 이용한 Echo 테스트

EchoServer

package coozplz.example;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MultiThreadEchoServerMain extends Thread {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("서버 소켓 생성 완료");

        while (true) {
            Socket sock = ss.accept();
            System.out.println(sock.getInetAddress().toString() + " // is connted");
            MultiThreadEchoServer client = new MultiThreadEchoServer(sock);
            client.start();


        }

    }
}

class MultiThreadEchoServer extends Thread {
    Socket socket;
    InputStream in;
    OutputStream os;

    public MultiThreadEchoServer(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        System.out.println("MultiThreadEchoServer is Running");
        try {
            in = socket.getInputStream();
            os = socket.getOutputStream();


            byte[] buf = new byte[1024];
            int count;
            while ((count = in.read(buf)) > 0) {

                System.out.println("received: " + new String(buf, 0, count));
                os.write(buf, 0, count);
                System.out.println("write:" + new String(buf, 0, count));
            }
            os.close();
            System.out.println("연결종료");
        } catch (IOException ex) {
            Logger.getLogger(MultiThreadEchoServerMain.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException ex) {
                    Logger.getLogger(MultiThreadEchoServerMain.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }


}

Echo Client

package coozplz.example;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

public class EchoClientThread {
    public static void main(String[] ar) throws IOException {
        EchoClientThread client = new EchoClientThread();
        client.process();
    }

    public void process() throws IOException {
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress("127.0.0.1", 9999));
        OutputStream os = socket.getOutputStream();
        InputStream in = socket.getInputStream();


        byte[] buf = new byte[1024];
        int count = 0;
        while ((count = System.in.read(buf)) > 0) {
            os.write(buf, 0, count);
            System.out.println("Client -> Svr: " + new String(buf, 0, count));
            count = in.read(buf);
            System.out.println("Svr -> Client: " + new String(buf, 0, count));
        }
        os.close();
        while ((count = in.read(buf)) > 0) {
            System.out.write(buf, 0, count);
        }
        System.out.close();
        System.out.println("연결종료");
        if (socket != null) {
            socket.close();
        }
    }
}

NIO 대용량 서버 준비 작업

이제 조금 있으면 대용량 서버를 만들어야 하는 경우가 발생 할 수도 있을 것 같아 미리 준비를 해야겠습니다. (해본 것과 할 수 있는 것은 분명한 차이가 있다고 생각이 되기에…)

대략 일정

1. 자료 검색 (4/23 ~ 5/4)
– Java NIO  Example 검색( 2D )

– Java NIO 자료 분석 ( 2D )
– Java NIO 채팅 서버 분석 ( 2D )
– Apache MINA ( 2D )
– 대용량 메신저 서버 분석 ( 2D )

2. 간단한 메신저 서버 구현  (5/6~5/11)
– 메신저 접속 및 전문 교환

3. SAPP 서버 NIO 로 변경 ( 5/12~5/18 )
– 이벤트 처리 부분 수정
– XML 파싱 부분 수정

1. 자료 검색

랜카드 정보 조회

라이선스 적용을 위해 랜카드 정보를 찾는데 기존 방식은
Runtime.getRuntime().exec(“ipconfig /all”);
을 사용하여 InputStream 으로 추출 했습니다.  이렇게 하는 방법 외에 다른 방법을 해보니 예전에 특정아이피로 바인딩 하는 방법에서 찾을 수 있을 것 같아. 아래와 같이 해봤습니다.

간단하게 맥어드레스와 이름 만 출력 합니다.

// 설치된 모든 랜카드 정보를 얻는다.
 Enumeration netEnumeration =  NetworkInterface.getNetworkInterfaces();

 while (netEnumeration.hasMoreElements()) {

    // 랜카드 목록에서 랜카드 하나의 정보를 추출한다.
    NetworkInterface networkInterface = (NetworkInterface) netEnumeration.nextElement();

    // 맥어드레스를 추출
    byte[] data = networkInterface.getHardwareAddress();
    if(data == null || data.length != 6) {
        continue;
    }


    StringBuilder macAddress = new StringBuilder();

    // 맥어드레스를 읽기 쉬운 타입 => 00:00:00:00:00:00 형식으로 변환한다.
    for (int i = 0; i < data.length; i++) {
        macAddress.append(Integer.toHexString(0x10000 | (0xff & (int)data[ 0 + i])).substring(3, 5).toUpperCase())
        if (i != 5)
            macAddress.append(":");
    }


    // 출력한다.
    System.out.println("NAME:" + networkInterface.getDisplayName());
    System.out.println("MAC: " + macAddress.toString());
 }

Set Specify Local IP Address

 

Socket

   1: Socket socket = new Socket();

   2:  

   3: // If set the port zero, the port will set any available port in you system

   4: SocketAddress localAddress = new InetSocketAddress(addr, 0);

   5:  

   6: socket.bind(localAddress);

   7:  

   8: SocketAddress remoteAddress = new InetSocketAddress(user.getIpAddress(), 8000);

   9:  

  10: // set Connection Timeout 

  11: socket.connect(remoteAddress, 2000);

 

URLConnection

   1: URL url = new URL(pushURL);

   2:             

   3: // set up specify local IP Address

   4: SocketAddress localAddress = new InetSocketAddress(addr, 0);

   5: Proxy proxy = new Proxy(Proxy.Type.DIRECT, localAddress);

   6: URLConnection urlConnection = url.openConnection(proxy);

   7:  

   8: // set the timeout

   9: urlConnection.setConnectTimeout(2000);

  10: urlConnection.setReadTimeout(2000);

 

 

HttpClient

   1: HttpClient client = new DefaultHttpClient();

   2: client.getParams().setParameter("http.connection.timeout", 1000);

   3: client.getParams().setParameter("http.socket.timeout", 1000);

   4: client.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_1);

   5: client.getConnectionManager().closeIdleConnections(2000, TimeUnit.MILLISECONDS);

   6:  

   7: // Set specify local ip address

   8: client.getParams().setParameter(ConnRouteParams.LOCAL_ADDRESS, addr); 

   9: post.getParams().setParameter(ConnRouteParams.LOCAL_ADDRESS, addr);