**Socket
=> 네트워크 인터페이스 카드를 추상화한 클래스
=> 통신 방식
- 저수준 통신 : Scoket을 직접 생성하여 통신(보편적으로 의미하는 Socket 통신)
- 고수준 통신 : Socket을 직접 생성하지 않고 추상화된 클래스를 이용
=> 저수준 통신이 효율은 좋으나 사용하기 어려움
1. java.net.InetAddress
=> 인터넷 주소와 관련된 클래스
=> 생성자는 없고, static메소드를 이용하여 인스턴스를 생성
- getLocalHost, getByName, getAllByName을 이용하여 생성
- 예외 처리를 강제
2. java.net.Socket 클래스
1) 생성자
- Socket()
- Socket(InetAddress addr, int port)
- Socket(String addr, int port)
- Socket(InetAddress addr, int port, InetAddress localAddr, int localPort)
=> 앞의 2개는 접속할 상대방의 주소정보, 뒤의 2개는 자신의 주소정보 - Network Interface card가 2개 이상
=> 상대방의 주소를 설정하여 생성시 자동으로 접속
- addr이 잘못되면 NullPointerException이 발생
- port번호가 잘못되면 illegalArgumentException이 발생
2) 입출력을 위한 스트림을 제공하는 메소드
- InputStream getInputStream()
- OutputStream getOutputStream()
3) UDP와 TCP
=> UDP : 비 연결형 통신 : 데이터를 일방적으로 보내기만 하는 방식
- 효율은 좋으나 신뢰성이 떨어짐(APNS, FCM(스마트폰 알림메시지)
=> TCP : 연결형 통신 : 서로 연결하고 대화를 주고받는 방식으로 통신
- 효율은 UDP에 비해 떨어지지만 신뢰성이 높음
=> java에서는 일반 소켓가 ServerSocket을 이용하면 TCP통신이고, DatagramSocket을 이용하면 UDP통신
**Android와 PC Application TCP Socket통신
1. java Project
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
//포트번호 설정
//1024번 이하, 8080, 1521, 3306은 제외하고 설정
int portNumber = 11000;
System.out.println("서버 실행중...");
//서버 소켓 생성 - 접속 준비
ServerSocket ss = new ServerSocket(portNumber);
while(true) {
//클라이언트 접속 대기 여기서 블럭 되어 있다가
//클라이언트가 접속시 클라이언트와 통신할 수 있는 소켓을 리턴하고
//아래 문장을 실행
Socket socket = ss.accept();
//클라이언트 정보 출력
System.out.println(socket.getInetAddress() + " : " + socket.getLocalPort());
//클라이언트와 문자 통신을 위한 스트림 생성
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//한 줄 읽어오기
String msg = br.readLine();
System.out.println(msg);
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//한줄 메시지 전송
pw.write("서버가 보내는 메시지");
pw.flush();
//소켓 종
}
}catch (Exception e) {
// TODO: handle exception
}
}
}
2. Android Application
1) 프로젝트 생성
2) 화면 디자인
=> 전송할 문자열을 입력받을 EditText 1개와 전송받은 문자열을 출력할 TextView 1개와 전송 버튼 1개를 디자인
<?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">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/send"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/receive"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="전송"
android:id="@+id/btn"/>
</LinearLayout>
3) Activity에 소스 작성
package com.example.android0722;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
//뷰의 참조를 저장하기 위한 변수
EditText send;
TextView receive;
Button btn;
//네트워크 사용을 위한 스레드
//스레드는 한 번 사용하면 새로 생성해야 사용이 가능합니다.
//
class ThreadEx extends Thread{
@Override
public void run(){
String content = null;
try{
//서버에 접속하는 소켓 생성
Socket socket =
new Socket(
"192.168.0.200",
11000);
//스트림 생성
ObjectOutputStream oos =
new ObjectOutputStream(
socket.getOutputStream()
);
oos.writeObject(send.getText().toString());
oos.flush();
ObjectInputStream ois =
new ObjectInputStream(
socket.getInputStream()
);
content = (String)ois.readObject();
//스레드에서는 UI갱신을 못함
//receive.setText(content);
socket.close();
}catch(Exception e){
Log.e("전송 에러", e.getMessage());
}
//핸들러에게 전송할 메시지 생성
Message msg = new Message();
msg.obj = content;
//핸들러 호출
handler.sendMessage(msg);
}
};
//스레드가 전송한 내용을 출력하기 위한 핸들러
Handler handler = new Handler(
Looper.getMainLooper()){
@Override
public void handleMessage(Message msg){
String temp = (String)msg.obj;
receive.setText(temp);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
send = (EditText)findViewById(R.id.send);
receive = (TextView)findViewById(R.id.receive);
btn = (Button)findViewById(R.id.btn);
//버튼의 클릭 이벤트 처리
btn.setOnClickListener(
new Button.OnClickListener(){
public void onClick(View view){
//스레드 시작
new ThreadEx().start();
}
});
}
}
4) 안드로이드에서는 네트워크를 사용하기 위한 권한 설정
=> INTERNET
<uses-permission android:name="android.permission.INTERNET"/>
**URL 통신
=> 웹의 주소를 이용하는 통신 방식
=> http 나 https 프로토콜를 이용하는 통신 방식
=> 스마트 폰은 보안 문제 때문에 http는 추가 설정을 해야만 통신이 가능
1. URL 클래스
=> 접속할 주소를 만들어주는 클래스
1) 생성자
- URL(String addr) : new URL(“https://www.daum.net/”)
- URL(String protocol, String host, int port, String file) : new URL(“https”, “www.daum.net”, 443, “/“);
2) 메소드
=> get으로 시작하는 메소드를 이용해서 각 부분의 정보를 리턴받을 수 있음
3) 주의할 점
=> 잘못된 URL을 대입하면 MalformedURLException이 발생
=> URL은 전부 인코딩 된 상태로 생성해야 함
- URL에 영문이나 숫자 이외의 데이터가 있다면 인코딩을 해 주어야 함
- URLEncoder.encode(변환할 문자열, “utf-8”)
2. URLConnection
=> URL에 접속하여 서버에게 데이터를 전송하고 읽어올수 있는 클래스
1) 객체 생성
- (실제 사용할 클래스)URL객체.openConnection();
=> 실제 사용할 클래스는 HttpURLConnection, HttpsURLConnection, JarURLConnection
2) 옵션을 설정하는 메소드
=> setConnectTimeout(int timeout) : 접속이 안될때 최대 시도시간으로 미설정시 무제한
=> setUseCache(boolean newValue) : 변화가 거의 없는 경우, 브라우저의 캐시처럼 데이터를 로컬에 저장, 다시 불러오기 위한 옵션
=> post 방식의 파라미터나 헤더를 추가하는 메소드
- setRequestProperty(String attr, String value)
- addRequestProperty(String attr, String value)
=> 전송방식 선택 : setRequestMethod("GET" or "POST") : default는 GET
=> 요청 결과 리턴 : int getResponseCode()
3) Stream을 리턴해주는 메소드
- InputStream getInputStream()
- OutputStream getOutputStream()
4) http 통신의 문제
=> 보안이 취약하기 때문에 기본적으로 접속이 안되도록 되어 있음
=> 안드로이드에서는 application 설정에 android:usesClearTraffic="true"를 추가해야 함
**텍스트를 읽어서 출력하기
1. 실행가능한 Activity 추가
2. 화면 디자인
=> 버튼 1개를 배치하고, HTML을 출력할 TextView를 배치
- html이 TextView의 크기를 넘어가면 스크롤 할 수 있도록 TextView를 ScrollView안에 배치
=> WebView, ListView(데이터 출력을 위한 뷰), MapView는 ScrollView를 상속받아 자체적으로 스크롤이 가능
- 그 외의 View는 스크롤 기능이 없으므로 내용이 많은 경우 스크롤 뷰안에 배치해야 함
<?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=".TextDownloadActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="다운로드"
android:id="@+id/btndownload"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="HTML 출력"
android:id="@+id/display"/>
</ScrollView>
</LinearLayout>
3. Activity.java 파일 작성
public class TextDownloadActivity extends AppCompatActivity {
private TextView display;
private Button download;
//데이터를 다운로드 받을 클래스
//Thread는 재사용이 안되므로 클래스로 만들어서
//필요할 때 마다 객체를 생성해서 사용
class ThreadEx extends Thread{
public void run(){
//다운로드 받은 문자열을 저장할 변수
String html = null;
try{
//다운로드 받을 주소 생성
URL url = new URL("https://www.google.com");
//연결 객체를 생성하고 옵션을 설정
HttpURLConnection con =
(HttpURLConnection)url.openConnection();
con.setUseCaches(false);
con.setConnectTimeout(30000);
con.setRequestMethod("GET");
//필요한 스트림을 생성해서 읽기
//문자열을 읽을 스트림 생성
BufferedReader br =
new BufferedReader(
new InputStreamReader(
con.getInputStream()));
StringBuilder sb = new StringBuilder();
while(true){
//한 줄 읽기
String line = br.readLine();
//읽은 내용이 없으면 종료
if(line == null){
break;
}
//읽은 내용이 있으면 sb에 추가
sb.append(line + "\n");
}
html = sb.toString();
//생성한 객체 닫기
br.close();
con.disconnect();
//UI 갱신 할 거라면 Handler를 호출해서
//데이터를 넘겨주기
Message message = new Message();
message.obj = html;
handler.sendMessage(message);
}catch(Exception e){
Log.e("다운로드 에러", e.getMessage());
}
}
}
//데이터를 출력하기 위한 객체
//재사용이 가능하므로 하나의 객체를 바로 생성해서 사용
Handler handler =
new Handler(Looper.getMainLooper()){
public void handleMessage(Message message){
//데이터 가져오기
String html = (String)message.obj;
display.setText(html);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_download);
display = (TextView)findViewById(R.id.display);
download = (Button)findViewById(R.id.download);
download.setOnClickListener(
new Button.OnClickListener() {
@Override
public void onClick(View view) {
new ThreadEx().start();
}
});
}
}
4. AndroidManifest.xml 파일에서 권한 확인
=> INTERNET 권한이 있어야 하고, 접속하는 곳이 http로 시작하는 경우 application에 android:usesCleartextTraffic="true" 추가
**데이터 다운로드 받아서 출력할 때 주의사항
=> 권한 설정하지 않으면 permit관련 에러
=> 다운로드 받는 코드를 Thread에 작성하지 않으면 앱은 Crash
=> UI 갱신을 스레드에서 직접 하면 아무일도 발생하지 않음
**이미지 파일 다운로드
=> 파일을 다운로드 받는 것은 BufferedReader 대신 BufferedInputStream을 사용하여 읽어온 후 파일에 기록
=> 파일은 계속 다운로드 받지 않고 대부분의 경우 처음 접속시만 다운로드 받아 저장 후 저장데이터를 확인하여 없을 경우만 다운로드
=> 파일이 업데이트 될 가능성이 있다면 서버를 만들때 업데이트 날짜를 기록해야하고, 앱은 접속시 업데이트 날짜 확인
- 서버의 업데이트 날짜가 더 클경우 다운로드, 같을 경우 로컬 파일 사용
=> 안드로이드 앱은 자신의 데이터 디렉토리에만 파일을 저장할 수 있음
- /data/data/패키지명/files 디렉토리가 자신의 데이터를 저장할 수 있는 디렉토리
- 맨 앞의 /data대신 Environment.getDataDirectory().getAbsolutePath()를 이용해서도 생성 가능
**이미지 파일을 다운받아서 바로 ImageView에 출력하는 것과 파일로 저장하고 출력하기
1. 실행 가능한 Activity 생성
2. 레이아웃 수정
=> 버튼 2개, 이미지뷰 1개
<?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=".ImageActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="이미지 바로 출력"
android:id="@+id/btndrawimage"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="이미지 저장 후 출력"
android:id="@+id/btnsaveimage"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/imageview"/>
</LinearLayout>
3.
TIP!
1. GET, POST
1) GET : 파라미터를 URL에 포함하여 전송
- 보안이 취약하고, 전송하는 크기에 제한이 있음
2) POST : 파라미터를 header에 숨겨서 전송
- 보안이 우수, 전송하는 크기에 제한이 없음
- 인코딩을 잘 설정해야하고, 속도는 GET에 비해 느림,
- 최근에는 FORM의 데이터는 되도록 POST로 전송하는 것을 권장
- Password, textarea, file이 존재하는 경우 반드시 POST
2. 응답코드
- 200번대 : 정상 응답
- 300번대 : 리다이렉트 중
- 400번대 : 클라이언트 오류(URL 에러나 권한이 없거나 하는 등의 에러)
- 500번대 : 서버 오류(서버의 로직이 잘못 수행되는 경우)
'수업 정리' 카테고리의 다른 글
76~78일차 수업정리(MySQL&Hibernate Project) (0) | 2020.07.23 |
---|---|
75~76일차 수업 정리(Oracle&MyBatis) (0) | 2020.07.23 |
74일차 수업 정리(Android - Thread) (0) | 2020.07.21 |
73일차 수업 정리(Android - 파일 입출력, SQLite) (0) | 2020.07.20 |
72일차 수업정리(Android - Timer, Animation, LandScope) (0) | 2020.07.17 |