文章目录
  1. 1. 写在前面
  2. 2. C语言
  3. 3. BSDSocket
  4. 4. NSStream
  5. 5. CocoaAsyncSocket
    1. 5.1. 创建socket
    2. 5.2. 链接服务器或者中间件(物联网设备中间控制)
    3. 5.3. 执行代理方法—GCDAsyncSocketDelegate
  6. 6. 参考
  7. 7. 写在后面

写在前面

做了一个向中间件发送字符指令的应用,本着实用的原则,socket的基本原理我只用一张图代替.socket的使用我将以四中形式展现,

首先要知道的:socket传输 分为输入流也叫写入流(针对于客户端而言的,来源于服务器端),输出流(从客户端输出,发送至服务器端)

  • C语言
  • BSDSocket
  • NSStream
  • GCDAsyncSocket
    原理图

C语言

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MAXRCVLEN 500
#define PORTNUM 2348


int main(int argc, char *argv[])
{
char buffer[] = "My name is khan"; /* +1 so we can add null terminator */
int len, mysocket;
struct sockaddr_in dest;


mysocket = socket(AF_INET, SOCK_STREAM, 0);

memset(&dest, 0, sizeof(dest)); /* zero the struct */
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1"); /* set destination IP number */
dest.sin_port = htons(PORTNUM); /* set destination port number */

connect(mysocket, (struct sockaddr *)&dest, sizeof(struct sockaddr));

len = send(mysocket, buffer, strlen(buffer), 0);
perror("len\n");

/* We have to null terminate the received data ourselves */
buffer[len] = '\0';

printf("sent %s (%d bytes).\n", buffer, len);

close(mysocket);
return EXIT_SUCCESS;
}

服务器端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define PORTNUM 2348

#define bufferLength 500


int main(int argc, char *argv[])
{

char buffer[bufferLength];

struct sockaddr_in dest; /* socket info about the machine connecting to us */
struct sockaddr_in serv; /* socket info about our server */
int mysocket; /* socket used to listen for incoming connections */
socklen_t socksize = sizeof(struct sockaddr_in);

memset(&serv, 0, sizeof(serv)); /* zero the struct before filling the fields */
serv.sin_family = AF_INET; /* set the type of connection to TCP/IP */
serv.sin_addr.s_addr = htonl(INADDR_ANY); /* set our address to any interface */
serv.sin_port = htons(PORTNUM); /* set the server port number */

mysocket = socket(AF_INET, SOCK_STREAM, 0);

/* bind serv information to mysocket */
bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr));

/* start listening, allowing a queue of up to 1 pending connection */
listen(mysocket, 1);


int consocket;

int cpid;

while(1)
{

consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);

perror("consocket\n");

if( (cpid = fork()) == 0 )
{
printf("inside child process\n\n\n");

close(mysocket);

close(consocket);

int recivedBytes = recv(consocket, buffer, bufferLength, 0);

buffer[recivedBytes] = '\0';

printf("recieved data %s \n", buffer);

return 0;
}
else
close(consocket);

}

close(mysocket);

return EXIT_SUCCESS;
}

BSDSocket

1.创建一个线程加入事件

1
2
3
NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%d", HOST, PORT]];
NSThread * backgroundThread = [[NSThread alloc] initWithTarget:self selector:@selector(loadDataFromServerWithURL:) object:url];
[backgroundThread start];

2.从发送到响应接受服务器返回数据数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
-(void)loadDataFromServerWithURL:(NSURL*)url{

NSString * host = [url host];
NSInteger port = [[url port] integerValue];
//创建Client
CFStreamClientContext ctx = {0, (__bridge void *)(self), NULL, NULL, NULL};
//注册回调事件 触发的时机 具体时机在下面的回调函数中一一介绍

CFOptionFlags registeredEvents = (kCFStreamEventHasBytesAvailable |kCFStreamEventNone|kCFStreamEventOpenCompleted| kCFStreamEventCanAcceptBytes|kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred);

//创建写入流
CFWriteStreamRef writeStream ;



CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)host, (UInt32)port, NULL, &writeStream)
;
//加入runloop
if (CFWriteStreamSetClient( writeStream , registeredEventsTwo, socketCallbackCFWriteStreamRef, &ctx)) {
CFWriteStreamScheduleWithRunLoop( writeStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);

}else{
[self networkFailedWithErrorMessage:@"CFWriteStreamSetClient Failed to assign callback method"];
return;
}
//开启写入流
if (CFWriteStreamOpen( writeStream) == NO) {
[self networkFailedWithErrorMessage:@"Failed to open write stream"];

return;
}

CFErrorRef error2 = CFWriteStreamCopyError(writeStream);
if (error2 != NULL) {
if (CFErrorGetCode(error2) != 0) {
NSString * errorInfo = [NSString stringWithFormat:@"Failed to connect stream; error2 '%@' (code %ld)", (__bridge NSString*)CFErrorGetDomain(error2), CFErrorGetCode(error2)];
[self networkFailedWithErrorMessage:errorInfo];
}
//释放
CFRelease(error2);

}
// 开启进程

CFRunLoopRun();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void socketCallbackCFWriteStreamRef(CFWriteStreamRef stream, CFStreamEventType event, void * myPtr)
{
NSLog(@"socketCallbackCFWriteStreamRef >> in Thread %@", [NSThread currentThread]);
StreamViewController * controller = (__bridge StreamViewController *)myPtr;
switch(event) {

//开启流
case kCFStreamEventOpenCompleted:
break;
//数据流可以接受数据,在这里你要把你的数据发送到服务器
case kCFStreamEventCanAcceptBytes:{
NSString *testString = @"168353b1-6b6c-4ebd-891e-0b51d81be166|1|f4c1b518-80fe-47aa-8f87-58b5a062dcb9";
NSData *testData = [testString dataUsingEncoding: NSUTF8StringEncoding];
Byte *testByte = (Byte *)[testData bytes];
CFWriteStreamWrite( stream, testByte, testData.length);
}
break;
//发生意外调用,比如你发送了数据给服务器,服务器返回数据在这里读取
case kCFStreamEventErrorOccurred: {
[controller networkFailedWithErrorMessage:@"socketCallbackCFWriteStreamRef 写入失败"];
break;
}
//读取服务器数据
case kCFStreamEventHasBytesAvailable:{
//读取数据,直到无数据返回
while (CFReadStreamHasBytesAvailable(stream)) {
UInt8 buffer[kBufferSize];
int numBytesRead = CFReadStreamRead(stream, buffer, kBufferSize);

[controller didReceiveData:[NSData dataWithBytes:buffer length:numBytesRead]];
}

break;
}
break;
//完成数据接收,关闭流
case kCFStreamEventEndEncountered:
[controller didFinishReceivingData];
// 清理释放内存
CFWriteStreamClose(stream);
CFWriteStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFRunLoopStop(CFRunLoopGetCurrent());
break;
default:
break;
}

NSStream

NSStream是对上面CFReadStreamRef或者CFWriteStreamRef的简单的封装.
NSSream分为输入流(NSOutputStream:这对于客户端而言的,来源于服务器端),输出流(NSInputStream:从客户端输出,发送至服务器端)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//输出流
uint8_t * buffer ;
char * str = "168353b1-6b6c-4ebd-891e-0b51d81be166|1|ddef2049-761a-45a0-9e77-ebf684c4b8f0";
NSOutputStream * outputStream = [NSOutputStream outputStreamToBuffer:buffer capacity:sizeof(buffer)];

//配置 HOST PORT 代理NSStreamDelegate
[outputStream setProperty:@"123.123.118.67" forKey:NSStreamSOCKSProxyHostKey];
[outputStream setProperty:@"4521" forKey:NSStreamSOCKSProxyPortKey];
[outputStream setDelegate:self];
//加入普通寻
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
NSLog(@"%@", [outputStream propertyForKey:NSStreamSOCKSProxyHostKey]);
//开启输出流
[outputStream open];

代理回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#pragma mark -NSStreamDelegate
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{

//这里和前面的十分类似,对比学习即可,此处不再做详细介绍
switch (eventCode) {
case NSStreamEventOpenCompleted:
NSLog(@"NSStreamEventOpenCompleted");
break;
case NSStreamEventHasBytesAvailable:
NSLog(@"NSStreamEventHasBytesAvailable");

uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)aStream read:buf maxLength:1024];
if(len) {
[_data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.

} else {
NSLog(@"no buffer!");
}
break;

//写入数据
case NSStreamEventHasSpaceAvailable:

break;
case NSStreamEventErrorOccurred:

break;
case NSStreamEventEndEncountered:

[aStream close];
[aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

aStream = nil; // aStream是一个指针,避免野指针的产生,此处赋空值;
break;

default:
break;
}
}

CocoaAsyncSocket

创建socket

1
2

Socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

链接服务器或者中间件(物联网设备中间控制)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//断开连接
if (Socket.isConnected) {
NSLog(@"链接已经断开,重新开启链接");
[Socket disconnect];
}


//连接服务器 当然公司的ip和端口不能对外公布,请更换为自己的
NSError *err = nil;
if (![Socket connectToHost:@"123.155.118.167" onPort:4521 error:&err]) //Asynchronous!
{
//If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(@"I goofed: %@", err);
return;
}else{
NSLog(@"正在链接");
}

执行代理方法—GCDAsyncSocketDelegate


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#pragma mark-GCDAsyncSocketDelegate
//成功链接服务器后调用
- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port
{

NSLog(@"链接成功Cool, I'm connected! That was easy.%@",[NSThread currentThread]);
//在这里发数据给服务器
NSString *testString = @"168353B1-6B6C-4EBD-891E-0B51D81BE166|1|DDEF2049-761A-45A0-9E77-EBF684C4B8F0";
NSString * lowerString =testString.lowercaseString;

NSData *data = [lowerString dataUsingEncoding:NSUTF8StringEncoding];
//发送数据
[Socket writeData:data withTimeout:-1 tag:1];
}
//与服务器断开链接调用
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
NSLog(@"链接断开%@",[NSThread currentThread]);
}
//发送数据后调用
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
NSLog(@"didWriteDataWithTag %@",[NSThread currentThread]);
switch (tag) {
case 1:
NSLog(@"第一个写入请求发出");
//读取服务器返回信息(不调用这个不会回调下面的紧接着的这个方法)
[sock readDataWithTimeout:-1 tag:10 ];
break;
case 2:
NSLog(@"第2个写入请求发出");

break;
default:
NSLog(@"第3个写入请求发出");

break;
}


}
//服务器返回回数据调用
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
NSLog(@"didReadData %@",[NSThread currentThread]);

if (receiveData == nil) {
//如果不存在,则创建一个新的
receiveData = [[NSMutableData alloc] init];
}
while(data){
[receiveData appendData:data];//把接收到的数据添加上
}

[Socket readDataToData:data withTimeout:-1 tag:TAG_MSG];
NSLog(@"data %@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}
//
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock{
NSLog(@"%s",__func__);
[sock disconnect];
sock = nil;
}

参考

深入浅出Cocoa

写在后面

本文主要针对于发送数据到服务器,直接读取服务器内容的请参考自行更改

——————————–写入3/21/2017 11:57

转载请注明网址(yanqinglove.xyz)

文章目录
  1. 1. 写在前面
  2. 2. C语言
  3. 3. BSDSocket
  4. 4. NSStream
  5. 5. CocoaAsyncSocket
    1. 5.1. 创建socket
    2. 5.2. 链接服务器或者中间件(物联网设备中间控制)
    3. 5.3. 执行代理方法—GCDAsyncSocketDelegate
  6. 6. 参考
  7. 7. 写在后面