2012. 8. 3. 22:56

[출처 :http://blog.naver.com/khs7515?Redirect=Log&logNo=20155866934 ] 




Service란 액티비티와는 반대로 눈에 보이지 않는 작업 단위를 처리하는 객체이다.

   Thread와 다른점은 Thread는 Activity와 생명주기를 함께 하지만 Service는 스스로의 생명주기를 가지고 있기 때문에

   따로 종료해주지 않는 이상 앱을 종료해도 살아있다.


# 본 예제는 시작 버튼을 누르면 서비스를 상속받은 페이지로 이동해서 Thread를 실행시키고 Thread가 10초 단위로 5개의 메세지를 

   순서대로 handler에 보낸다. handler는 받은 메세지를 Notification(알림)을 통해서 출력한다.


*************************************** MainActivity.java ******************************************


package test.day10.service;


/*

 * [ 안드로이드 4대요소 ]

 *  

 * 1. Activity - 실체 사용자를 대면하고 입력을 받는 것

 * 2. Service - 레이아웃이 없는데 뭔가 작업한다. 감시를 한다던가..(manifest등록해야한다)

 * 3. Alarm 

 * 4. ContentProvider - 콘텐츠 공급 객체(다른 어플의 자원, 사진, 동영상, 음악을 읽어올때)

 * 

 */

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;


public class MainActivity extends Activity {

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

    }

    //서비스 시작 시키기

    public void start(View v){

     //인텐트 객체 생성

     Intent intent = new Intent(this, MyService.class);

     //서비스 시작시키기

     startService(intent);

    

    }

    //서비스 종료 시키기

    public void end(View v){

     //인텐트 객체 생성

     Intent intent = new Intent(this, MyService.class);

     //서비스 종료시키기

     stopService(intent);

    }

}


*************************************** MyService.java ******************************************


package test.day10.service;



import android.app.Notification;

import android.app.NotificationManager;

import android.app.PendingIntent;

import android.app.Service;

import android.content.Context;

import android.content.Intent;

import android.os.Handler;

import android.os.IBinder;

import android.os.Message;

import android.widget.Toast;

//manifest에 등록해야함.

public class MyService extends Service{


//알림을 띄울 것이므로 

NotificationManager notiManager;

//thread

ServiceThread thread;

//알림을 중복을 피하기 위한 상태값

final int MyNoti = 0;

@Override

public IBinder onBind(Intent intent) {

//할일 없음

return null;

}

//서비스가 시작되면 onstartcommand가 호출된다. 

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

/*

 * 서비스에서 수행할 작업을 수행시키고 재빨리 리턴한다.

 * 시간이 오래 걸리면 서비스가 취소된다.

 * 액티비티는 생명주기가 있지만 서비스는 생명주기가 없다(메모리가 부족하지 않다면 계속 백그라운드에 떠있다.)

 * ex. 카카오톡의 경우 새로운 메시지가 오는지 지속적으로 관찰하는 작업

 */

notiManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

//백그라운드에서 작업할 내용을 초기화 하고 시작한다.

thread = new ServiceThread(handler);

//스레드 시작하기

thread.start();

return START_STICKY;

}

//서비스가 종료될 때 할 작업

public void onDestroy() {

//스레드 종료시키기

thread.stopForever();

thread=null;//쓰레기 값을 만들어서 빠르게 회수하라고 null을 넣어줌.

}

//백그라운드 스레드로부터 메세지를 받을 핸들러 객체 

Handler handler = new Handler(){

public void handleMessage(android.os.Message msg) {

//what으로 메시지를 보고 obj로 메시지를 받는다.(형변환필수)

//notification 객체 생성(상단바에 보여질 아이콘, 메세지, 도착시간 정의)

     Notification noti = new Notification(R.drawable.icon//알림창에 띄울 아이콘

     , "메시지 도착!", //간단 메세지

     System.currentTimeMillis()); //도착 시간

     //기본으로 지정된 소리를 내기 위해

     noti.defaults = Notification.DEFAULT_SOUND;

     //알림 소리를 한번만 내도록

     noti.flags = Notification.FLAG_ONLY_ALERT_ONCE;

     //확인하면 자동으로 알림이 제거 되도록

     noti.flags = Notification.FLAG_AUTO_CANCEL;

     //사용자가 알람을 확인하고 클릭했을때 새로운 액티비티를 시작할 인텐트 객체

     Intent intent = new Intent(MyService.this, MainActivity.class);

     //새로운 태스크(Task) 상에서 실행되도록(보통은 태스크1에 쌓이지만 태스크2를 만들어서 전혀 다른 실행으로 관리한다)

     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

     //인텐트 객체를 포장해서 전달할 인텐트 전달자 객체

     PendingIntent pendingI = PendingIntent.getActivity(MyService.this, 0, intent, 0);

     //받아온 메시지 담기

     String arrivedMsg = (String)msg.obj;

     //상단바를 드래그 했을때 보여질 내용 정의하기

     noti.setLatestEventInfo(MyService.this, "[새 메세지]", arrivedMsg, pendingI);

     //알림창 띄우기(알림이 여러개일수도 있으니 알림을 구별할 상수값, 여러개라면 상수값을 달리 줘야 한다.)

     notiManager.notify(MyNoti, noti);

     //토스트 띄우기

     Toast.makeText(MyService.this, arrivedMsg, 0).show();

    

}

};


}



*************************************** ServiceThread.java ******************************************


package test.day10.service;


import android.os.Handler;

import android.os.Message;

/*

 * 서비스에서 사용할 스레드 클래스

 */

public class ServiceThread extends Thread{

//서비스 객체의 핸들러

Handler handler;

boolean isRun = true;

//예제로 서비스할 메세지

String msg[] = {"나는 서비스 입니다.", "액티비티와 상관없이 살아있습니다.", "메세지1", "메세지2", "메세지3"};

//생성자

public ServiceThread(Handler handler){

this.handler = handler;

}

//스레드 정지 시키는 메소드

public void stopForever(){

synchronized (this) {

this.isRun = false;

notify();

}

}

int index;

//스레드 본체

public void run(){

//반복적으로 수행할 작업을 한다.

while(isRun){

//핸들러로 들어오는 메시지별로 다르게 동작.

String message = msg[index];

index++;

//핸들러에 전달할 Message 객체 생성하기

Message m = new Message();

if(index==4) index = 0;

if(index==3) {

m.what = 1;

m.obj = message;

}else{

m.what = 0;

m.obj = message;

}

//핸들러에 메세지를 보낸다.

handler.sendMessage(m);

try{

Thread.sleep(10000); //10초씩 쉰다.

}catch (Exception e) {}

}

}

}



*************************************** main.xml ******************************************


<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

<TextView  

    android:layout_width="fill_parent" 

    android:layout_height="wrap_content" 

    android:text="서비스 테스트"

    />

    <Button android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:text="서비스 시작"

    android:onClick="start"/>

    <Button android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:text="서비스 종료"

    android:onClick="end"/>

</LinearLayout>



*************************************** AndroidManifest.xml ******************************************



<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

      package="test.day10.service"

      android:versionCode="1"

      android:versionName="1.0">

    <uses-sdk android:minSdkVersion="8" />


    <application android:icon="@drawable/icon" android:label="@string/app_name">

        <activity android:name=".MainActivity"

                  android:label="@string/app_name">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

<service android:name=".MyServiceandroid:enabled="true"/>

    </application>

</manifest>

Posted by k1rha
2012. 8. 3. 22:52

서비스를 띄우고 액티비티가 종료시 다시 띄울때 서비스를 체크해 줘야 한다.


private Boolean isServiceRunning(String serviceName) {

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo runningServiceInfo : activityManager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceName.equals(runningServiceInfo.service.getClassName())) {
return true;
}
}
return false;


Posted by k1rha
2012. 8. 3. 22:48


 public void MessageCloass() {

// mValue++;
// mText.setText(Integer.toString(mValue));

mNotiManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

Notification noti = new Notification(R.drawable.ic_launcher,
"링크허브 메시지", System.currentTimeMillis());
noti.defaults |= Notification.DEFAULT_SOUND;
// 진동 사용
noti.defaults |= (Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
// 커스텀 진동
noti.vibrate = new long[] { 1000, 1000, 500, 500, 200, 200, 200, 200,
200, 200 };
noti.flags |= Notification.FLAG_INSISTENT;

Intent intent = new Intent(linkhub_main.this, linkhub_main.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent content = PendingIntent.getActivity(linkhub_main.this, 0,
intent, 0);
noti.setLatestEventInfo(linkhub_main.this, "링크허브 메시지", "메시지가 도착하였습니다.",content);

mNotiManager.notify(linkhub_main.NAPNOTI, noti);








 public void MessageCloass() {

// mValue++;
// mText.setText(Integer.toString(mValue));

mNotiManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

Notification noti = new Notification(R.drawable.ic_launcher,
"링크허브 메시지", System.currentTimeMillis());
noti.defaults |= Notification.DEFAULT_SOUND;
// 진동 사용
noti.defaults |= (Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
// 커스텀 진동
noti.vibrate = new long[] { 1000, 1000, 500, 500, 200, 200, 200, 200,
200, 200 };
noti.flags |= Notification.FLAG_INSISTENT;

//Intent intent = new Intent();
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// PendingIntent contentIntent = PendingIntent.getActivity(linkhub_main.this, 0, new Intent(this,linkhub_main.class),0);

// PendingIntent content = PendingIntent.getActivity(linkhub_main.this, 0, intent,0);
// startService(intent);
//noti.setLatestEventInfo(context, contentTitle, contentText, contentIntent)
noti.setLatestEventInfo(linkhub_main.this, "링크허브 메시지", "메시지가 도착하였습니다.", pendingIntent());
mNotiManager.notify(linkhub_main.NAPNOTI, noti);
}
public PendingIntent pendingIntent() {
Intent i = new Intent(getApplicationContext(), linkhub_main.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
cancelNotivication();
return pi;
}
public void cancelNotivication(){
mNotiManager.cancelAll();
}

Posted by k1rha
2012. 8. 3. 22:41

package tae.NetworkClient;
 
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import android.util.Log;
 
public class NetworkUDP {
    private static final String serverIP = "10.0.2.2"; //serverIP를 추가합니다.
    private static final int port = 8000; //서버에서 설정한 UDP 포트번호를 추가합니다.
    private String msg;
    private String return_msg;
         
    public NetworkUDP(String _msg) {
        this.msg = _msg;
    }
     
    public String run() {
        try {
            DatagramSocket socket = new DatagramSocket();
             
            InetAddress serverAddr = InetAddress.getByName(serverIP);
             
//TCP와 다르게 UDP는 byte단위로 데이터를 전송합니다. 그래서 byte를 생성해줍니다.
            byte[] buf = new byte[128];
             
            //받아온 msg를 바이트 단위로 변경합니다.
            buf = msg.getBytes();
             
            //DatagramPacket를 이용하여 서버에 접속합니다.
            DatagramPacket Packet = new DatagramPacket(buf, buf.length, serverAddr, port);
            Log.d("UDP", "sendpacket.... " + new String(buf));
            socket.send(Packet);
            Log.d("UDP", "send....");
            Log.d("UDP", "Done.");
 
            socket.receive(Packet);
            Log.d("UDP", "Receive" + new String(Packet.getData()));
 
            //데이터를 받아와 return_msg에 복사합니다.
            return_msg = new String(Packet.getData());
 
        } catch (Exception ex) {
            Log.d("UDP", "C: Error", ex);
            }
        return return_msg;
       }
}

Posted by k1rha
2012. 8. 2. 15:35

SO ListView 라이브러리 - 1.0 입니다.

안드로이드 개발 할 때Custom listView 를 간편하게 구현 할 수 있게 랩핑된 라이브러리 입니다.

사용법 문의및 추가 기능이 필요하신 분들은 댓글 남겨주시기 바랍니다.


[출처 : http://iapptocompany.tistory.com/3 ]


SOListViewLibSample.zip


엄청 편하게 잘되어있다. 

Posted by k1rha
2012. 7. 30. 20:39

Thread 에서 소켓 통신을 하고, 그 값을 받아 올때마다  UI를 변경 시켜 주고싶을때, MainActivity를 그대로 넘겨버릴 수도 있지만, 그렇게 되는 경우 Activity의 전환시 그 통신을 계속 할수가 없다. 


Thread 를 다시 생성하여 그 Activity를 넘겨줘야 하기 때문이다. 

이런경우 우리는 singletone pattern 과 Handler를 이용하여 완벽히 해결 할 수 있다. 


우선 Handler 란? 

Thread 에서 UI 같은 Main Thread 의 값을 변경해 주기 위해서 MainThread 의 역할을 잠깐 위임해 주는 것이다.

C#에서는 delegate가 비슷한 역할을 한다. 


사용법은 아래의 출처 예제코드가 가장 간단하고 이해하기 편하다.

http://www.cyworld.com/aspire1127/5581351


사용을 예로들면 

우선 MainActivity 에 Handler 클래스를 선언해 준다.

Override 를 통해 HandlerMessage를 재정의해준다. 


MainAcitivity 의 위 선언부

  public Handler hMain = new Handler() {

@Override

public void handleMessage(Message msg) {

IntentMainToGame(); //MainActivity의 함수이다.

}

};


MainAcitivity->IntentMainToGame()

  public void IntentMainToGame() {

 Toast toast = Toast.makeText(getBaseContext(),"회원가입 완료",Toast.LENGTH_SHORT);

 toast.setGravity(Gravity.CENTER,0, 0);

 toast.show();

Log.e("-------------------------", "thread");

Intent intent = new Intent(MainActivity.this,GameActivity.class);

 startActivity(intent);

}



그리고 MainActivity에서 Thread 를 생성시에 이 핸들러를 넘겨준다.

MainAcitivity 내 thread 호출시..

  mScoketClient.startServer(hMain);




Thread 안에 Thread 시에도 이 핸들러 값만 이어서 가져가 주면된다.

private Handler mHandle=null;
       public void startServer(Handler _handle) {
this.mHandle = _handle;
}



이후에 이 핸들러의 sendMessage를 통하여 인자값을 전하고 원하는 메소드를 호출 시킬수 있다.
Thread 안 메시지 로 sendMessage 호출

 Message msg = Message.obtain(mHandle,0,0);
mHandle.sendMessage(msg);




Posted by k1rha
2012. 7. 27. 13:51

아.. 출처가 기억나질 않는다. 

일단 아래와 같은 방식으로 DB연결하고 값을 불러오는데 성공.

테이블이 이미 존재할때 가능한 코드이다. 
"select * from test";  는 test 테이블에 있는 모든값을 가져온다는 구문이고 안에는 컬럼이 2개 존재한다.



 void Database(){

char* query="select * from test"; // 실행할 쿼리

int len;

MYSQL* conn_ptr; // MySQL과의 연결을 담당

MYSQL_RES* res; // 쿼리에 대한 결과를 받는 변수

MYSQL_ROW row; // 쿼리에 대한 실제 데이터 값이 들어있는 변수

conn_ptr=mysql_init(NULL); // 초기화


if(!conn_ptr){

DisplayText("mysql_init failed!!\n");

// printf("mysql_init failed!!\n");

}

// MySQL에 연결

conn_ptr=mysql_real_connect(conn_ptr, HOST, USER, PASS, NAME, 3306, (char*)NULL, 0);

if(conn_ptr){

DisplayText("연결 성공\n");

// printf("연결 성공\n");

}else{

DisplayText("연결 실패\n");

//printf("연결 실패\n");

}

// 쿼리 실행

// mysql_query() 실행 후 반환값이 0이어야 성공

len=mysql_query(conn_ptr, query);

res=mysql_store_result(conn_ptr); // 전속한 결과 값을 MYSQL_RES 변수에 저장

// 쿼리 결과 값 출력

while((row=mysql_fetch_row(res))!=NULL){ // 한 ROW 씩 얻어 온다

DisplayText("%s %s\n", row[0], row[1]);

//printf("%s %s\n", row[0], row[1]); // 결과 값 출력

}

// 할당 된 메모리 해제

mysql_free_result(res); 

mysql_close(conn_ptr);


//scanf("%d",len);

}



Posted by k1rha
2012. 7. 26. 22:06

IOCP 서버 구현 



#include <stdio.h>

#include <stdlib.h>

#include <winsock2.h>

#include "resource.h"


using namespace std;


#define WM_MYSOCKET_NOTIFY (WM_APP+0)  //WSAAsyncSelect에 쓰일 네트워크 이벤트 발생시 Post될 메시지

#define MAXLINE 1024


BOOL CALLBACK ClientDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);  //클라이언트 다이얼로그 프로시져

BOOL CALLBACK ConnectDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);  //연결 다이얼로그 프로시져

void OnConnect();  //연결 함수

void OnMySocketNotify(WPARAM wParam,LPARAM lParam);  //SEND,WRITE,CLOSE 발생시 수행하는 함수

void Disconnect();  //연결 해제 함수

void DisplayText(char *fmt,...);  //화면에 출력 함수


HINSTANCE hInst;

HWND hClientDlg,hEditContent,hEditSendMsg,hBTSend,hBTConnect,hBTDisconnect;

char IPAddr[20]="127.0.0.1";

int PortNum=5056;

SOCKET ClientSocket;

char Buffer[MAXLINE];  //메시지 저장 변수


int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nCmdShow) {

hInst=hInstance;

//윈속 초기화

WSADATA WsaData;

if(WSAStartup(MAKEWORD(2,2),&WsaData)!=0) {

MessageBox(NULL,"윈속 초기화 실패!","ERROR",MB_OK);

return 0;

}

//다이얼로그 생성 및 실행

DialogBox(hInst,MAKEINTRESOURCE(IDD_CLIENT_DLG),NULL,ClientDlgProc);

//윈속 종료

closesocket(ClientSocket);

WSACleanup();

return 0;

}

//클라이언트 다이얼로그 프로시져

BOOL CALLBACK ClientDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) {

switch(uMsg) {

case WM_INITDIALOG:

hClientDlg=hDlg;

hEditContent=GetDlgItem(hDlg,IDC_EDIT_CONTENT);

hEditSendMsg=GetDlgItem(hDlg,IDC_EDIT_SENDMSG);

hBTSend=GetDlgItem(hDlg,IDC_BT_SEND);

hBTConnect=GetDlgItem(hDlg,IDC_BT_CONNECT);

hBTDisconnect=GetDlgItem(hDlg,IDC_BT_DISCONNECT);

return TRUE;

case WM_COMMAND:

switch(LOWORD(wParam)) {

case IDC_BT_CONNECT:

DialogBox(hInst,MAKEINTRESOURCE(IDD_CONNECT_DLG),NULL,ConnectDlgProc);

break;

case IDC_BT_DISCONNECT:

Disconnect();

break;

case IDC_BT_SEND:

GetDlgItemText(hDlg,IDC_EDIT_SENDMSG,Buffer,MAXLINE-1);

DisplayText("[SEND MSG] %s\n",Buffer);

SendMessage(hDlg,WM_MYSOCKET_NOTIFY,ClientSocket,WSAMAKESELECTREPLY(FD_WRITE,0));

SetDlgItemText(hDlg,IDC_EDIT_SENDMSG,"");

break;

}

return TRUE;

case WM_MYSOCKET_NOTIFY:

OnMySocketNotify(wParam,lParam);

return TRUE;

case WM_CLOSE:

EndDialog(hDlg,0);

return TRUE;

}

return FALSE;

}

//연결 다이얼로그 프로시져

BOOL CALLBACK ConnectDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) {

switch(uMsg) {

case WM_INITDIALOG:

SetDlgItemText(hDlg,IDC_IPADDRESS,IPAddr);

SetDlgItemInt(hDlg,IDC_EDIT_PORTNUM,PortNum,FALSE);

return TRUE;

case WM_COMMAND:

switch(LOWORD(wParam)) {

case IDC_BT_CONNETOK:

GetDlgItemText(hDlg,IDC_IPADDRESS,IPAddr,sizeof(IPAddr));

PortNum=GetDlgItemInt(hDlg,IDC_EDIT_PORTNUM,NULL,FALSE);

SendMessage(hDlg,WM_CLOSE,0,0);

//서버에 접속

OnConnect();

break;

}

return TRUE;

case WM_CLOSE:

EndDialog(hDlg,0);

return TRUE;

}

return FALSE;

}

//연결 함수

void OnConnect() {

//Socket 생성

ClientSocket=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);

if(ClientSocket==INVALID_SOCKET) {

DisplayText("[ERROR] Socket 생성 실패!\n");

return;

}else DisplayText("[SUCCESS] Socket 생성 성공!\n");

//주소 정보 설정

SOCKADDR_IN ServerAddr;

ZeroMemory(&ServerAddr,sizeof(SOCKADDR_IN));

ServerAddr.sin_family=AF_INET;

ServerAddr.sin_addr.s_addr=inet_addr(IPAddr);

ServerAddr.sin_port=htons(PortNum);

DisplayText("서버에 접속중.....\n");

//Connect

if(connect(ClientSocket,(PSOCKADDR)&ServerAddr,sizeof(SOCKADDR_IN))==SOCKET_ERROR) {

DisplayText("[ERROR] Connect 실패!\n");

closesocket(ClientSocket);

return;

}else DisplayText("[SUCCESS] Connect 성공!\n");

//WSAAsyncSelect 실행

if(WSAAsyncSelect(ClientSocket,hClientDlg,WM_MYSOCKET_NOTIFY,FD_READ|FD_WRITE|FD_CLOSE)==SOCKET_ERROR) {

DisplayText("[ERROR] WSAAsyncSelect 실패!\n");

closesocket(ClientSocket);

return;

}else DisplayText("[SUCCESS] WSAAsyncSelect 성공!\n");

//버튼 상태 변경

EnableWindow(hEditSendMsg,TRUE);

EnableWindow(hBTSend,TRUE);

EnableWindow(hBTDisconnect,TRUE);

EnableWindow(hBTConnect,FALSE);


DisplayText("Connect!!!\n");

}

//SEND,WRITE,CLOSE 발생시 수행하는 함수

void OnMySocketNotify(WPARAM wParam,LPARAM lParam) {

int nEvent=WSAGETSELECTEVENT(lParam);

if(nEvent==FD_READ) {

ZeroMemory(Buffer,sizeof(Buffer));

recv(ClientSocket,Buffer,sizeof(Buffer),0);

DisplayText("[RECV MSG] %s\n",Buffer);

}

else if(nEvent==FD_WRITE) {

send(ClientSocket,Buffer,strlen(Buffer),0);

}

else if(nEvent==FD_CLOSE) {

Disconnect();

}

}

//연결 해제 함수

void Disconnect() {

//소켓 닫기

closesocket(ClientSocket);

//버튼 상태 변경

EnableWindow(hEditSendMsg,FALSE);

EnableWindow(hBTSend,FALSE);

EnableWindow(hBTDisconnect,FALSE);

EnableWindow(hBTConnect,TRUE);

DisplayText("서버와 연결이 끊어짐.\n");

}

//화면에 출력 함수

void DisplayText(char *fmt,...) {

va_list arg;

va_start(arg,fmt);

char cbuf[MAXLINE+256];

vsprintf(cbuf,fmt,arg);

int nLength=GetWindowTextLength(hEditContent);

SendMessage(hEditContent,EM_SETSEL,nLength,nLength);

SendMessage(hEditContent,EM_REPLACESEL,FALSE,(LPARAM)cbuf);

va_end(arg);

}

Posted by k1rha
2012. 7. 26. 15:39

멤버십 과제중 프로토콜 맞추기가 너무 시간이 오래 걸림에 있어 JSON 규약으로 통신을 맞추기로 했다.

게임 개발자의 편의를 맞추기 위해 JSON 방식을 좀더 사용하기 편하게 객체화 시켜 보았다. 


이 포스팅은 오로지 필자가 개인용도로 만든 것이기 때문에, 다른 사용자에게는 오히려 불편할 수 있음을 미리 전한다.

Main Activity


        JsonHandler json = new JsonHandler();

        json.putJsonInit();  //json 초기화 부분.. 값을 입력하기전 반드시 해야한다.

        

        json.putJson("key1","test1");  //key 값과 value 값을 이어서 필요할때마다 추가해 줄수 있다.

        json.putJson("key2","test2");

        json.putJson("key3","test3");

        

        String aaa = json.putJsonFinish();   //obj 를 다만들었으면 그것을 array에 추가하는 부분이다. 필요에 따라서 여러개의 오브젝트를 배열로 추가해주는 방식으로 변경할 수 있다.

        Log.e("check"," JSON FINAL STRING "+aaa);  //JSON ARRAY 전체 문자열 출력 

      

        json.getJsonInit(aaa);   //JSON array 안에 obj 형식으로 들어오는 JSON 만 파싱 가능하다.

       

        Log.e("result value",":::: resutl : "+json.getJson("key1"));  //getjson 으로 값을 가져 올 수 있다.

        Log.e("result value",":::: resut2 : "+json.getJson("key2"));






JsonHandler.java

 package JsonHandler;


import org.json.JSONArray;

import org.json.JSONException;

import org.json.JSONObject;

import android.util.Log;


public class JsonHandler {

String json_string = null;

String[] jsonGet = new String[20];


String json_ex = "[{\"key01\":\"aaaa\",\"key02\":\"bbbb\"}]";


JSONArray ParseJsonArray = null;

JSONObject ParsejsonRoot = null; // 0번째 라인...다중 배열시엔 for문

JSONArray combineJsonArray = null;

JSONObject combineJsonObject = null;



public JsonHandler() {

ParseJsonArray = null;

ParsejsonRoot = null;

combineJsonArray = null;

combineJsonObject = null;

}

/*JSON 에서 파싱된 값을 가져오기 위한 메소드 */



public void getJsonInit(String _jsonValue) {

try {

ParseJsonArray = new JSONArray(_jsonValue);

ParsejsonRoot = ParseJsonArray.getJSONObject(0);

} catch (JSONException e) {

e.printStackTrace();

}


}


public String getJson(String _key) {

String result=null;

try {

result = ParsejsonRoot.getString(_key);

//소켓 메소드 쪽으로 호출 

} catch (JSONException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return result;

}



/*JSON 으로  만들기 관련된 메소드들 */

public void putJsonInit(){

combineJsonObject = new JSONObject();

}

public void putJson(String _key,String _value){

try {

combineJsonObject.put(_key, _value);


} catch (Exception e) {

Log.e("JSON Combine", ":::::array Error " + e.toString());

}

}

public String putJsonFinish(){

combineJsonArray = new JSONArray();


String result = null;

if(combineJsonObject.length()<2){

result = "Object is empty";

}else{

combineJsonArray.put(combineJsonObject);

result = combineJsonArray.toString();

//소켓으로 쏴주기 

}

return result;

}


}



Posted by k1rha
2012. 7. 26. 04:34

멤버십 과제중 안드로이드로 게임통신을 해야하는 부분에서 자바에 WSASelect 가 없으므로 비동기 소켓을 보내고 받는것이 익숙치 않았다. IOCP 소켓이란 것이 이미 윈도우의 이벤트를 이용 하는 것이기 때문에, 자바에서는 100% 맞는 통신은 없지만, 아래와 같이 channel selector 를 이용하여 비슷한 효과로 비동기 소켓을 처리 할 수 있다. 


자바는 WSAselect 가 없기 때문에 IOCP 통신을 해주기위해서 select 개념을 사용해야 한다. 


MainActivity.java


 import android.os.Bundle;

import android.app.Activity;


public class MainActivity extends Activity {


    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    

SimpleChatClient scc = new SimpleChatClient();

scc.initServer();

scc.startServer();

    }

}




SimpleChatClient.java

 

package com.example.iocpclient;


import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.nio.charset.CharacterCodingException;

import java.nio.charset.Charset;

import java.nio.charset.CharsetDecoder;

import java.util.Iterator;


import android.util.Log;


public class SimpleChatClient {


private static final String HOST = "210.118.64.148";

private static final int PORT = 5056;


private Selector selector = null;

private SocketChannel sc = null;


private Charset charset = null;

private CharsetDecoder decoder = null;

public SimpleChatClient(){//Handler MainAct) {


charset = Charset.forName("EUC-KR");

decoder = charset.newDecoder();

}


public void initServer() {


try {


// open Selector

selector = Selector.open();

// 소켓채널을 생성한다.

sc = SocketChannel.open(new InetSocketAddress(HOST, PORT));

// 비 블록킹 모드로 설정한다.

sc.configureBlocking(false);

// 서버소켓 채널을 셀렉터에 등록한다.

sc.register(selector, SelectionKey.OP_READ);

} catch (IOException ex) {


Log.e("aaa", "_____________________1" + ex.toString());

System.exit(0);

// log(Level.WARNING, "SimpleChatClient.initServer()", ex);

}

}


public void startServer() {

//startWriter();

Thread tWriter = new WriteThread(sc);

tWriter.start();

/*쓰레드 추가 */

ReaderThread tReader = new ReaderThread();

tReader.start();

}

public class ReaderThread extends Thread {

public ReaderThread(){

}

public void run() {

try {

while (true) {

// info("요청을 기다리는중...");

// 셀렉터의 select() 메소드로 준비된 이벤트가 있는지 확인한다.

selector.select();

// 셀렉터의 SelectoedSet에 저장된 준비된 이벤트들(SelectionKey)을 하나씩 처리한다.

Iterator it = selector.selectedKeys().iterator();

while (it.hasNext()) {

SelectionKey key = (SelectionKey) it.next();

if (key.isReadable()) {

// 이미 연결된 클라이언트가 메시지를 보낸경우...

read(key);

}

// 이미 처리한 이벤트므로 반드시 삭제해준다.

it.remove();

}

}

} catch (Exception ex) {

// log(Level.WARNING ,"SimpleChatClient.startServer()", ex);

}

}

}class WriteThread extends Thread {


private SocketChannel sc = null;


public WriteThread(SocketChannel sc) {

this.sc = sc;

}


public void run() {


ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

String message =null;


try {

sleep(400); //소켓 연결하는 동안 잠깐 대기. 

while (!Thread.currentThread().isInterrupted()) {

sleep(5); //초당 프레임 체크 

buffer.clear();

message=singletonMove.movement;  //싱글톤으로 가져온 변수값 


if (message.length() <2 || message==null) {

//Log.e("============","===============error NULL"+message);


} else {

if (message.equals("quit")|| message.equals("shutdown")) {

System.exit(0);

}


buffer.put(message.getBytes());

buffer.flip();

sc.write(buffer);

message = null;

}

}


} catch (Exception ex) {

ex.printStackTrace();

System.exit(0);

// log(Level.WARNING, "MyThread.run()", ex);

} finally {

System.exit(0);

clearBuffer(buffer);

}

}

}


private void read(SelectionKey key) {


// SelectionKey로부터 소켓채널을 얻어온다.


SocketChannel sc = (SocketChannel) key.channel();

// ByteBuffer를 생성한다.

ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

int read = 0;


try {


// 요청한 클라이언트의 소켓채널로부터 데이터를 읽어들인다.

read = sc.read(buffer);


// info(read + " byte를 읽었습니다.");

} catch (IOException ex) {

try {

sc.close();

} catch (IOException ex1) {


}

}


buffer.flip();


String data = new String();


try {

data = decoder.decode(buffer).toString();

} catch (CharacterCodingException ex) {


// log(Level.WARNING, "SimpleChatClient.read()", ex);

}


System.out.println("Message - " + data);


// 버퍼 메모리를 해제한다.

clearBuffer(buffer);

}


private void clearBuffer(ByteBuffer buffer) {


if (buffer != null) {

buffer.clear();

buffer = null;

}


}



}



Posted by k1rha