[안드로이드] 서버/클라이언트 소켓(Socket) 통신하기

2010.04.27 19:40

 

 

 

<목표> 

 

[안드로이드] 서버/클라이언트 소켓(Socket) 통신하기

   

  오늘은 서버, 클라이언트의 소켓(Soket) 통신에 대해서 알아보겠습니다. 기존의 많은 안드로이드 어플리케이션이 각각의 서버를 이용하여 정보를 주고 받습니다. 아무래도 기기 내에서 만으로 서비스하기에는 한계가 있기 때문이죠. 정보를 저장하고, 서버에서 처리하여 결과를 주고, 클라이언트는 그 결과를 받아서 어플리케이션에 알맞은 동작을 취하도록 합니다. 트위터 서비스나 스마트폰을 이용해서 공짜 문자(통신료 제외)를 주고 받을 수 있는 것도 서비스를 제공하는 곳에서 서버를 두기 때문입니다. 그 덕분에 핸드폰을 벗어나 더 많은 정보를 처리할 수 있도록 할 수 있습니다.

 

  서버/클라이언트 소켓 통신은 기존의 자바를 이용해서 소켓 통신을 해보신 분들이라면 어렵지 않게 사용하실 수 있습니다. 서버의 소스 자체는 완전히 자바 소스로 이루어지기 때문이지요. 실제로 서버는 안드로이드를 통해서 돌리는 것이 아니라, 기존의 자바 프로그래밍을 이용하여 수행합니다. 클라이언트는 당연히 안드로이드로 개발을 해야겠지요. 오늘 보여드릴 예제 소스는 인터넷에 돌아다니는 간단한 서버 – 클라이언트 소켓 통신을 가지고 와서 나름대로 수정을 해본 것입니다. 기존의 샘플 코드가 하나의 메시지를 서버로 보내고 난 뒤에, 바로 클라이언트에서 기다리면서 데이터가 오기를 기다리는 형태로 제공되었습니다. 그렇기 때문에 클라이언트가 계속 데이터를 기다리면서 블록킹 되어 있는 상태에서만 프로그래밍이 돌아갔습니다. 이러한 부분을 수정하여 쓰레드를 이용하여 백그라운드에서 돌아가게 하여, 기존의 클라이언트에서는 원래의 프로그램이 수행되고, 서버에서 오는 정보를 받는 부분은 쓰레드를 통해 해결했습니다.

 

  먼저 서버를 만들 자바 코드를 알아보고, 뒤에는 안드로이드에서 소켓 통신을 위한 설정과정을 간단한 예제를 통해서 알아보겠습니다.

     

     

     

  STEP 1 Java Source Code 

     

  자바 코드는 두 가지를 다루게 됩니다. 처음은 서버를 돌리는 데 필요한 자버 코드를 알아보고, 두 번째는 안드로이드 클라이언트 코드를 알아보겠습니다.

 

 

  [ 서버 ] TCP Server Java Code 

 

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
 
public class TCPServer implements Runnable {
 
    public static final int ServerPort = 9999;
    public static final String ServerIP = "xxx.xxx.xxx.xxxx";
 
    @Override
    public void run() {
 
        try {
            System.out.println("S: Connecting...");
            ServerSocket serverSocket = new ServerSocket(ServerPort);
 
            while (true) {
                Socket client = serverSocket.accept();
                System.out.println("S: Receiving...");
                try {
                    BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                    String str = in.readLine();
                    System.out.println("S: Received: '" + str + "'");
                     
                    PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
                    out.println("Server Received " + str);
                } catch (Exception e) {
                    System.out.println("S: Error");
                    e.printStackTrace();
                } finally {
                    client.close();
                    System.out.println("S: Done.");
                }
            }
        } catch (Exception e) {
            System.out.println("S: Error");
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
 
        Thread desktopServerThread = new Thread(new TCPServer());
        desktopServerThread.start();
 
    }
 
}

 

   서버의 기능은 클라이언트에서 오는 데이터를 받아들이는 게 핵심입니다. 자신의 포트를 세팅하여 소켓을 여는 것부터 시작하여, 클라이언트에서 오는 정보를 받기 위해서 accept() 를 통해서 기다립니다. 그리고 데이터가 왔다는 신호가 오면 리더를 통해서 스트림을 읽어냅니다. 그리고 자신이 받은 스트림 정보를 다시 돌려보내는 역할을 합니다. 현재는 하나의 클라이언트와 컨넥트를 통해 데이터를 주고 받는 형식입니다. 많은 클라이언트와 통신하고 싶으면 클라이언트에 대한 정보를 저장하고, 여러 클라이언트에게 적절하게 보내는 기능을 추가하시면 되겠습니다.

 

 

  [ 클라이언트 ] TCP Client Java Code 

 

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import org.apache.http.util.ByteArrayBuffer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
 
public class NewClient extends Activity {
 
    private String html = "";
    private Handler mHandler;
 
    private Socket socket;
 
    private BufferedReader networkReader;
    private BufferedWriter networkWriter;
 
    private String ip = "xxx.xxx.xxx.xxx"; // IP
    private int port = 9999; // PORT번호
 
    @Override
    protected void onStop() {
        super.onStop();
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mHandler = new Handler();
 
        try {
            setSocket(ip, port);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
 
        checkUpdate.start();
 
        final EditText et = (EditText) findViewById(R.id.EditText01);
        Button btn = (Button) findViewById(R.id.Button01);
        final TextView tv = (TextView) findViewById(R.id.TextView01);
 
        btn.setOnClickListener(new OnClickListener() {
 
            public void onClick(View v) {
                if (et.getText().toString() != null || !et.getText().toString().equals("")) {
                    PrintWriter out = new PrintWriter(networkWriter, true);
                    String return_msg = et.getText().toString();
                    out.println(return_msg);
                }
            }
        });
    }
 
    private Thread checkUpdate = new Thread() {
 
        public void run() {
            try {
                String line;
                Log.w("ChattingStart", "Start Thread");
                while (true) {
                    Log.w("Chatting is running", "chatting is running");
                    line = networkReader.readLine();
                    html = line;
                    mHandler.post(showUpdate);
                }
            } catch (Exception e) {
 
            }
        }
    };
 
    private Runnable showUpdate = new Runnable() {
 
        public void run() {
            Toast.makeText(NewClient.this, "Coming word: " + html, Toast.LENGTH_SHORT).show();
        }
 
    };
 
    public void setSocket(String ip, int port) throws IOException {
 
        try {
            socket = new Socket(ip, port);
            networkWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            networkReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException e) {
            System.out.println(e);
            e.printStackTrace();
        }
 
    }
 
}

 

 

  이 번에 살펴볼 코드는 안드로이드 클라이언트 프로그램입니다. 안드로이드 클라이언트 코드에서는 서버에 소켓을 연결하고 받은 정보를 이용하여 프로그램을 수행시키는 것을 다룹니다. 먼저 onCreate()가 되면 서버에 연결하도록 설계되어 있습니다. IP 주소와 포트 번호를 알맞게 설정해주시고, 소켓을 연결합니다. 그리고 데이터를 주고 받기 위해서 리드, 라이터를 설정하여 둡니다. 예제에서는 간단히 텍스트 박스에 문자를 적고 버튼을 누르면, 서버로 데이터를 보냅니다. 그리고 서버에서 오는 데이터를 계속 받기 위해서 while을 쓰레드를 통해서 실행합니다. 여기서 받은 데이터 정보를 토스트 기능을 통해 출력합니다. 여기서 UI에 대한 접근을 핸들러를 통해서 하고 있는 것을 확인할 수 있습니다. 이렇게 하는 이유는 다른 포스트에 올리겠습니다. 일단 UI에 대한 접근은 핸들러를 통해서 수행한다고 생각하시고 프로그램을 수행해야한다는 것만 기억하시고 계시면 될 것 같습니다.

       

 

  STEP 2 Xml Code 

       

   Xml 코드에는 간단히 EditText에서 넣은 문자열을 버튼을 통해 서버로 데이터를 보내는 기본적인 기능만 추가하시면 됩니다.

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
    <TextView
        android:id="@+id/TextView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 
    <EditText
        android:id="@+id/EditText01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 
    <Button
        android:id="@+id/Button01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send" />
 
    <TextView
        android:id="@+id/chatting"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 
</LinearLayout>

 

 

  STEP 3 AndroidManifest.xml Code 

  

  메니페스트에는 소켓을 이용하기위해 인터넷을 사용해야 하므로, 인터넷을 사용하겠다는 퍼미션만 추가해주시면 됩니다.

   

/* AndroidManifest.xml 에 추가해야할 인터넷 사용 허가권 */
<uses-permission android:name="android.permission.INTERNET" />

 

<?xml version="1.0" encoding="utf-8"?>
    package="socket.client"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
 
        <activity
            android:name=".NewClient"
            android:label="@string/app_name" >
 
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

 

     

     

<마무리> 서버/클라이언트 소켓(Socket) 통신하기

 

 

  서버와 클라이언트의 소켓 통신에 대해서 알아보았습니다. 핸드폰 내에서의 기능과 정보만으로는 어플리케이션이 한정적일 수 밖에 없습니다. 그렇기 때문에 통신을 통해서 좀 더 폭넓은 서비스를 제공하기 위해서 서버와 클라이언트가 서로 통신할 수 있도록 소켓에 대해서 알아볼 필요가 있었습니다. 하나의 서버가 여러 곳의 클라이언트들에 대해서 서비스를 해줘야 할 때는 클라이언트에 대한 정보를 가지고 있으면서 소켓을 각각 연결시켜주어야 합니다. 안드로이드 클라이언트 부분에서도 현재 수행되고 있는 동작에 영향을 받지 않으면서 백그라운드에서 쓰레드가 돌면서 서버에서 오는 정보를 수시로 받을 수 있도록 해야합니다. 소켓 통신에 대한 기존 개념을 알고 계신 분들이라면 크게 어렵지 않게 이해하실 수 있으셨으리라 봅니다. 처음 접하시는 분들은 데이터를 주고 받는 부분에서 자신이 수행하고 싶은 기능만 추가해주시면 간단히 소켓 통신을 하실 수 있으리라 생각합니다.

   

   

   

[ 참고자료 ]

     

 


Posted by 
맥박맥박의 개발 일지

 

  • 2013년 7월 25일 업데이트하였습니다.

 

 

맥박 안드로이드 , , , ,

  1. 이전 댓글 더보기
  2. Blog Icon
    안드로이드초보자용

    항상 감사드려요^^ 덕분에 안드로이드에 많은 이해를 얻고갑니다.

    아참 정말 제가초보라서그러는데요... 서버를 먼저 자바로 실행시킨후 안드로이드를 켜야하나요? 정말초보죠..ㅜㅜ

  3. Blog Icon
    초보자

    안드로이드 상위버전(3.x이상) 허니콤이상에서는
    onCreate 맨 첫줄에 StrictMode.DefaultEnhance()써줘야 정상적으로 통신합니다.(서버,클라 둘다)
    저도 이것때문에 한참을 헤매었네 어지럽게 그누가 옆에 있는지도모르게~

  4. Blog Icon
    초보자

    스트릭트모드 검색하시면 잘나옵니다.

  5. Blog Icon
    SeeTheStar

    좋은자료 정말 감사합니다. ^^ 혹 에뮬레이터가 socket = new Socket(ip, port); 이부분에서 계속 정상작동 안되시는분은 아래 블로그 가보시면 원인파악이 될 것 같네요 ^^
    http://blog.naver.com/PostView.nhn?blogId=gotomars&logNo=30146980248

  6. Blog Icon

    정말 좋은 자료 감사합니다^^
    덕분에 블루투스 챗에 대해서 이해가 됬네요.

    혹시 괜찮으시다면 질문 한개 해도 될까요?
    기기가 3개가 있는데, 두개는 클라이언트로 작동 할거고, 한개는 서버로 작동 할겁니다.

    근데 두개의 기기가 각각 휴대폰, BT모듈을 이용한 아두이노입니다.

    기기를 두개를 동시에 서버에서 받아 오고 싶은데 안드로이드 이클립스 다른 프로젝트내에서 동시에 받을 수 있게 가능한가요?

  7. Blog Icon
    감사합니다

    감사합니다 많은 도움이 되었습니다 ^^
    샌드아이스크림으로 하니
    윗분처럼 에러가 뜨네요 ㅠ
    하지만 SeeTheStar님께서 올려주신 링크덕분에
    해결했습니다 ^^
    두분 모두 감사드립니다!

  8. 감사합니다..게시판 어플 만들건데 이 것이 적용 되겟죠?

  9. Blog Icon
    안녕하세요`

    많은 도움이 되는데 죄송하지만 저도 제도로된 소스코드 좀 보내주시면 안될까요??

    minkyuj0@naver.com 입니다~~

  10. Blog Icon

    와우 좋은소스 잘보고갑니다

    ;)

  11. Blog Icon
    벤지

    감사합니다...공부하는데 큰 도움이 되었어요..^-^

  12. Blog Icon
    낄낄

    당연히 생각없이 복붙하면 R.layout.값
    때문에 에러나죠..

  13. Blog Icon
    후유와

    감사합니다.. 소스를 보고 해보니 좀 더 정확한 소스를 제공해 주셨으면 합니다.

    gamblerush@naver.com 입니다~

    참고로 사용하여 서버 클라이언트에 공부하는데 도움이 되겠습니다.

  14. Blog Icon
    홍홍

    좋은 게시물 감사합니다!!
    이클립스에 자바 서버를 먼저 실행시킨 후 안드로이드 클라이언트를 돌려보면 앱도 문제없이 뜨긴하는데요, 서버쪽에 connecting만 뜨고 아무것도 되지 않습니다. 안드로이드 앱에서도 무언가를 입력하고 send 버튼을 눌러도 아무 진행이 안되는데요, 서버와 클라이언트가 통신하는 것을 어떻게 확인할 수 있는지 설명 부탁드려요ㅠㅠㅠㅠ

  15. Blog Icon
    마시리스

    홍홍 // 안드로이드 최근버전에서는 네트워크 통신을 메인엑티비티에서 바로 사용할 수 없습니다.
    쓰레드를 하나 만들어서 그 안에서 통신이 이루어지거나
    메인엑티비티 크리에이트코드에
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
    를 넣으시면 될꺼에요

  16. Blog Icon
    John

    잘보고 갑니다~~

  17. Blog Icon

    감사합니다!!!!

    제가 이 기능을 꼭 만들어 보고 싶었는데 정리가 너무 잘되있어서 이해하는데 큰 도움이 되었습니다.

    제가 아직 서버 클라이언트 개념이 이해가 안되서 그러는데요 제가 사용중아 이클립스 안드로이드 프로젝트 안에 위에 알려주신 서버와 클라이언트 소스를 바로 가져와 사용하면 되는건가요?

  18. Blog Icon
    권혁

    저기 제가 좀 많이 초보자라서 그러는데 이거 ip주소랑 포트번호를 어떻게 설정해 줘야되나요?? 지금 스마트폰이랑 테스트 중인데 노트북을 스마트폰 핫스팟에 연결하고 ipconfig 쳐서 ip 주소까진 다 됫는데 나머지는 잘 모르겟네요;;;

  19. Blog Icon
    빈경진

    너무좋은글 감사합니다!!

  20. Blog Icon
    안녕하세요

    스마트폰 으로 pc 마우스포인터를 제어하는 앱을 만들고있습니다.

    서버는 자바 어플리케이션
    클라이언트는 안드로이드 앱

    지금 앱으로 pc포인터를 조종하는대 까지 햇는데
    문제가 이클립스프로그램으로 만든 서버 어플리케이션을 어떻게 배포해야하는지를 모르겠습니다.
    저는 자바공부하고 안드로이드 스튜디오공부만해서
    이클립스에 하나도 몰라서.. 도대체 어떻게 배포가 가능한지 조언을 구하고싶습니다. 꾸벅

  21. Blog Icon

    비밀댓글입니다

  22. Blog Icon
    김진환

    안드로이드에서 안돌아가여...ㅠ
    setContentView(R.layout.main);이부분의 main과
    StrictMode.setThreadPolicy(policy);이 부분의
    setThreadPolicy 가 붉은색으로 뜨네요 ㅠ