Android

안드로이드 자바 TCP IP ( 소켓 통신 )

Machine_웅 2018. 6. 1. 21:47
728x90
반응형

* 저도  공부중인 부분이라, 자세히는 모르지만 일단 소스가 동작하고

방이 나눠지는 것을 확인 했습니다.

 

 

 

순서

 

서버 ( 자바 이클립스 )

 

1) 접속할 포트와 소켓을 열어준다.  ( 무한 반복 )

 

2) 접속한 소캣의 정보를 저장해둔다.

 

3) 저장 한 소캣의 입력 출력 스트림을 열어준다   ( 위의 무한 반복 안에 생성 )

( 메세지 송 수신 관련 스트림입니다.  DataInpuStream 과 DataOutputStream 를 사용합니다.    다른것도 사용할 수 있습니다.  )

 

4) 메세지를 수신할 스레드를 작성한다.

 

5) 수신한 메세지를 처리하는 로직을 짠다  ( ex 방나누기  1:1 채팅 그룹채팅 등을 구분할수 있는 구분자들 )

 

6) 보내고 싶은 상대방 쪽스트림에다가 데이터를 전송한다.

 

----------------------------------------------------------------------------------------------------------------------------------------

 

클라이언트쪽

 

1)   소캣 객체를 만든다

 

Socket socket = new Socket(host,port);

host에는 목적지의 주소 port에는 목적지가 열어놓은 포트번호  ( 여기서 목적지는 서버쪽이 된다 )

 

 

발생 예외 )

- UnknownHostException : host로 서버를 찾을 수 없을 때

- IOException : 네트워크 문제로 소켓을 생성할 수 없을 때

- SecurityException : 보안 매니저에 의해 연결이 허용되지 않을 때

 

 

 

------------------------------------------------------------------------------------------------------------------------------------

 

 

 

자바 ( 서버 코드 )

 

 

 

public class chatSever {
 
 // 접속자 정보를 저장할 리스트
 ArrayList<user_data> clients;
 user_data uData;
 
 // 서버 소캣 ( 서비스용 소켓 )
 private ServerSocket severSocket = null;
 
 
 public chatSever() {
  // 연결부에서 리스트 생성
  clients = new ArrayList<>();
  
  // clients 동기화
  Collections.synchronizedList(clients);
 }
 
 public static void main(String[] args) {
  new chatSever().start(); // 쓰레드의 시작
 }
 
 // ( 접속 ) 메인함수에서 시작하는 메소드
 private void start() {
  // 서버에서 열어주는 포트 번호  ( 랜덤 값 가능 )
  int port = 9999;
  Socket socket = null;
  
  try {
   // 서버 소캣을 생성후  while문으로 진입 하여 accept(대기)
   // 접속시 ip 주소를 획득하고 출려한뒤 MultiThread를 실행한다.
   
   // 서버 소캣을 생성
   severSocket = new ServerSocket(port);
   System.out.println("서버 소캣 생성 : 접속 대기중");
   while(true) {
    socket = severSocket.accept(); //접속 대기중   ( 대기하고 있다가 들어오면 소켓을 만들고 )
    
    // 클라에서 접속하게 되면 통신회선이 만들어지면서  데이터를 주고 받을 수 있다.
    
   /* // ip 주소 얻기
    InetAddress ip = socket.getInetAddress();
    System.out.println(ip+ " 접속 ");*/
    
    
    // 쓰레드에서 객체를 주고받을 Stream 생성자를 선언한다. ( 생성자 )
    // run에서  접속한 주소를 받아와 출력하고 클라이언트에 정보를 넘겨주고 clients에게 ip 주소 mac 주소를 보낸다 .
    
    // 사용자가 들어올때마다 쓰레드를 생성및 실행 
    //( 서버의 성능이 좋아야겟지  100명이 들어오면 쓰레드가 100개가 돌태니까 )
    new MultiThread(socket).start(); // MultiThread 의 run()실행
    
   }
   
   
  } catch (Exception e) {
   e.printStackTrace();
  }
  
 } //  start() 끝
 
 // 내부클래스 ( 사용자가 접속할때마다  쓰레드를 생성해주는 클래스 )
 class MultiThread extends Thread{
  
  Socket socket = null;
  String msg = null;
  String mac = null;
  DataInputStream in;
  DataOutputStream out;
  
  public MultiThread ( Socket socket) {
   this.socket = socket;
   
   // 데이터 입출력을 위한 스트림 실행
   try {
    in = new DataInputStream(socket.getInputStream());
    out = new DataOutputStream(socket.getOutputStream());
    
   }catch (Exception e) {
    e.printStackTrace();
   }
  }
  
  @Override
  public void run() {
   
   try {
    // 처음에 한번만 받을건데 이때 정보를 가지고 오자 !
    mac = in.readUTF();  //수신 ( 클라가 글을 쓰면 서버로 온다 )   클라 - 서버 - 클라
    
    // 초기에 한번만 접속자의 정보를 받는것.
    System.out.println("접속자 정보들  : "+mac);
    
    // 리스트에 추가 ( 클라이언트의 정보를 저장 아이디를 key로 쓰자 )
    String [] filter;
    filter = mac.split("@");
    
    // ( 기존 방에 있는 아이디가 있는지 탐색후  없을시 저장한다 )
    
    //클라이언트의 정보를 만들어서 리스트에 추가  
    uData = new user_data(socket,Integer.parseInt(filter[0]),filter[1],out);
    clients.add(uData);
    
    // 접속자 확인
    System.out.println(filter[1]+"님이"+filter[0]+"번 방에 입장하셨습니다.");
    
    // 메세지 보내기 ( 클라 쪽으로 방에 접속했다라는 메세지인데 , 나중에 단체 채팅일 경우에  써 보자 );
    // **** sendMsg( mac + "접속");
    
    
    while (in != null) {
     try {
      String temp = in.readUTF(); //수신된 메세지 
      sendMsg(temp);
      System.out.println("MultiThread 의 런 "+ temp);
     } catch (Exception e) {
      e.printStackTrace();
     }
    }
    
   } catch (Exception e) {
    e.printStackTrace();
   }
  }//run 의 끝
  
  
  // 서버가  클라이언트에게 보낸다 .  ( 지금은 다보내고 있는데, 그룹인 경우 구분자를 줘서 한다 )
  void sendMsg ( String msg) {
   
   // 방번호, 보낸사람, 받는 사람, 메세지
   String [] filt1 =  msg.split("@");

     // 메세지 전송시 날짜
      SimpleDateFormat mFormat = new SimpleDateFormat("aa hh:mm");

   
   // 클라이언트들의 정보를 모은 집합
   for ( int i = 0 ; i < clients.size() ; i++) {
    try {
     //  클라이언트들의 정보를 모은 집합에서 같은 방에 있고  자신이 아닌 상대방에게 보내는 경우에   메세지를  보낸다 .
     // 방번호가 같고
     if ( Integer.parseInt(filt1[0]) == clients.get(i).roomNo ) {
      
      // 본인이 아닌 모든 사람에게 보낸다.
      if( !filt1[1].equals(clients.get(i).Userid)) {
       // 클라이언트의 OutputStream 을 저장
       OutputStream  dos = clients.get(i).output;
       DataOutputStream out = new DataOutputStream(dos);
       
                      // 현재 시간 받아오기
                      long mNow;
                      Date mDate;
                      mNow = System.currentTimeMillis();
                      mDate = new Date(mNow);

                      String time = mFormat.format(mDate);
       
       // 클라이언트에 메세지를 전송  ( 보낸 사람과, 내용 )
       out.writeUTF(filt1[1]+"@"+filt1[3]+"@"+time);
      }
      
     }
     
    } catch (Exception e) {
     e.printStackTrace();
    }
   }
  } // sendMsg 끝
 }  // MultiThread 끝

}

 

 

 

 

 

 

 

------------------------------------------------------------------------------------------------------------------------------------

 

user_data  클래스

 

 

public class user_data {
 
 Socket userSocket;
 int roomNo;
 String Userid;
 DataOutputStream output;
 
 public user_data(Socket userSocket,int roomNo, String UserId, DataOutputStream output) {
  
  this.userSocket = userSocket;
  this.roomNo = roomNo;
  this.Userid = UserId;
  this.output = output;
  
 }

}

 

 

 

------------------------------------------------------------------------------------------------------------------------------------

 

 

안드로이드

 

 

< 수신 핸들러 >

/ 서버로부터 수신한 메세지를 처리하는 곳  ( AsyncTesk를  써도됨 )
msgHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 1111){
// 메세지가 왔다면.
Toast.makeText(context, "메세지 : "+msg.obj.toString(), Toast.LENGTH_SHORT).show();
Log.d("받은 메세지 ",msg.obj.toString());

msgFilter = msg.obj.toString().split("@");


// 수신 1
messageContent = new chattingMessageContent(1, msgFilter[0], targetNickName, msgFilter[1], msgFilter[2]);

messageData.add(messageContent);
chattingRoomAdapter.notifyDataSetChanged();

}
}
};

 

 

 

< 메세지 보내기 이벤트 >

 

// 채팅 입력 이벤트
public void setSendBtn() {
ImageView enterRoomChattingSend = (ImageView) findViewById(R.id.enterRoomChattingSend);

enterRoomChattingSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EditText enterRoomChattingEditText = (EditText) findViewById(R.id.enterRoomChattingEditText);
String message = enterRoomChattingEditText.getText().toString();

if (message == null || TextUtils.isEmpty(message) || message.equals("")) {
Toast.makeText(chattingRoom.this, "메세지를 입력해주세요", Toast.LENGTH_SHORT).show();
} else {
int mode = 2;
String senderId = loginUserId;
String senderNick = loginUserNick;

// 현재 시간 받아오기
long mNow;
Date mDate;
mNow = System.currentTimeMillis();
mDate = new Date(mNow);

String time = mFormat.format(mDate);

messageContent = null;
messageContent = new chattingMessageContent(mode, senderId, senderNick, message, time);

messageData.add(messageContent);
chattingRoomAdapter.notifyDataSetChanged();

// 메세지 보내주기
send = new SendThread(socket,message);
send.start();

// 에디트 텍스트 비워주기
enterRoomChattingEditText.setText(null);

// 키보드 내려주기
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(enterRoomChattingEditText.getWindowToken(),0);

Toast.makeText(chattingRoom.this, "전송", Toast.LENGTH_SHORT).show();

}
}
});
}

 

 

< 서버에 접속 >

 

// 내부클래스   ( 접속용 )
class SocketClient extends Thread{

DataInputStream in = null;
DataOutputStream out = null;
String roomAndUserData; // 방 정보 ( 방번호 / 접속자 아이디 )

public SocketClient(String roomAndUserData){
this.roomAndUserData = roomAndUserData;
}


public void run(){
try {
// 채팅 서버에 접속 ( 연결 ) ( 서버쪽 ip와 포트 )
socket = new Socket(ipad2,port);

// 메세지를 서버에 전달 할 수 있는 통로 ( 만들기 )
output = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());

// 서버에 초기 데이터 전송 ( 방번호와 접속자 아이디가 담겨서 간다 ) - 식별자 역할을 하게 될 거임.
output.writeUTF(roomAndUserData);

// (메세지 수신용 쓰레드 생성 ) 리시브 쓰레드 시작
recevie = new ReceiveThread(socket);
recevie.start();

}catch (Exception e){
e.printStackTrace();
}
}


} //SocketClient의 끝

 

 

< 메세지 전송 >

 


// 내부 클래스 ( 메세지 전송용 )
class SendThread extends Thread{
Socket socket;
String sendmsg;
DataOutputStream output;


public SendThread(Socket socket, String sendmsg ){
this.socket = socket;
this.sendmsg =sendmsg;
try {
// 채팅 서버로 메세지를 보내기 위한 스트림 생성.
output = new DataOutputStream(socket.getOutputStream());

}catch (Exception e){
e.printStackTrace();
}

}

// 서버로 메세지 전송 ( 이클립스 서버단에서 temp 로 전달이 된다.
public void run(){
try {
if ( output != null){
if (sendmsg != null){

// 여기서 방번호와 상대방 아이디 까지 해서 보내줘야 할거같다 .
// 서버로 메세지 전송하는 부분
output.writeUTF(roomNo+"@"+loginUserId+"@"+targetId+"@"+sendmsg);
}
}

}catch (Exception e){
e.printStackTrace();
}
}
}

 

 

< 메세지  수신 >

 

// ( 메세지 수신용 )   -  서버로부터 받아서, 핸들러에서 처리하도록 할 거.
class ReceiveThread extends Thread{

Socket socket = null;
DataInputStream input = null;

public ReceiveThread(Socket socket) {
this.socket = socket;

try {
// 채팅 서버로부터 메세지를 받기 위한 스트림 생성.
input = new DataInputStream(socket.getInputStream());
}catch (Exception e){
e.printStackTrace();
}
}

@Override
public void run() {
try {
while (input != null){
// 채팅 서버로 부터 받은 메세지
String msg =input.readUTF();

if (msg != null){
// 핸들러에게 전달할 메세지 객체
Message hdmg = msgHandler.obtainMessage();

// 핸들러에게 전달할 메세지의 식별자
hdmg.what = 1111;

// 메세지의 본문
hdmg.obj = msg;

// 핸들러에게 메세지 전달 ( 화면 처리 )
msgHandler.sendMessage(hdmg);
}
}
}catch (Exception e){
e.printStackTrace();
}
}

}


 

 

 

 

 

 

 

 

728x90
반응형