关于socket 使用实战总结

写在前面

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

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

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

C语言

客户端

#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;
}

服务器端

#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.创建一个线程加入事件

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

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

-(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();
}
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:从客户端输出,发送至服务器端)

//输出流
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];

代理回调

#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


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

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

//断开连接
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

#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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

严青

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值