UDP(User Datagram Protocol)는 TCP/IP 프로토콜의 4계층인 통신계층 (transport Layer)에서 사용되는 통신 규약이다. 통신계층은 송신자와 수신자의 데이터 전달을 담당하는 계층으로 크게 TCP와 UDP로 나뉜다.
오늘은 UDP 통신의 특성을 알아보고 안드로이드 스튜디오 (Java)를 통해서 cilent와 server의 UDP통신을 구현해보자.
UDP는 client와 server 간에 datagram의 신뢰할 수 없는 전송을 제공한다.
*신뢰할 수 없는 전송이란 분실이 일어날 수 있음을 의미한다.
UDP의 특성을 고려하여 Android 어플로 UDP통신을 하는 client와 server를 구현해보자
먼저 client socket과 server socket을 만든다. socket에는 상대방의 IP주소나 포트넘버가 필요없다. 대신 datagram을 보내려고 할 때 목적하는 곳의 IP주소, 포트넘버를 datagram에 언급해야 한다.
datagram을 생성해서 상대방의 IP주소와 포트넘버를 담고 UDP socket을 통해서 datagram을 보낸다.
서버는 datagram을 받고 client의 IP주소와 포트넘버를 client가 보낸 datagram으로 부터 추출할 수 있다. 읽어들인 datagram의 소스 IP주소, 소스 포트넘버는 client의 IP주소, 포트넘버가 된다.
서버는 echo msg를 서버소켓을 통해서 내보낸다.: 1. 데이터그램을 만든다. 2. 추출한 client의 ip주소와 포트넘버를 datagram에 담아서 내보낸다
서버가 보낸 datagram을 클라이언트가 받아서 화면에 뿌려주고 socket을 끝낸다.
먼저 인터넷을 사용해야 하기 때문에 client와 server 모두 manifest에 permission을 해준다.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="UDP Client"
android:layout_gravity="center"
android:textSize="30dp"/>
<EditText
android:id="@+id/editIP"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="ip주소를 입력하시오."/>
<EditText
android:id="@+id/editMsg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="보낼 메세지를 입력 하시오."/>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="메세지 전송"/>
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="서버로 부터 받은 메세지 :"/>
</LinearLayout>
MainActivity.java
package com.example.udpclienttest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class MainActivity extends AppCompatActivity {
TextView textView;
EditText editIP, editMsg;
String inputMsg;
private int CLIENT_PORT = 17777;
private int SERVER_PORT = 18888;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
editIP = (EditText) findViewById(R.id.editIP);
editMsg = (EditText) findViewById(R.id.editMsg);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("VR", "Click OK");
startClient();
}
});
}
// 10.0.2.16
public void startClient() {
// 안드로이드에서 네트워킹은 thread 이용이 필수 이다.
new Thread(new Runnable() {
@Override
public void run() {
try {
// 1. Client Socket 생성
DatagramSocket clientSocket = new DatagramSocket(CLIENT_PORT);
Log.d("VR", "Client Socket Created");
inputMsg = editMsg.getText().toString(); // 서버에 보낼 메세지
InetAddress inetAddress = InetAddress.getByName(editIP.getText().toString()); // InetAddress 객체에 입력받은 ip 담기
// 2. datagram 생성 - server IP 와 server 포트넘버 포함
DatagramPacket clientPacket = new DatagramPacket(inputMsg.getBytes(), inputMsg.length(), inetAddress, SERVER_PORT);
// UDPSocket 을 통해서 datagram 을 내보낸다.
clientSocket.send(clientPacket);
Log.d("VR", "Packet Send");
byte[] receiveData =new byte[256]; // server 가 보내는 data 를 받을 객체 생성
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
clientSocket.receive(receivePacket);
final String receive_data = new String(receivePacket.getData(),0 , receivePacket.getLength());
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("서버로 부터 받은 메세지 : " + receive_data);
}
});
// 10.0.2.15
} catch (SocketException e) {
Log.e("VR", "Sender SocketException");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="UDP Server"
android:textSize="30dp"
android:gravity="center" />
<TextView
android:id="@+id/serverIp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="IP Adrress : "
android:textSize="20dp"/>
<TextView
android:id="@+id/clientMsg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="클라이언트로 부터 받은 메세지 : "
android:textSize="15dp"/>
</LinearLayout>
MainActivity.java
// server
public class MainActivity extends AppCompatActivity {
TextView serverIp, clientMsg;
private int SERVER_PORT = 18888;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serverIp = (TextView) findViewById(R.id.serverIp);
clientMsg = (TextView) findViewById(R.id.clientMsg);
serverIp.setText(getIpAddress()); // ip 가져오기
Thread serverThread = new Thread(new serverThread());
serverThread.start();
}
class serverThread extends Thread {
@Override
public void run() {
try {
// server socket 생성
DatagramSocket serverSocket = new DatagramSocket(SERVER_PORT);
Log.d("VR", "Sever Socket Created");
// client메세지를 받을 배열 선언
byte[] receiveMsg = new byte[256];
// server datagram 생성
DatagramPacket serverPacket = new DatagramPacket(receiveMsg, receiveMsg.length);
// 2.
serverSocket.receive(serverPacket);
final String receive_data = new String(serverPacket.getData(),0 , serverPacket.getLength());
MainActivity.this.runOnUiThread(new Runnable() {
@SuppressLint("SetTextI18n")
@Override
public void run() {
clientMsg.setText("클라이언트로 부터 받은 메세지 : " + receive_data);
}
}
);
final String EchoMsg = getIpAddress() + " sends UDP echo message " + "'" + receive_data +"'";
int port = serverPacket.getPort();
DatagramPacket send_packet = new DatagramPacket(EchoMsg.getBytes(), EchoMsg.length(),serverPacket.getAddress(), port);
serverSocket.send(send_packet);
Log.d("VR", "Packet Send");
} catch (Exception e) {
e.printStackTrace();
}
}
}
private String getIpAddress() {
String ip = "";
try {
Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface.getNetworkInterfaces();
while (enumNetworkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = enumNetworkInterfaces.nextElement();
Enumeration<InetAddress> enumInetAddress = networkInterface.getInetAddresses();
while (enumInetAddress.hasMoreElements()) {
InetAddress inetAddress = enumInetAddress.nextElement();
if (inetAddress.isSiteLocalAddress()) {
ip += "IP address: " + inetAddress.getHostAddress() + "\n";
}
}
}
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ip += "Something Wrong! " + e.toString() + "\n";
}
return ip;
}
}