본문 바로가기
안드로이드

[Android] 5. 네트워킹 - 소켓 사용하기

qbang 2019. 7. 28.

안녕하세요 이번 시간에는 5강 네트워킹의 두번째 강의인 '소켓 사용하기' 첫번째 리뷰입니다.

마찬가지로 개념을 알아보면서 실습한 내용을 덧붙여 알아보도록 하겠습니다!

 


안드로이드에서 네트워킹을 사용할 때는 반드시 스레드를 사용하고, 스레드를 사용하므로 UI 업데이트를 위해서는 반드시 핸들러를 사용하게 됩니다.

만약 서버 쪽에 데이터를 요청해서 응답을 받고 UI를 업데이트 하려고 한다면 어떻게 해야 할까요?

 

소켓은 네트워킹(TCP/IP)의 가장 기본으로, 서버 소켓과 클라이언트 소켓을 만들고 서로 연결이 되도록 합니다.

서버 소켓은 연결을 기다리는 역할을 하고 클라이언트 소켓은 연결을 만드는 역할을 하죠. 클라이언트에서 연결을 만들고 나면 데이터를 보내거나 받을 수 있습니다.

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

이로 인해 인터넷이라고 하는 권한을 쓰고 스레드를 썼을 때 UI만 접근을 안한다면 PC에서와 동일한 기능을 클라이언트와 서버로 구성할 수 있습니다.

그러나 UI를 업데이트 해줘야 한다면 꼭 핸들러를 써야합니다.

 

먼저 클라이언트 쪽 코드입니다.

package com.example.mysocket;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.logging.SocketHandler;

public class MainActivity extends AppCompatActivity {

    TextView textView;
    Handler handler = new Handler();

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

        textView = (TextView) findViewById(R.id.textView);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                ClientThread thread = new ClientThread();
                thread.start();

            }
        });
    }

    class ClientThread extends Thread{
        public void run(){
            String host = "localhost";
            int port = 5001;
            try{
                Socket socket = new Socket(host, port);

                //서버로 데이터 주기
               ObjectOutputStream outstream = new ObjectOutputStream(socket.getOutputStream());
               outstream.writeObject("안녕!");
               outstream.flush();
               Log.d("ClientThread","서버로 보냄");

               //서버에서부터 데이터 받기
               ObjectInputStream instream = new ObjectInputStream(socket.getInputStream());
               final Object input = instream.readObject();
               Log.d("ClientThread", "받은 데이터: "+input);

                //스레드 안에서 UI 접근 -> 핸들러
               handler.post(new Runnable() {
                   @Override
                   public void run() {
                       textView.setText("받은 데이터: "+input);
                   }
               });

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

        }
    }
}

해당 코드를 실행시키고 버튼을 클릭하면 Run 탭에 지정한 로그가 찍히는 것을 볼 수 있습니다.

Log 확인

다음으로 서버쪽 코드입니다.

package com.example.myserver;

import android.content.Intent;
import android.sax.StartElementListener;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MainActivity extends AppCompatActivity {

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

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent(getApplicationContext(), ServerService.class);
                startService(intent);
            }
        });
    }


}

시스템이 리소스를 잡아먹다보면 서버를 셧다운 시킬 수도 있기 때문에 새로운 Service를 추가하여 불러오도록 합니다.

package com.example.myserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerService extends Service {
    public ServerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 서버 소켓을 실행하는 코드
        ServerThread thread = new ServerThread();
        thread.start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    class ServerThread extends Thread{
        public void run(){
            //서버 실행
            int port = 5001;
            try{
                ServerSocket server = new ServerSocket(port);
                Log.d("ServerThread", "서버가 실행됨.");

                while (true){
                    //대기를 하다가 클라이언트가 접속하면 소켓이라고 하는 객체가 return됨
                    Socket socket = server.accept();

                    //받은 데이터를 뿌려줌
                    ObjectInputStream instream = new ObjectInputStream(socket.getInputStream());
                    Object input = instream.readObject();
                    Log.d("ServerThread", "input: "+input);

                    //클라이언트로 데이터 전송
                    ObjectOutputStream outstream = new ObjectOutputStream(socket.getOutputStream());
                    outstream.writeObject(input + "from server.");
                    //outstream, OutputStream은 write를 하면 버퍼에 남아있을 수 있기 때문에 flush()를 꼭 해주기
                    outstream.flush();
                    Log.d("ServerThread","output");

                    //한정적인 리소스를 계속 쓰는 것을 방지하기 위해 끊어주기
                    socket.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");

    }
}

마찬가지로 해당 코드를 실행시키고 버튼을 클릭하면 Run 탭에 지정한 로그가 찍히는 것을 볼 수 있습니다.

Log 확인

위 코드에 대한 결과 화면은 아래와 같습니다.

초기 화면
결과 화면

클라이언트 시작을 눌렀을 때 서버로부터 전달받은 데이터를 화면에 출력하는 것을 볼 수 있습니다.

이로써 TCP/IP 통신을 할 때 소켓을 이용해야 하고 UI를 업데이트 하려면 핸들러를 이용해야 하는 것을 살펴볼 수 있었습니다.

마찬가지로 상황에 맞는 방법을 찾아 이용하시면 될 것 같네요. 글 읽어주셔서 감사합니다 :)

댓글