English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
以前、App間の5つの通信方法について紹介している記事を読みました。それには、URL Scheme、Keychain、UIPasteboard、UIDocumentInteractionController、そしてローカル通信にsocketを使う方法があります。以下では、4どちらも使ったことがあります。比較的簡単で、数行のコードで済みます。最後の1つは、以前は使ったことがありませんでした(私も初心者ですからね)。だから、今日は試してみて、ここに記録して皆さんと共有します。
それでは、余計なことを言わずに始めましょう:
まず、その原理について説明します。実は非常にシンプルです。1つのAppがローカルのポートでTCPのbindとlistenを行い、もう1つのAppが同じポートでconnectを行うと、正常なTCP接続が確立されます。どんなデータを送るかは自由です。以下に、まずサーバー側を作成します:
1、まずsocket()関数を使用してソケットを作成します
/* * ソケットはint値を返します。-1、ソケットの作成に失敗しました * 第1引数はプロトコルファミリーを指明します/ドメイン、通常AF_INET(IPV4)、AF_INET6)、AF_INET6)、AF_LOCAL * 第2引数は、ソケットインターフェースタイプを指定します:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKETなど * 第3引数は、対応するトランスポートプロトコルを指定します:TCPなど/UDPなど、一般的にデフォルトの値を使用するために0に設定されます */ int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1){ close(sock); NSLog(@"socket error : %d",sock);<br> return; } /* * ソケットはint値を返します。-1、ソケットの作成に失敗しました * 第1引数はプロトコルファミリーを指明します/ドメイン、通常AF_INET(IPV4)、AF_INET6)、AF_INET6)、AF_LOCAL * 第2引数は、ソケットインターフェースタイプを指定します:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKETなど * 第3引数は、対応するトランスポートプロトコルを指定します:TCPなど/UDPなど、一般的にデフォルトの値を使用するために0に設定されます */ int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1){ close(sock); NSLog(@"socket error : %d",sock);<br> return; }
2、本機のアドレスとポート番号をバインドします
// アドレス構造体データ、IPとポート番号を記録します struct sockaddr_in sockAddr; // 使用するプロトコルを宣言します sockAddr.sin_family = AF_INET; // 本機のIPを取得し、char型に変換します const char *ip = [[self getIPAddress] cStringUsingEncoding:NSASCIIStringEncoding]; // ipを構造体に割り当てます。inet_addr()関数は、点分十進数のIPを長整数型に変換します sockAddr.sin_addr.s_addr = inet_addr(ip); // ポート番号を設定します。htons()は、整型変数をホストバイトオーダーからネットワークバイトオーダーに変換します sockAddr.sin_port = htons(12345; /* * bind関数はソケットをアドレスに関連付け、int値を返します。-1为失败 * 第1引数はソケットを指定し、それは前のsocket関数呼び出しで返されたソケットです * 第2引数は指定されたアドレスです * 第3引数はアドレスデータのサイズです */ int bd = bind(sock,(struct sockaddr *) &sockAddr, sizeof(sockAddr)); if(bd == -1){ close(sock); NSLog(@"bind error : %d",bd); return; }
3、リスニングにバインドされたアドレス
/* * listen関数は、アクティブな接続ソケットインターフェースを被接続インターフェースに変換し、他のプロセスからのリクエストを受け入れることができます。int値を返します。-1为失败 * 第1引数はsocket関数が返したソケットです * 第2引数は接続の最大制限と解釈できます */ int ls = listen(sock,2,0); if(ls == -1){ close(sock); NSLog(@"listen error : %d",ls); return; }
4、以下はクライアントの接続を待つ部分です。accept()を使用します(accept関数はスレッドをブロックするため、接続待ちの間にスレッドが停止するため、サブスレッド内で実行することをお勧めします)
// 1.サブスレッドを開始します NSTread *recvThread = [[NSThread alloc] initwithTarget:self selector:@selector(recvData) object: nil]; [recvThread start]; - (void)recvData{ // 2.クライアントの接続を待つ // 受信したクライアントのアドレスを受け取るために、後に使用されるアドレス構造体を宣言します struct sockaddr_in recvAddr; // アドレスのサイズ socklen_t recv_size = sizeof(struct sockaddr_in); /* * accept()関数が接続成功すると新しいソケット(self.newSock)を返し、そのクライアントとのデータの送受信に使用されます * 第1引数は前にリスニングしていたソケットであり、以前はローカル変数であったが、今はグローバルにする必要があります * 第2引数も結果引数であり、戻り値を取り、その戻り値はクライアントのアドレスを指定します * 第3引数も結果引数であり、recvAddr構造体の代わりを取り、占めるバイト数を示します */ self.newSock = accept(self.sock,(struct sockaddr *) &recvAddr, &recv_size); // 3ここに来ることは新しいクライアントに接続したことを意味し、以下ではデータの送受信を行うことができます。主にsend()とrecv()関数を使用します。 ssize_t bytesRecv = -1; // データバイトサイズを返す char recvData[128] = ""; // データバッファーを返す // 接続が切断された場合、recvはすぐに返り値を返し、bytesrecvは0に等しくなるため、whileループは常に実行されるので、0であることを判断してループを抜ける while(1){ bytesRecv = recv(self.newSocket,recvData,128,0); // recvDataが受け取ったデータ if(bytesRecv == 0){ break; } } }
5、データの送信
- (void)sendMessage{ char sendData[32'] = "hello client";" ssize_t size_t = send(self.newSocket, sendData, strlen(sendData), 0); }
客户端那边就主要分为:创建套接字,根据ip和端口号获取服务端的主机地址,然后再连接,连接成功过后就能够向服务端收发数据了,下面我们看代码。
1、和服务端一样用socket函数创建套接字
int sock = socket(AF_INET, SOCK_STREAM,0); if(sock == -1){ NSLog(@"socket error : %d",sock); return; }
2、获取主机的地址
NSString *host = [self getIPAddress]; // 获取本机ip地址 // 返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针 struct hostent *remoteHostEnt = gethostbyname([host UTF8String]); if(remoteHostEnt == NULL){ close(sock); NSLog(@"无法解析服务器主机名"); return; }// 配置套接字将要连接主机的ip地址和端口号,用于connect()函数 struct in_addr *remoteInAddr = (struct in_addr *)remoteHost->h_addr_list[0]; struct sockaddr_in socktPram; socketPram.sin_family = AF_INT; socketPram.sin_addr = *remoteInAddr; socketPram.sin_port = htons([port intValue]);
3、使用connect()函数连接主机
/* * connect函数通常用于客户端简历tcp连接,连接指定地址的主机,函数返回一个int值,-1为失败 * 第一个参数为socket函数创建的套接字,代表这个套接字要连接指定主机 * 第二个参数为套接字sock想要连接的主机地址和端口号 * 第三个参数为主机地址大小 */ int con = connect(sock, (struct sockaddr *) &socketPram, sizeof(socketPram)); if(con == -1){ close(sock); NSLog(@"接続失敗"); return; } NSLog("接続成功"); // ここにきて代表接続が成功しています
4、接続が成功した後にはデータを受信・送信できます
- (IBAction)senddata:(id)sender { // データを送る char sendData[32] = "hello service"; ssize_t size_t = send(self.sock, sendData, strlen(sendData), 0); NSLog(@"%zd",size_t); } - (void)recvData{ // データを受け取る,ちじゅうれんに ssize_t bytesRecv = -1; char recvData[32] = ""; while (1) { bytesRecv = recv(self.sock, recvData, 32, 0); NSLog(@"%zd %s",bytesRecv,recvData); if (bytesRecv == 0) { break; } } }
じょうぜん、socketをもちいてどくびょうでりょうごう Appのきょうそうどうどうするのが、こんなふうです。はんはんてきほん,いちにじぐんのこころをきこえて、にちようけいしゅうをかいしゃします。もんにちわにちわじょうて、まちがいがあるとかいしゃしていただけるとかんがいします。さいごにDemoのどじをつけます、にちようごうけん、にんいちきょうのしゃいせんをかいしゃします:
これでごんぶんのぜんぶのないようです。かんかいにありがとう、がんばりょうきょうせんようきょうをたすけます。
せいみ:ごんぶんの中の內容はねんりゅうわくをにょけい、さくせいしゃにゆうゆう、ゆうけいしをくみあげ、ほんしゃはしょゆうせいりょうをもたない。じんじんせいはじゅつしょくしゅにたいして、じんじんせいはじゅつしょくしゅにたいして、せいりょくせいりょうせいりょくをもたない。あなたがぼうりょうりょくようのコンテンツがあると、まいりょうまいりょういんにしよいんせんひょうきょうしゅうしていただきます:notice#oldtoolbag.com(はしめつとき、#を@に変えてください。てきぼうをし、かんけいしをきょうじゅうしてください。もとりょくじょうけんにちじょうし、せいかくしんりょくをしんこうじゅうじょう、ほんしゃはしょゆうせいりょくをもたない。もんやおうりんようのコンテンツがかいられていると、まいりょうまいりょういんにしよいんせんひょうきょうしゅうしていただきます。