소켓 사용하기
IP 주소로 목적지 호스트를 찾아내고 포트로 통신 접속점을 찾아내는 소켓 연결은
TCP와 UDP 방식으로 나눌 수 있다. 하지만 일반적으로 프로그래밍에서는 대부분
TCP 연결을 사용한다.
HTTP 프로토콜과 소켓
HTTP 프로토콜은 소켓으로 웹 서버에 연결한 후 요청을 전송하고 응답을 받은 후
연결을 끊는다. 이런 특성을 비연결성 이라고 하는데 이러한 특성 때문에 실시간으로
데이터를 처리하는 앱은 속도를 높이기 위해 연결성이 있는 소켓 연결을 선호 했다고 한다.
하지만 지금은 인터넷 속도가 빨라져 HTTP 프로토콜을 사용하는 웹이 일반적이 되었고
결국 속도가 그렇게 느리지 않으면서도 국제 표준을 따르는 장점을 가진 많은 웹서버가
만들어지게 되었다.
예제
안드로이는 소켓 연결 등을 시도하거나 응답을 받아 처리할 때 반드시 스레드를 사용해야한다.
이전에는 권장 사항이었으나 현재 플랫폼에서는 강제사항으로 바뀌었으므로 스레드를
사용하지 않으면 네트워킹 기능이 동작하지 않는다.
일단 아래와 같이 화면을 구성하자.
그 후 아래와 같이 코드를 작성하자.
MainActivity.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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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((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((v) -> {
new Thread(new Runnable() {
@Override
public void run() { startServer(); }
}).start();
});
}
public void send(String data){
try{
int portNumber = 5001;
Socket sock = new Socket("localhost", portNumber);
printClientLog("소켓 연결함.");
/* 소켓 객체로 데이터 보내기 */
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(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
handler.post(new Runnable() {
@Override
public void run() {
textView.append(data + "\n");
}
});
}
public void printServerLog(final String data){
Log.d("MainActivity",data);
// 서버 쪽 로그를 텍스트뷰에 출력하기 위한 Handler
handler.post(new Runnable(){
@Override
public void run(){
textView2.append(data+"\n");
}
});
}
}
여기서 ObjectInputStream 과 ObjectStream을 사용했지만 실제 앱을 만들때는 잘 사용하지 않는다.
이유는 위 두 클래스는 자바의 객체 정보를 편리하게 주고 받을 수 있도록 만들어진 클래스이다.
자바가 아닌 다른 언어로 만들어진 서버와 통신할 경우에는 데이터 송수신이 정상적으로 이루어지지 않을 수 있다.
인제 위의 코드를 작성 했으면 INTERNET 권한을 추가해 주자.
AndroidManifest.xml
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.techtown.socket">
<uses-permission android:name="android.permission.INTERNET"/>
...
결과