한컴퓨터에서 서버와 클라이언트를 켜서 통신하는것과 외부 인터넷을 잡은 컴퓨터에서 서버에 접속하는 프로그램

<id>를 입력하고 뒤에 문자열을 적으면 해당 클라이언트의 아이디가 된다.


▲왼)서버 오)클라이언트  한컴퓨터에서 동시에 돌리는 영상 + 38초 쯤에 다른 클라이언트 접속


▲외부 인터넷을 잡은 클라이언트 영상


클라이언트는 서버보다 간단하다.

단지 서버의 ip와 port를 알고 있다면 Socket 클래스를 이용하여 객체를 생성하자마자 연결되기 때문이다.

 

1
2
3
Socket socket = new Socket(id address, iPort);
InputStream InputStream = socket.getInputStream();
OutputStream OutputStream = socket.getOutputStream();

cs

 


서버 때와 마찬가지로 Thread를 사용해야 입출력을 동시에 할 수 있으니 그것을 구현해야 한다.


Diagram을 참조하여 프로그램을 만든다.


서버소켓을 만들 때 필요한 건 ServerSocket 클래스 이다.

ServerSocket 클래스의 생성자 중 Port를 파라미터로 사용하는 생성자가 있는데 이를 사용하여 객체를 생성한다.


그리고 ServerSocket에 있는 accept 메소드를 이용하여 클라이언트가 접근할 때 까지 대기한다.

접속을 했을 경우 생기는 Socket을 반환하여 그것을 통해 입출력을 얻어 사용하면 된다.


이때 쓰레드로 구현하지 않을 경우 단순하게 서버와 클라이언트 간의 1:1 접속이 되지만 쓰레드로 구현할 경우 멀티 소켓 프로그램이 되는 것 이다. 하지만 Socket을 받았을 때 이것을 Thread없이 돌릴경우 입력 또는 출력만 할 수 있게되므로 Thread를 구현하여 입출력을 받을 수 있게 해야한다.


 

1
2
3
4
5
ServerSocket serverSocket = new ServerSocket(m_iPort);
 
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
cs


stream은 byte타입으로 read(),write(byte[] obj)를 이용하여 데이터를 송수신한다.

집에서 서버를 열 때 공유기를 사용하면 외부에서 바로 연결하지 못하고 공유기에서 포트를 열어줘야 한다.

1. 고정아이피 설정해주기

유동아이피 일경우 특정 아이피에 포트를 열어주더라도 나중에 IP가 변경되어 포트를 다시열어줘야 되는 경우가 생겨 장금해준다.

사용하는 공유기마다 다르지만 ipTime에서는 고급설정-네트워크 관리-내부 네트워크 설정 으로 들어간다.


2. 포트포워드 설정하기

고정한 IP주소를 적고 외부에서 접속할 때 사용하는 포트와 내부에서 접속할 때 사용할 포트를 기제후 추가한다.


첨부파일

TCPView 컴퓨터에서 사용하고 있는 TCP/UDP 포트를 보여주는 프로그램

https://technet.microsoft.com/ko-kr/library/bb897437.aspx


TCPView.zip



SocketServerManager : SocketDataTransfer와 SocketServerMake를 관리하는 클래스.

SocketDataTrnasfer : 서버와 클라이언트 간의 데이터 송수신을 하는 클래스.

SocketServerMake : 할당 받은 포트를 통해 Socket을 열어주는 클래스.

사용한 툴 : 안드로이드 스튜디오

사용 최소 버전 : API 18


TTS란

Text To Speech 의 약자이며 텍스트를 음성으로 읽어주는 기능을 말한다.

사용할 땐 클래스 내부에 TextToSpeech 객체를 생성하고, 객체 생성시 이벤트를 처리할 TextToSpeech.OnInitListener를 implement 한다.

객체 생성 까지 약간의 시간이 걸리며 이 시간동안은 TTS기능을 사용 할 수 없다.

객체 생성이 끝나면 내부 설정(TTS 엔진, 언어, 음성 속도 같은 set 계열과 get 계열) 메소드를 사용 할 수 있다.

자세한 것은 아래 링크에서 참고한다.


http://developer.android.com/reference/android/speech/tts/TextToSpeech.html#QUEUE_ADD

TTS 를 사용하기 전 디바이스에서 사용 할 수 있도록 설정해주어야 한다.

설정은 아래 그림을 참고하면 된다 (API22기준)


사용하기 전 TTS 설치 및 설정



Simple Code

디바이스에 한국어 설정이 되있다고 가정했을 때,

어플을 실행하고 잠시뒤 'TTS기능 테스트 중'이라고 말한다.


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener{
 
    private TextToSpeech ttsClient;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_etcbeacon);
 
        ttsClient= new TextToSpeech(getApplicationContext(),this);
    }
    @Override
    public void onInit(int i) {
        ttsClient.speak("TTS기능 테스트 중.",TextToSpeech.QUEUE_FLUSH,null);
    }
 
}
 
cs

▲MainActivity.java

Java AWT는 이전 포스트에서 말했다 싶히 Window에서 제공하는 껍데기를 가져다가 쓰는 것이다.
때문에 버튼을 누르거나 폼창을 닫으려고 해도 아무런 행동을 하지 않는다.
그것을 추가할려면 어떤 Event가 있는지 감지하는 Listener에 등록해야 특정행동을 하도록 할 수 있다.
Listener : 특정 위젯이 Event가 발생했는지 계속 듣고 있는 것

특정 리스너를 등록하고 싶다면 interface를 implement한 뒤 interface에서 제공하는 add메소드로 이벤트를 받을 위젯을 추가한다.
이벤트가 발생했을 경우 Listener interface에서 제공하는 이벤트 처리기 메소드가 호출 되는데 매개변수를 이용하여 이벤트가 어디서 일어났는지 알 수 있다.



▲이벤트 이름에 따른 Listener interface, add 메소드와 이벤트 처리기 메소드 이름


아래 코드는 TextField와 Button에 리스너를 등록하여 이벤트를 처리하는 프로그램이다.

동작 설명

종료 Button을 누르면 Form이 꺼짐
TextField에 값이 바뀌면 TextField에 글이 아래에 있는 TextArea에 한줄씩 추가되고,
엔터를 누르면 위에 있는 Label의 글이 바뀜.


1
2
3
4
5
6
7
8
public class EventMain {
 
    public static void main(String[] args) {
        MyEvent myEvnet = new MyEvent();
        myEvnet.startFrame();
    }
 
}
cs
▲EventMain.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Label;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;
 
public class MyEvent implements ActionListener,TextListener{
    private Frame mainFrame;
    private Button btnExit;
    private Label lblOutput;
    private TextField tfInput;
    private TextArea taOutList;
    public MyEvent() { //AWT 구성에 필요한 객체를 생성
        mainFrame = new Frame("객체와 리스너 실습");
        btnExit = new Button("종료");
        lblOutput= new Label("리스너가 감지한 이벤트 : ");
        tfInput = new TextField(30);
        tfInput.setText("천 / 당 / 속 / 서 / 제");
        taOutList = new TextArea("천\n속",5,50);
    }
 
    public void startFrame(){//컴포넌트 객체를 프레임에 추가 + 리스너 등록
        mainFrame.setLayout(new FlowLayout());
        mainFrame.add(lblOutput);
        mainFrame.add(tfInput);
        mainFrame.add(taOutList);
        mainFrame.add(btnExit);
        mainFrame.setSize(400300);
        mainFrame.setVisible(true);
        
        //Listener 
        btnExit.addActionListener(this);
        tfInput.addActionListener(this);
        tfInput.addTextListener(this);
        
    }
    
    @Override
    public void actionPerformed(ActionEvent e) { //액션 리스너의 핸들러 + 필요작업 작성
        String strTemp =e.getActionCommand();
        if(strTemp.equals("종료"))
        {
            System.exit(0);
        }
        else{
            lblOutput.setText(strTemp);
        }
    }
 
    @Override
    public void textValueChanged(TextEvent e) { // 텍스트 리스너의 핸들러 + 필요작업 작성
        taOutList.append("\ntextField 내용 : " +  tfInput.getText());
    }
}
 
cs

 

▲MyEvent.java


13 : ActionEvent와 TextEvent를 사용하기 때문에 각각의 interface를 implement 함.

14~18 : 사용할 위젯, Frame 변수를 선언함

19~26 : MyEvent가 객체로 생성될 때 각 위젯과 Frame도 객체로 생성.

29~35 : mainFrame에 Layout 설정과 위젯 추가, 사이즈, Visible 설정을 함

38~40 : btnExit와 tfInput를 add메소드를 이용하여 리스너 등록

44~54 : ActionEvent가 발생했을 때 이벤트 처리

57~59 : TextEvent가 발생했을 때 이벤트 처리


java awt(Abstract Window Toolkit)란?
Window에서 제공하는 툴을 자바에 가져와 GUI를 구성하는 도구이다.
단순하게 외형만 가져오는 것 이기 때문에 사용자가 리스너를 이용하여 이벤트 처리를 하지 않을 경우 아무런 기능이 없다.

화면을 구성하는 방법은 두가지가 있는데 Frame 객체를 생성해서 add 메소드로 프레임을 꾸미는 것과
클래스가 Frame을 상속받아서 클래스 자체를 Frame으로 사용하는 방법이 있다.


▲AWT를 이용한 화면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.awt.*;
 
public class JavaAWT{
    Frame m_frame;
    Label m_label;
    Button m_btn0;
    TextField m_tf0;
    TextArea m_ta0;
//    CheckboxGroup cbg;
//    Scrollbar vSlider, hSlider;
//    Canvas imagCanvas;
    /**
     * @param args
     */
    public JavaAWT()
    {
        m_frame= new Frame("JavaAWT Sample");
        m_label= new Label("Label sample");
        m_btn0 = new Button("Button0");
        
        m_tf0 = new TextField(30);
        m_tf0.setText("It is TextArea");
        m_ta0=new TextArea("천안\n속초",3,20);
    }
    private void startFrame(){
        m_frame.setLayout(new FlowLayout());
        m_frame.add(m_btn0);
        m_frame.add(m_label);
        m_frame.add(m_tf0);
        m_frame.add(m_ta0);
        m_frame.setSize(500300);
        m_frame.setVisible(true);
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        JavaAWT myFirstJavaWindow = new JavaAWT();
        myFirstJavaWindow.startFrame();
    }
 
}
 
cs

 

▲클래스에서 Frame'객체를 생성'하여 실행

 

4~8 : 화면구성에 사용될 위젯들과 Frame 변수들 선언

17~23 : 각 위젯과 Frame 객체 생성

26 : m_frame의 Layout을 FlowLayout으로 설정 (FlowLayout은 왼쪽 위부터 차례대로 위젯들을 삽입하는 레이아웃이다.)

27~30 : 생성한 위젯을 m_frame에 넣음

31 : m_frame의 form 사이즈 설정

32 : m_frame을 Visible로 설정(보이도록 설정. show()와 같은 기능을 한다.)


▲AWT 화면 구성

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 
import java.awt.*;
 
 
public class JavaAWT2 extends Frame{
    CheckboxGroup cbGroup;
    Scrollbar vSlider, hSlider;
    Canvas imageCanvas;
    public JavaAWT2(){
        super("JavaAWT sample(checkbox, radiogroup)");
        setLayout(new FlowLayout());
        
        add(new Checkbox("천안"));
        add(new Checkbox("당진"));
        add(new Checkbox("속초"));
        add(new Checkbox("서울"));
        add(new Checkbox("제주"));
        cbGroup = new CheckboxGroup();
        add(new Checkbox("천안",cbGroup,false));
        add(new Checkbox("당진",cbGroup,false));
        add(new Checkbox("속초",cbGroup,false));
        add(new Checkbox("서울",cbGroup,false));
        add(new Checkbox("제주",cbGroup,false));
        
        List list = new List(3false);
        list.add("천안");
        list.add("당진");
        list.add("속초");
        list.add("서울");
        list.add("제주");
        add(list);
        
        Choice choice = new Choice();
        choice.add("천안");
        choice.add("당진");
        choice.add("속초");
        choice.add("서울");
        choice.add("제주");
        add(choice);
        
        imageCanvas = new Canvas();
        imageCanvas.setBackground(Color.YELLOW);
        imageCanvas.setSize(200200);
        
        add(imageCanvas);
        
        vSlider=new Scrollbar(Scrollbar.VERTICAL,0,1,0,100);
        hSlider=new Scrollbar(Scrollbar.HORIZONTAL,0,4,0,50);
        add(vSlider);
        add(hSlider);
        
        setSize(500,500);
        setVisible(true);
    }
    
    public static void main(String[] args) {
        JavaAWT2 myWindow = new JavaAWT2();
    }
 
}
 
cs

▲클래스가 Frame을 '상속'받아서 사용한 코드


사용한 툴 : 안드로이드 스튜디오

사용 버전 : API18~22


핸들러란 메인스레드에 무리를 안주기 위해 있는 것으로, 서브 스레드에서 메인 스레드로 접근할 때 직접 접근하지 않고 핸들러를 이용해서 보내야 한다.

핸들러에 메시지를 보내면 핸들러의 handleMessage(Message) 메소드로 오게되는데  Message 안에는 what으로 어떤 값(정수값)이 왔는지 확인 할 수 있다.


액티비티에 핸들러를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private final Handler m_Handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what){
            case MESSAGE_SELECTING: //미리 위에서 public static final int로 설정
 
                selectBeacon();
                break;
            case MESSAGE_SCANNING:
                tvState.setText("스캐닝 중...");
                centralManager.startScanning();
                break;
            case MESSAGE_SERVERSTATE:
                String strTemp = msg.getData().getString("Result");
                if(strTemp.equals("1"))
                    tvState.setText("출석체크 완료");
                else
                    tvState.setText("서버에 전송 오류");
        }
    }
};
 
cs

 

▲ ScanConnActivity.java


 

1
2
3
Message msg;
msg = mHandler.obtainMessage(ScanConnActivity.MESSAGE_SELECTING);
mHandler.sendMessage(msg);
cs

 

▲ 스레드 상속받은 다른 클래스


핸들러에 메시지를 보낼 때 값을 같이 보내고 싶다면 Bundle로 값을 추가하거나 Message내에 arg1(int),arg2(int), obj(Object) 변수에 넣으면 된다.

 

1
2
3
4
5
6
Bundle bundle = new Bundle();
bundle.putString("Result",isOk);
msg = mHandler.obtainMessage(ScanConnActivity.MESSAGE_SERVERSTATE);
msg.setData(bundle);
mHandler.sendMessage(msg);
 
cs

 ▲ 스레드 상속받은 다른 클래스


사용한 툴 : 안드로이드 스튜디오

사용 버전 : API 22

*주의 : 2015.11.19 기준 API23 버전에서는 org.apache.http.client.HttpClient를 지원하지 않으니 주의해야 한다.*

안드로이드 스튜디오는 간혈적으로 org.apache.http.client.HttpClient 라이브러리를 import하지 못한다.

따라서 개발자가 Gradle Scripts -  build.grade (Module : app) 에 있는 컴파일러에 직접 명시 해주어야 한다.

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.0'
compile 'org.apache.httpcomponents:httpclient:4.5' <- 추가
}

▲build.grade 맨하단의 dependencies


서버로 요청하는 행동은 인터넷을 사용하기 때문에 android.permmision.INTERNET 퍼미션을 AndroidManifest.xml추가해야한다.

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

안드로이드에서 서버로 POST 보내는 순서

1. HttpClient 오브젝트 생성 

2. HttpPost 오브젝트 생성

3. POST 파라미터 추가

4. POST 데이터 엔코더

5. HTTP POST 생성 요청

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void sendJsonDataToServer(){
    
    String URL = "http://toybox.iptime.org:18080/test";
    //1
    HttpClient httpClient = new DefaultHttpClient();
    //2
    HttpPost httpPost = new HttpPost(URL);
    //3
    List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
    nameValuePairs.add(new BasicNameValuePair("ID",m_strId));
    nameValuePairs.add(new BasicNameValuePair("BeaconMAC",m_strbeaconMac));
    try {
        //4
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        //5
        HttpResponse response = httpClient.execute(httpPost);
        // write response to log
        Log.d("Http Post Response:", response.toString());
    } catch (UnsupportedEncodingException e)    {
        e.printStackTrace();
    } catch (ClientProtocolException e) {
        // Log exception
        e.printStackTrace();
    } catch (IOException e) {
        // Log exception
        e.printStackTrace();
    }
}
 
cs

 

혹시 코드를 메인스레드에서 돌렸을 때 예외가 발생한다면 서브 스레드를 통해 값을 전송하면 된다.

(메인스레드가 무거워지는 것을 방지하려고 예외를 발생시킴)


+ Recent posts