Socket
-
Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口谱姓。
-
網(wǎng)絡(luò)中進(jìn)程之間如何通信
- 網(wǎng)絡(luò)中進(jìn)程之間如何通信?首要解決的問題是如何唯一標(biāo)識(shí)一個(gè)進(jìn)程后控,否則通信無從談起匿级!在本地可以通過進(jìn)程PID來唯一標(biāo)識(shí)一個(gè)進(jìn)程,但是在網(wǎng)絡(luò)中這是行不通的媳板。其實(shí)TCP/IP協(xié)議族已經(jīng)幫我們解決了這個(gè)問題桑腮,網(wǎng)絡(luò)層的“ip地址”可以唯一標(biāo)識(shí)網(wǎng)絡(luò)中的主機(jī),而傳輸層的“協(xié)議+端口”可以唯一標(biāo)識(shí)主機(jī)中的應(yīng)用程序(進(jìn)程)蛉幸。這樣利用三元組(ip地址破讨,協(xié)議,端口)就可以標(biāo)識(shí)網(wǎng)絡(luò)的進(jìn)程了奕纫,網(wǎng)絡(luò)中的進(jìn)程通信就可以利用這個(gè)標(biāo)志與其它進(jìn)程進(jìn)行交互提陶。
- 使用TCP/IP協(xié)議的應(yīng)用程序通常采用應(yīng)用編程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已經(jīng)被淘汰),來實(shí)現(xiàn)網(wǎng)絡(luò)進(jìn)程之間的通信匹层。就目前而言隙笆,幾乎所有的應(yīng)用程序都是采用socket,而現(xiàn)在又是網(wǎng)絡(luò)時(shí)代又固,網(wǎng)絡(luò)中進(jìn)程通信是無處不在仲器,這就是我為什么說“一切皆socket”。
-
socket起源于Unix仰冠,而Unix/Linux基本哲學(xué)之一就是“一切皆文件”乏冀,都可以用“打開open –> 讀寫write/read –> 關(guān)閉close”模式來操作。我的理解就是Socket就是該模式的一個(gè)實(shí)現(xiàn)洋只,socket即是一種特殊的文件辆沦,一些socket函數(shù)就是對其進(jìn)行的操作(讀/寫IO昼捍、打開、關(guān)閉)
關(guān)于如下問題可參考這里
網(wǎng)絡(luò)中進(jìn)程之間如何通信肢扯?
socket的基本操作
socket()函數(shù)
bind()函數(shù)
listen()妒茬、connect()函數(shù)
accept()函數(shù)
read()、write()函數(shù)等
close()函數(shù)
iOS網(wǎng)絡(luò)編程層次模型
- Cocoa層:NSURL蔚晨,Bonjour乍钻,Game Kit,WebKit
- Core Foundation層:基于 C 的 CFNetwork 和 CFNetServices
- OS層:基于 C 的 BSD socket
Cocoa層:是最上層的基于 Objective-C 的 API铭腕,比如 URL訪問银择,NSStream,Bonjour累舷,GameKit等浩考,這是大多數(shù)情況下我們常用的 API。Cocoa 層是基于 Core Foundation 實(shí)現(xiàn)的被盈。
Core Foundation層:因?yàn)橹苯邮褂?socket 需要更多的編程工作析孽,所以蘋果對 OS 層的 socket 進(jìn)行簡單的封裝以簡化編程任務(wù)。該層提供了 CFNetwork 和 CFNetServices只怎,其中 CFNetwork 又是基于 CFStream 和 CFSocket袜瞬。
OS層:最底層的 BSD socket 提供了對網(wǎng)絡(luò)編程最大程度的控制,但是編程工作也是最多的身堡。因此吞滞,蘋果建議我們使用 Core Foundation 及以上層的 API 進(jìn)行編程。
C/S架構(gòu)程序設(shè)計(jì)基本框架
常用的Socket類型
有兩種:流式Socket(SOCK_STREAM)和數(shù)據(jù)報(bào)式Socket(SOCK_DGRAM)盾沫。流式是一種面向連接的Socket,針對于面向連接的TCP服務(wù)應(yīng)用殿漠;數(shù)據(jù)報(bào)式Socket是一種無連接的Socket赴精,對應(yīng)于無連接的UDP服務(wù)應(yīng)用。
-
TCP C/S架構(gòu)程序設(shè)計(jì)基本框架
-
UDP C/S架構(gòu)程序設(shè)計(jì)基本框架
SocketDemo
主要使用:NSStream绞幌、CFStream蕾哟、CFSocket
效果圖
客戶端 + 服務(wù)端.png
客戶端主要代碼、完整代碼下載地址
#include <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
static ViewController *selfClass =nil;
@interface ViewController ()<NSStreamDelegate>
{
NSInteger flag;
}
@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, assign) NSInteger kPort;
@property (weak, nonatomic) IBOutlet UITextField *port;
@property (weak, nonatomic) IBOutlet UITextField *serviceIP;
@property (weak, nonatomic) IBOutlet UILabel *message;
@property (weak, nonatomic) IBOutlet UITextField *putText;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
selfClass = self;
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)connectService:(id)sender {
flag = 0;
[self initNetworkCommunication];
}
- (IBAction)reviveMsg:(id)sender {
flag = 1;
[self initNetworkCommunication];
}
- (void)initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.serviceIP.text, (int)[self.port.text intValue], &readStream, &writeStream);
_inputStream = (__bridge_transfer NSInputStream *)readStream;
_outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[_inputStream setDelegate:self];
[_outputStream setDelegate:self];
[_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_inputStream open];
[_outputStream open];
}
- (void)close {
[_outputStream close];
[_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_outputStream setDelegate:nil];
[_inputStream close];
[_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_inputStream setDelegate:nil];
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
NSString *event;
switch (eventCode) {
case NSStreamEventNone:
event = @"NSStreamEventNone";
NSLog(@"%@",event);
break;
case NSStreamEventOpenCompleted:
event = @"NSStreamEventOpenCompleted";
NSLog(@"%@",event);
break;
case NSStreamEventHasBytesAvailable:
event = @"NSStreamEventHasBytesAvailable";
NSLog(@"%@",event);
if (flag == 1 && aStream == _inputStream) {
NSMutableData *input = [[NSMutableData alloc] init];
uint8_t buffer[1024];
NSInteger len;
while ([_inputStream hasBytesAvailable]) {
len = [_inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
[input appendBytes:buffer length:len];
}
}
NSString *result = [[NSString alloc] initWithData:input encoding:NSUTF8StringEncoding];
NSLog(@"%@",result);
_message.text = result;
}
case NSStreamEventHasSpaceAvailable:
event = @"NSStreamEventHasSpaceAvailable";
NSLog(@"%@",event);
if (flag ==0 && aStream == _outputStream) {
//輸出
NSString * putText = selfClass.putText.text;
UInt8 buff[1024];
memcpy(buff, [putText UTF8String],[putText lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
[_outputStream write:buff maxLength: strlen((const char*)buff)+1];
//必須關(guān)閉輸出流否則莲蜘,服務(wù)器端一直讀取不會(huì)停止谭确,
[_outputStream close];
}
break;
case NSStreamEventErrorOccurred:
event = @"NSStreamEventErrorOccurred";
NSLog(@"%@",event);
[self close];
break;
case NSStreamEventEndEncountered:
event = @"NSStreamEventEndEncountered";
NSLog(@"%@",event);
NSLog(@"Error:%ld:%@",[[aStream streamError] code], [[aStream streamError] localizedDescription]);
break;
default:
[self close];
event = @"Unknown";
break;
}
}
服務(wù)端主要代碼、完整代碼下載地址
#import "ViewController.h"
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
static ViewController *selfClass =nil;
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *portText;
@property (nonatomic, assign) NSInteger kPORT;
@property (weak, nonatomic) IBOutlet UITextField *putText;
@property (weak, nonatomic) IBOutlet UILabel *readText;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
selfClass = self;
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)startService:(id)sender {
//設(shè)置服務(wù)端端口號(hào)
self.kPORT = [self.portText.text integerValue];
CFSocketRef service;
/*CFSocketContext 參數(shù)cgindex version 版本號(hào)票渠,必須為0逐哈;
*void *info; 一個(gè)指向任意程序定義數(shù)據(jù)的指針,可以在CFScocket對象剛創(chuàng)建的時(shí)候與之關(guān)聯(lián)问顷,被傳遞給所有在上下文中回調(diào),可為NULL昂秃;
*CFAllocatorRetainCallBack retain; info指針中的retain回調(diào)禀梳,可以為NULL
*CFAllocatorReleaseCallBack release; info指針中的release的回調(diào),可以為NULL
*CFAllocatorCopyDescriptionCallBack copyDescription; info指針中的回調(diào)描述肠骆,可以為NULL
*/
CFSocketContext CTX = {0,NULL,NULL,NULL,NULL};
//CFSockerRef
//內(nèi)存分配類型算途,一般為默認(rèn)的Allocator->kCFAllocatorDefault,
//協(xié)議族,一般為Ipv4:PF_INET,(Ipv6,PF_INET6),
//套接字類型,TCP用流式—>SOCK_STREAM蚀腿,UDP用報(bào)文式->SOCK_DGRAM,
//套接字協(xié)議嘴瓤,如果之前用的是流式套接字類型:IPROTO_TCP,如果是報(bào)文式:IPPROTO_UDP,
//回調(diào)事件觸發(fā)類型 *1,
//觸發(fā)時(shí)候調(diào)用的方法 *2,
//用戶定義的數(shù)據(jù)指針莉钙,用于對CFSocket對象的額外定義或者申明廓脆,可以為NULL
service = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX);
if (service == NULL) {
return;
}
//設(shè)置是否重新綁定標(biāo)志
int yes = 1;
/* 設(shè)置socket屬性 SOL_SOCKET是設(shè)置tcp SO_REUSEADDR是重新綁定,yes 是否重新綁定*/
setsockopt(CFSocketGetNative(service), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
//設(shè)置端口和地址
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); //memset函數(shù)對指定的地址進(jìn)行內(nèi)存拷貝
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET; //AF_INET是設(shè)置 IPv4
addr.sin_port = htons(self.kPORT); //htons函數(shù) 無符號(hào)短整型數(shù)轉(zhuǎn)換成“網(wǎng)絡(luò)字節(jié)序”
addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY有內(nèi)核分配胆胰,htonl函數(shù) 無符號(hào)長整型數(shù)轉(zhuǎn)換成“網(wǎng)絡(luò)字節(jié)序”
/* 從指定字節(jié)緩沖區(qū)復(fù)制狞贱,一個(gè)不可變的CFData對象*/
CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr, sizeof(addr));
/* 設(shè)置Socket*/
if (CFSocketSetAddress(service, (CFDataRef)address) != kCFSocketSuccess) {
fprintf(stderr, "Socket綁定失敗\n");
CFRelease(service);
return ;
}
/* 創(chuàng)建一個(gè)Run Loop Socket源 */
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, service, 0);
/* Socket源添加到Run Loop中 */
CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
CFRelease(sourceRef);
printf("Socket listening on port %zd\n", self.kPORT);
/* 運(yùn)行Loop */
CFRunLoopRun();
}
//接受客戶端請求后回調(diào)函數(shù)
void AcceptCallBack(
CFSocketRef socket,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
//data參數(shù)的含義是,如果是kCFSocketAcceptCallBack類型蜀涨,data是CFSocketNativeHandle類型的指針
CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;
//創(chuàng)建讀寫socket流
CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock, &readStream, &writeStream);
if (!readStream || !writeStream) {
close(sock);
fprintf(stderr, "CFStreamCreatePairWithSocket() 失敗\n");
return;
}
CFStreamClientContext streamCtxt = {0,NULL,NULL,NULL,NULL};
//注冊兩種回調(diào)函數(shù)
CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable, ReadStreamClientCallBack, &streamCtxt);
CFWriteStreamSetClient(writeStream, kCFStreamEventCanAcceptBytes, WriteStreamClientCallBack, &streamCtxt);
//加入到循環(huán)當(dāng)中
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
}
//讀取操作瞎嬉,讀取客戶端發(fā)送的數(shù)據(jù)
static UInt8 buff[255];
void ReadStreamClientCallBack (CFReadStreamRef stream,CFStreamEventType eventType, void* clientCallBackInfo ) {
CFReadStreamRef inputStream = stream;
if (NULL != inputStream) {
CFReadStreamRead(inputStream, buff, 255);
printf("接收到的數(shù)據(jù) :%s\n", buff);
selfClass.readText.text = [NSString stringWithCString:(char*)buff encoding:NSUTF8StringEncoding];
CFReadStreamClose(inputStream);
//從循環(huán)中移除
CFReadStreamUnscheduleFromRunLoop(inputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
inputStream = NULL;
}
}
/* 寫入流操作 客戶端在讀取數(shù)據(jù)時(shí)候調(diào)用 */
void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo)
{
CFWriteStreamRef outputStream = stream;
//輸出
NSString * putText = selfClass.putText.text;
UInt8 buff[1024];
memcpy(buff, [putText UTF8String],[putText lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
if(NULL != outputStream)
{
CFWriteStreamWrite(outputStream, buff, strlen((const char*)buff)+1);
//關(guān)閉輸出流
CFWriteStreamClose(outputStream);
//從循環(huán)中移除
CFWriteStreamUnscheduleFromRunLoop(outputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
outputStream = NULL;
}
}