10장 - 네트워킹

모래사우르스
|2025. 1. 30. 23:31

네트워킹 : 인터넷🌍에 연결되어 있는 원격지의 서버

                  또는 원격지의 단말📱과 통신해서 데이터를 주고받는 동작

 

[ 네트워크 연결 방식 ]
2-tier C/S 클라이언트서버일대일로 연결.
ex) HTTP(웹페이지), FTP(파일전송), POP3(메일)
3-tier 연결 클라이언트, 응용 서버, 데이터 서버로 구성.
데이터베이스를 분리할 수 있어 중간에 비즈니스 로직을 처리하는 응용 서버가 좀 더 다양한 역할을 할 수 있다는 장점이 있음.
N-tier 연결 클라이언트와 n개의 서버로 구성.
P2P (Peer-to-Peer) 서버를 두지 않고 단말끼리 서버와 클라이언트 역할을 함. 📱⇄📱
정보를 검색할 때, 파일 송수신으로 정보를 공유할 때 많이 사용.
ex) 메신저 서비스, SIP(인터넷 전화)

 

 

소켓 연결 : IP 주소로 목적지 호스트를 찾아내고 포트로 통신 접속점을 찾아냄.

                  소켓연결은 TCP와 UDP 방식이 있으며

                  대부분 TCP 연결을 사용한다. (UDP 기반 소켓은 SIP, RTP에서 주로 사용한다.)

 

안드로이드는 소켓 연결을 하기 위해 스레드, 핸들러(UI 업뎃용)를 사용해야한다.

 

 

 

🚩소켓 사용하기

1️⃣activity_main.xml

더보기
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <LinearLayout
        android:id="@+id/client"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#EBABC5"
        android:orientation="vertical">

        <EditText
            android:id="@+id/editTextText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            android:background="@drawable/box"
            android:ems="10"
            android:inputType="text"
            android:padding="10dp" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:text="전송"
            android:textSize="20sp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ScrollView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_marginTop="10dp"
                android:layout_weight="0.7">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/textView"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:textSize="20sp" />
                </LinearLayout>
            </ScrollView>

            <TextView
                android:id="@+id/textView3"
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_gravity="right"
                android:layout_weight="0.3"
                android:paddingRight="20dp"
                android:text="Client"
                android:textAlignment="viewStart"
                android:textSize="34sp" />
        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/server"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#C3E2E1"
        android:orientation="vertical">

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="20dp"
            android:text="서버 시작"
            android:textSize="20sp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ScrollView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_marginTop="10dp"
                android:layout_weight="0.8">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/textView2"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:textSize="20sp" />
                </LinearLayout>
            </ScrollView>

            <TextView
                android:id="@+id/textView4"
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_gravity="right"
                android:layout_weight="0.2"
                android:paddingRight="20dp"
                android:text="Server"
                android:textSize="34sp" />
        </LinearLayout>

    </LinearLayout>
</LinearLayout>

 

2️⃣MainActivity.java

 

🔹전체코드🔹

더보기
package com.example.chapter10;

public class MainActivity extends AppCompatActivity {
    TextView textView1, textView2;
    Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EditText editText = findViewById(R.id.editTextText);
        textView1 = findViewById(R.id.textView);
        textView2 = findViewById(R.id.textView2);

        Button buttonSend = findViewById(R.id.button);
        Button buttonStart = findViewById(R.id.button2);

        buttonSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final String data = editText.getText().toString();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        send(data); // send 메서드 작성
                    }
                }).start();
            }
        });

        buttonStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        startServer(); // startServer 메서드 작성
                    }
                }).start();
            }
        });

    }

    private void send(String data) {
        try{
            int portNum = 5001; // 포트 5001 사용
            Socket socket = new Socket("localhost",portNum); // 접속할 IP 주소 : localhost
            printClientLog("소켓 연결함."); // printClientLog 메서드 작성

            ObjectOutputStream outstream = new ObjectOutputStream(socket.getOutputStream());
            outstream.writeObject(data); // OutputStream 객체는 writeObject 메서드를 사용할 수 있다. (쓰기)
            outstream.flush();
            printClientLog("데이터 전송함.");

            ObjectInputStream instream = new ObjectInputStream(socket.getInputStream());
            printClientLog("서버로부터 받음 : "+instream.readObject());
            // InputStream 객체는 readObject 메서드를 사용할 수 있다. (읽기)

            socket.close();

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    private void printClientLog(final String data) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                textView1.append(data+"\n");
            }
        });
    }

    private void startServer() {
        try{
            int portNum = 5001;
            ServerSocket serverSocket = new ServerSocket(portNum);
            printServerLog("서버 시작함 : "+portNum);

            while(true){
                Socket socket = serverSocket.accept();
                InetAddress clientHost = socket.getLocalAddress();
                int clientPort = socket.getPort();
                printServerLog("클라이언트 연결됨 : "+clientHost+" : "+clientPort);

                ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                Object object = inputStream.readObject();
                printServerLog("데이터 받음 : "+object);

                ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
                outputStream.writeObject(object + " from Server.");
                outputStream.flush();
                printServerLog("데이터 보냄.");

                socket.close();
            }

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    public void printServerLog(final String data){
        handler.post(new Runnable() {
            @Override
            public void run() {
                textView2.append(data+"\n");
            }
        });
    }


}

🔹전송 버튼을 누를 경우

더보기

🔸onCreate 내부

buttonSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final String data = editText.getText().toString();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        send(data); // send 메서드 작성
                    }
                }).start();
            }
        });

스레드를 생성한 뒤 run에서 send() 메서드를 실행한다.

 

🔸send 메서드

    private void send(String data) {
        try{
            int portNum = 5001; // 포트 5001 사용
            Socket socket = new Socket("localhost",portNum); // 접속할 IP 주소 : localhost
            printClientLog("소켓 연결함."); // printClientLog 메서드 작성

            ObjectOutputStream outstream = new ObjectOutputStream(socket.getOutputStream());
            outstream.writeObject(data); // OutputStream 객체는 writeObject 메서드를 사용할 수 있다. (쓰기)
            outstream.flush(); // OutputStream 객체는 flush 메서드를 사용할 수 있다.
            printClientLog("데이터 전송함.");

            ObjectInputStream instream = new ObjectInputStream(socket.getInputStream());
            printClientLog("서버로부터 받음 : "+instream.readObject());
            // InputStream 객체는 readObject 메서드를 사용할 수 있다. (읽기)

            socket.close();

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

스레드에 빠질 수 없는 try-catch문이 있다. (무조건 스레드는 run 내부에 try-catch가 존재하는 형태인건가?)

 

Socket 객체를 만들기 위해 Socket() 메서드에는 접속할 IP 주소와 포트 번호를 파라미터로 넘겨줘야한다.

 

소켓을 통해 데이터를 보내고 받고 싶을 때 소켓 객체에 getOutputStream(), getInputStream()를 사용한다.

문자열을 객체 그대로 보내기 위해 ObjectOutputStream, ObjectInputStream 클래스를 사용한다.

(실제 앱을 만들 때는 DataInputStreamDataOutputStream을 많이 사용한다고 한다. 위 클래스는 자바가 아닌 다른 언어로 만들어진 서버랑 통신할 시 데이터 송수신이 비정상적으로 이루어질 수 있다고 함.)

 

flush() : 버퍼가 꽉 차지는 않았지만 지금 바로 실제로 모니터에 출력을 하고 싶다면 그 때 사용하는 것이 flush 메소드이다. 그러면 버퍼에 있는 내용을 비우고 모니터에 바로 출력한다. (close()의 내부에는 flush()가 포함되어있다)

 

[Java] 입출력 스트림

우선 입출력 스트림의 특징부터 알아보자. 1. 입출력 스트림은 선입선출 구조이다. 따라서 순차적(단방향)으로만 접근이 가능하다. 사실 양방향으로 다 되는 게 있긴 하다. (임의 접근 파일 스트

breakcoding.tistory.com

정리가 잘 되어있어서 나중에 또 읽어보면 좋을 듯.

 

printStackTrace() : 예외처리가 발생할 경우, 어떤 에러가 발생했는지 상세한 설명이 나오는 메서드.

근데 아래 블로그 글 보면 printStackTrace는 안 쓰는게 좋다고 한다. (쓸데없는 에러메시지가 많다 등등의 이유)
 

[Spring] e.printStackTrace() 지양하기(Feat. getStackTrace 사용)

Spring 에서 개발을 하면서 예외가 발생되면 @ControllerAdvice 가 선언된 클래스가 동작하여 알맞은 예외타입(RuntimeException, NullPointerException 등)에 메소드가 호출된다. 대부분 프로젝트마다 로깅하는

dev-jwblog.tistory.com

 

🔸printClientLog 메서드

    private void printClientLog(final String data) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                textView1.append(data+"\n");
            }
        });
    }

Client 쪽 로그를 상단 텍스트뷰에 출력하기 위해 핸들러를 사용한다.

append() 메서드를 사용하여 문자열을 이어서 추가할 수 있다.

🔹서버 시작 버튼을 누를 경우

더보기

🔸onCreate 메서드 내부

        buttonStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        startServer(); // startServer 메서드 작성
                    }
                }).start();
            }
        });

 

🔸startServer 메서드

    private void startServer() {
        try{
            int portNum = 5001;
            ServerSocket serverSocket = new ServerSocket(portNum);
            printServerLog("서버 시작함 : "+portNum);

            while(true){
                // 클라이언트가 접속했을 때 만들어지는 소켓 객체 참조하기
                Socket socket = serverSocket.accept();
                InetAddress clientHost = socket.getLocalAddress();
                int clientPort = socket.getPort();
                printServerLog("클라이언트 연결됨 : "+clientHost+" : "+clientPort);

                ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                Object object = inputStream.readObject();
                printServerLog("데이터 받음 : "+object);

                ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
                outputStream.writeObject(object + " from Server.");
                outputStream.flush();
                printServerLog("데이터 보냄.");

                socket.close();
            }

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

여기서는 ServerSocket 객체를 생성한다. (위에서는 Socket 객체 생성했음) 이 때는 포트 번호만 파라미터로 받음.

 

while문을 사용해서 클라이언트의 접속을 기다리다가, 클라이언트의 접속 요청이 왔을 때 accept 메서드를 통해 소켓 객체가 반환되므로 클라이언트 소켓의 연결 정보를 확인할 수 있다.

 

[java] ServerSocket, Socket

ServerSocket, Socket ServerSocket은 java에서 서버 프로그램을 개발할 때 쓰이는 클래스이다. 해당 클래스를 이용해서 서버를 개발 하는 방법에 대해 알아보겠다. Socket 클래스는 client에서 서버로 접속하

jink1982.tistory.com

 

🔸printServerLog

    public void printServerLog(final String data){
        handler.post(new Runnable() {
            @Override
            public void run() {
                textView2.append(data+"\n");
            }
        });
    }

printClientLog랑 별 차이는 없음.

3️⃣AndroidManifest.xml

    <uses-permission android:name="android.permission.INTERNET"/>

권한 추가하기.

 

4️⃣실행 결과

1. 텍스트 입력 2. 서버 시작 클릭 3. 전송 클릭

1. 첫 화면 2. 입력 상자에 입력 3. 서버 시작 버튼 클릭
3. 서버 시작 버튼 클릭 4. 전송 버튼 한번 클릭 5. 전송 버튼 두번 클릭

 

서버 시작 버튼을 누르면 서버가 시작되었다는 로그가 출력된다.

전송 버튼을 누르면 입력상자의 내용이 서버로 전송되었다가 다시 클라이언트쪽으로 전달되었다는 것을 알 수 있다.