본문 바로가기

안드로이드

소켓 사용하기

반응형

소켓 - IP주소로 목적지 호스트를 찾아내고 포트로 통신 접속점을 찾아내는 소켓 연결은 TCP와 UDP 방식으로 나눌 수 있다

일반적인 프로그래밍에서는 대부분 TCP 연결을 사용한다

 

UDP 기반 소켓은 인터넷 전화에 많이 사용되는 SIP 프로토콜이나 멀티미티어 데이터 스트림을 처리하는 RTP 프로토콜은 기본적으로 UDP를 많이 사용한다

SIP 프로토콜은 Session Initiation Protocol이라고 하는데 IETF에서 정의한 시그널링 프로토콜이다

음성과 화상통화 같은 멀티미디어 세션을 제어하기 위해 사용된다

 

안드로이드는 표준 자바에서 사용하던 java.net 패키지의 클래스들을 그대로 사용할 수 있다

화면 구성을 위한 액티비티를 구성하고 나면 소켓 연결에 필요한 코드는 기존에 사용하던 자바 코드를 그대로 사용할 수 있다

 

안드로이드는 소켓 연결등을 시도하거나 응답을 받아 처리할 때 스레드를 사용해야 한다

스레드를 사용하지 않으면 네트워킹 기능 자체가 동작하지 않는다

 

스레드를 사용하면서 UI를 업데이트하려면 핸들러를 사용해야 한다

원격지에 데이터를 요청하고 응답을 기다리는 네트워킹 기능은 네트어ㅜ크의 상태에 따라 응답 시간이 길어질 수 있을뿐더러 최근 플랫폼에서는 스레드 사용을 강제하고 있기 때문에 이런 경우에는 UI 업데이트를 위해 핸들러를 사용한다

 

AndroidManifast.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.a55_socket">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.A55_Socket">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

 

activity_main

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_bright"
        android:orientation="vertical">

        <EditText
            android:id="@+id/editText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="textPersonName" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="전송" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <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>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="@android:color/holo_orange_light"
        android:orientation="vertical">

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="서버 시작" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <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>
    </LinearLayout>
</LinearLayout>

 

 

 

MainAcitivity

package com.example.a55_socket;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

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

public class MainActivity extends AppCompatActivity {
    EditText editText;

    TextView textView;
    TextView textView2;

    Handler handler = new Handler();

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

        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        textView2 = findViewById(R.id.textView2);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final String data = editText.getText().toString();

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        send(data);
                    }
                }).start();

            }
        });

        Button button2 = findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        startServer();
                    }
                }).start();

            }
        });
    }

    public void send(String data) {
        try {

//            소켓 객체 만들기
            int portNumber = 5001;
//            접속할 ip주소와 포트
            Socket sock = new Socket("localhost", portNumber);
            printClientLog("소켓 연결함.");

//            소켓 객체로 데이터 보내기
//            새로 만들어진 소켓을 통해 데이터를 보내거나 받도 싶을 때 getOutputStream, getInputStream 메소드로 입출력 스트림 객체를 참조
//            문자열을 개체 그대로 보내기 위해 ObjectOutputStream, ObjectInputStream 클래스 사용
            ObjectOutputStream outstream = new ObjectOutputStream(sock.getOutputStream());
            outstream.writeObject(data);
            outstream.flush();
            printClientLog("데이터 전송함.");


            ObjectInputStream instream = new ObjectInputStream(sock.getInputStream());
            printClientLog("서버로부터 받음 : " + instream.readObject());
            sock.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void startServer() {
        try {
            int portNumber = 5001;

//            소켓 서버 객체 만들기
            ServerSocket server = new ServerSocket(portNumber);
            printServerLog("서버 시작함 : " + portNumber);

//            while문을 사용해서 클라이언트의 접속을 기다리다가 클라이언트의 접속 요청이 왔을 때
//            accept 메소드를 통해 소켓 객체가 반환되어, 클라이언트의 연결 정보 확인할 수 있음
            while (true) {
                Socket sock = server.accept();
                InetAddress clientHost = sock.getLocalAddress();
                int clientPort = sock.getPort();
                printServerLog("클라이언트 연결됨 : " + clientHost + " : " + clientPort);

                ObjectInputStream instream = new ObjectInputStream(sock.getInputStream());
                Object obj = instream.readObject();
                printServerLog("데이터 받음 : " + obj);

                ObjectOutputStream outstream = new ObjectOutputStream(sock.getOutputStream());
                outstream.writeObject(obj + " from Server.");
                outstream.flush();
                printServerLog("데이터 보냄.");

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

    //    화면 상단의 텍스트뷰에 글자 출력
    public void printClientLog(final String data) {
        Log.d("MainActivity", data);

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

    }

    //    화면 하단의 텍스트뷰에 글자 출력
    public void printServerLog(final String data) {
        Log.d("MainActivity", data);

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

}

 

 

실제 앱을 만들 때는 ObjectInputStream과 ObjectOutputStream을 잘 사용하지 않는다

일반적으로는 DataInputStream과 DataOutputStream을 많이 사용한다

반응형

'안드로이드' 카테고리의 다른 글

Volley - HTTP 라이브러리  (0) 2021.10.28
웹으로 요청하기  (0) 2021.10.27
스레드로 애니메이션 만들기  (0) 2021.10.27
일정 시간 후에 실행하기  (0) 2021.10.27
스레드와 핸들러, Runnable, runOnUiThread  (0) 2021.10.27