フォトアルバム

他のアカウント

更新ブログ

Powered by Six Apart
Member since 03/2005

« 昔のノート見てたら・・・ | メイン | 成龍ヤバい••• かっこ良すぎる••• »

iPhone でSocket 通信(クライアント側)

と、言う事で•••
先日こしらえていた実験用iPhone プログラムのSocket 部分だけメモに残しておく事に!!
 
ヘッダファイルにはこのような定義を
 
変数
//Socket
CFSocketContext _CTX;
CFSocketRef _Socket;
bool _ConectFlag;
//Data
NSString* _RecvStr;
 
メソッド
//Alart
- (void)showAlert:(NSString*)title text:(NSString*)text;
//Socket
- (void)dataSend:(NSString *)DataStr;
static void SocketCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
 
@property (nonatomic, retain) NSString *RecvStr;
@property bool ConectFlag;
 
※Alart は、受信した時やSocket の状態変化でアラートを表示する為に使用
 SocketCallBack で使用する変数は渡されたインスタンスから呼ぶ為(self が使えない)プロパティにしておく。
 
ココからはプログラム。
 
アラートはこんな感じで•••
 
- (void)showAlert:(NSString*)title text:(NSString*)text {
UIAlertView* alert=[[[UIAlertView alloc]
initWithTitle:title message:text delegate:nil
cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease];
[alert show];
}
 
TCP 接続処理
 
- (IBAction)clickButtonNo3:(UIBarButtonItem*)sender{
 
if (_ConectFlag==TRUE) {
[self showAlert:@"SocketOpen" text:@"DoubleConnect"];
return;
}
 
_CTX.version = 0;
_CTX.info = (void *)self;
_CTX.retain = NULL;
_CTX.release = NULL;
_CTX.copyDescription = NULL;
 
_Socket = CFSocketCreate(kCFAllocatorDefault,
PF_INET, SOCK_STREAM, IPPROTO_TCP,
kCFSocketConnectCallBack | kCFSocketDataCallBack,
SocketCallBack, &_CTX);
if (_Socket == NULL){
[self showAlert:@"SocketOpen" text:@"CreateError"];
return;
}
 
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _Socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
CFRelease(sourceRef);
 
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(5000); //Port No.
addr.sin_addr.s_addr = inet_addr([@"192.168.***.***" UTF8String]); //IP Address
NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)];
CFSocketError SockError = CFSocketConnectToAddress(_Socket, (CFDataRef)address, 2);
if (SockError) {
//Error
_ConectFlag = FALSE;
[self showAlert:@"SocketOpen" text:@"ConnectError"];
CFSocketInvalidate(_Socket);
CFRelease(_Socket);
return;
}
_ConectFlag = TRUE;
//[self showAlert:@"SocketOpen" text:@"Connect!!"];
}
 
最初に有るのは二重オープン防止
接続処理のはじめは、コンテキストを作成。
ココで、
_CTX.info = (void *)self;
こうする事で、コールバックにself のポインタを渡す。
そうすれば、コールバックからself 内のプロパティやメソッドを呼び出せる。
CFSocketCreate でソケットを作成。 接続時と、データ受信時にコールバックを設定したかったので、
二つのフラグをAnd する。 kCFSocketConnectCallBack | kCFSocketDataCallBack,
コールバック関数は、SocketCallBack です。
後は、RunLoop を追加?して、接続先アドレスを作ってCFSocketConnectToAddress で追加する。
コレで接続処理はOK
 
ついでに、切断処理とデータ送信処理
 
- (IBAction)clickButtonNo4:(UIBarButtonItem*)sender{
if (_ConectFlag==FALSE) {
[self showAlert:@"SocketClose" text:@"NoneConnect"];
return;
}
CFSocketInvalidate(_Socket);
CFRelease(_Socket);
_ConectFlag = FALSE;
}
 
- (IBAction)clickButtonNo5:(UIBarButtonItem*)sender{
if (_ConectFlag==FALSE) {
[self showAlert:@"DataSend" text:@"NoneConnect"];
return;
}
[self dataSend:@"iPhoneSendMessage"];
}
 
- (void)dataSend:(NSString *)DataStr
{
if (_ConectFlag==FALSE) {
[self showAlert:@"DataSend" text:@"NoneConnect"];
return;
}
 
NSData *refDataStr = [DataStr dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
CFDataRef refData = CFDataCreate(kCFAllocatorDefault, [refDataStr bytes], [refDataStr length]);
CFSocketSendData(_Socket, NULL, refData, 0);
}
 
送信処理は、単純にNSString からNSData を作成する。(NSString をASCII に、ソレをByteData にする)
CFSocketSendData でソレを送信。
 
受信処理はコールバックで•••
 
static void SocketCallBack(CFSocketRef socket, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info) {
 
Test_iPhone_APPAppDelegate *SelfClass = info;
 
if (CFSocketIsValid(socket) == FALSE) {
[SelfClass showAlert:@"SocketCallBack" text:@"Socket Disonnect!!"];
SelfClass.RecvStr = @"Socket Disconnect!!";
CFSocketInvalidate(socket);
CFRelease(socket);
SelfClass.ConectFlag = FALSE;
return;
}

if (type == kCFSocketConnectCallBack) {
[SelfClass showAlert:@"SocketCallBack" text:@"Socket Connect!!"];
SelfClass.RecvStr = @"Socket Connect!!";
}
else if (type == kCFSocketDataCallBack) {
NSData* RcvData = (NSData*)data;
if ([RcvData length] == 0){
[SelfClass showAlert:@"SocketCallBack" text:@"Socket Disonnect!!"];
SelfClass.RecvStr = @"Socket Disconnect!!";
CFSocketInvalidate(socket);
CFRelease(socket);
SelfClass.ConectFlag = FALSE;
return;
}
NSString *TempStr = [[NSString alloc] initWithData:RcvData encoding:NSASCIIStringEncoding];
[SelfClass showAlert:@"SocketCallBack" text:TempStr];
SelfClass.RecvStr = TempStr;
}
}
 
まずは、先ほどコンテキストで設定したself のポインタで、SelfClass と言うクラスのインスタンスに設定する。
Test_iPhone_APPAppDelegate *SelfClass = info;
コレで、SelfClass を使ってプロパティやメソッドを呼び出す。
例えば、こんな感じ。
[SelfClass showAlert:@"SocketCallBack" text:@"Socket Connect!!"];
SelfClass.RecvStr = @"Socket Connect!!";
 
コールバックが呼び出されている時点で、
if (CFSocketIsValid(socket) == FALSE) {
こんな事は無いとは思うが、一応、ソケットが無い場合は切断されていましたと言う処理をする。
 
コネクト時には、下記のtype で呼び出されている。
if (type == kCFSocketConnectCallBack) {
どうやら、本当に接続時だけみたいだった。
 
データコールバックの時には、
else if (type == kCFSocketDataCallBack) {
NSData* RcvData = (NSData*)data;
if ([RcvData length] == 0){
ココに有るように、RcvData のlength が0の時が有る。
コレは、サーバー側(リスナ側)がクローズした時にこのコールバックが発生する感じ。
※サーバー側は、VB.NET で普通のSystem.Net.Sockets のTcpListener を使用しました。
この時に、切断処理を行います。
ソレ以外の時はデータ受信なので、受信した時の処理を行います。
 
なんせ資料が少なくて、本当にコレであっているのか不明ですが、
とりあえず通信は出来ました。(接続、切断処理も含め)
 
まだObjective-C の書き方を良く理解していない為、コールバック内の処理等、怪しいと思います。
※もし、.NET ならコールバックは別スレッドから呼ばれる事になる為スレッドセーフでなくなる?
 ソレを、ポインタでクラスのインスタンスに直接アクセスして良いのかどうか?
 
ま、良く分かりませんが•••
iOS と Socket で検索してもなかなか欲しい情報が無かったので、こんな感じでBlog にメモっときました。

コメント

追記!!
 
Framework はCFNetwork.framework を、
あとはこいつらを•••
 
#import 〈CFNetwork/CFNetwork.h〉
#import 〈sys/socket.h〉
#import 〈netinet/in.h〉
#import 〈arpa/inet.h〉
 
チョット説明の追加で、
コールバック関数の中の引数 void *info に、コンテキストで設定したinfo の値が渡されます。
コノ説明を忘れてた^^;
コレが結構ミソだったりする。

コメントを投稿