iOS socket 简单使用

这篇博客主要介绍如何在iOS应用中实现简单的TCP Socket通信,包括客户端和服务端的建立连接、数据发送与接收。代码示例展示了如何创建客户端连接、设置回调函数以及服务端的接受连接和数据处理。通过导入相关头文件并使用CFSocket API,实现了客户端与服务端之间的消息传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于socket的文章非常多,别人写 的也很清楚,我敲这个demo的时候,也是看着别人的博客一步一步敲的,这样才能发现其中的问题。在这里socket的原理、TCP、http协议就不做介绍了。下面的代码只是实现简单的客户端和服务端的通信,服务端给指定客户端发送消息。当然也可以实现客户端给客户端发送消息,想给谁发送,在消息里加上一个标志,服务端收到时,再简单解析一下就可以了。

因为写博客的时候和学习socket的时候隔的时间有点长,所以参考的文章不知道是哪篇了,就不关联了。微笑

注:代码里面的控件全是在storyboard拖拽出来的

使用socket首先导入这几个头文件

#import <sys/socket.h>

#import <netinet/in.h>

#import <arpa/inet.h>

#import <unistd.h>

#import <netinet/tcp.h>


客户端:

创建一个静态变量

static ClientViewController *selfClass =nil;//c函数里可使用其调自身方法或属性



- (void)viewDidLoad {

    [super viewDidLoad];

    selfClass=self;

    [self creatConnect];

}



/**

 创建连接

 */

-(void)creatConnect{

    

    CFSocketContext sockContext={0,NULL, NULL,NULL,NULL};

//    创建socket

     _socket=CFSocketCreate(kCFAllocatorDefault,//为对象分配内存 可为nil

                                       PF_INET,//协议族,0或负数 默认为 PF_INET

                                       SOCK_STREAM,//套接字类型,协议族为PF_INET 默认

                                       IPPROTO_TCP,//套接字协议

                                       kCFSocketConnectCallBack,//触发回调消息类型

                                       TCPServerConnectCallBack ,//回调函数

                                       &sockContext//一个持有CFSocket结构信息的对象,可以为nil

                                       );

    

    if (_socket!=nil) {

//        配置服务器地址

        struct sockaddr_in addr4; //IPV4

        memset(&addr4, 0, sizeof(addr4));

        

        addr4.sin_len=sizeof(addr4);

        addr4.sin_family=AF_INET;//协议族

        addr4.sin_port=htons(8888);//端口号

        addr4.sin_addr.s_addr=inet_addr([@"服务端IP地址" UTF8String]); // 把字符串的地址转换为机器可识别的网络地址

        

        CFDataRef address=CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4 , sizeof(addr4));

        //绑定socket

        CFSocketConnectToAddress(_socket, address, -1);// 连接超时时间,如果为负,则不尝试连接,而是把连接放在后台进行,如果_socket消息类型为kCFSocketConnectCallBack,将会在连接成功或失败的时候在后台触发回调函数

        

    

        CFRunLoopRef cRunRef=CFRunLoopGetCurrent();

        

        CFRunLoopSourceRef sourceRef=CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);

        CFRunLoopAddSource(cRunRef, sourceRef, kCFRunLoopCommonModes);

        

//        释放sourceRef

        CFRelease(sourceRef);

    }

    

    

}



/**

 socket 回调函数,函数格式可在socket创建函数里查看

(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)

*/

static void TCPServerConnectCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info){

    NSLog(@"%@",info);

    if (data!=NULL && type ==kCFSocketConnectCallBack) {

        NSLog(@"连接失败");

        return;

    }

    

  //使用异步线程监听有没有收到数据

    [NSThread detachNewThreadSelector:@selector(readMessage) toTarget:selfClass withObject:nil];

    

}


- (IBAction)send:(id)sender {

    [self sendMessage];

    

    [self.view endEditing:YES];

}




-(void)readMessage{

    //            监听收到的数据

    char buf[2048];

    NSString *logStr;

    do {

        // 接收数据

        ssize_t recvLen = recv(CFSocketGetNative(_socket), buf, sizeof(buf), 0);

    

        

        if (recvLen > 0) {

            logStr = [NSString stringWithFormat:@"%@\n", [NSString stringWithFormat:@"%s", buf]];

            //                        回到主线程刷新UI

            [self performSelectorOnMainThread:@selector(showMessage:) withObject:logStr waitUntilDone:YES];

        }

        

    } while (strcmp(buf, "exit") != 0);

    

    

}


// 发送数据


- (void)sendMessage {

    

    NSString*stringTosend = self.textF.text;

    

   const char *data = [stringTosend UTF8String];

    

    send(CFSocketGetNative(_socket), data, strlen(data) + 1, 0);

    

}


-(void)showMessage:(NSString *)message{

    self.receivedLabl.text=message;

}



服务端:

.h里 

@property (weak, nonatomic) IBOutlet UITextField *textF; //消息输入框

@property (weak, nonatomic) IBOutlet UILabel *receLa;//消息展示

@property (weak, nonatomic) IBOutlet UILabel *listlabl;//客户端IP地址显示

@property (weak, nonatomic) IBOutlet UITextField *clientTf;//指定第几个连接的客户端



//

//  ServiceViewController.m

//  socket_Test

//

//  Created by Chu on 17/8/4.

//  Copyright © 2017 Chu. All rights reserved.

//


#import "ServiceViewController.h"

#import <sys/socket.h>

#import <netinet/in.h>

#import <arpa/inet.h>

#import <unistd.h>

#import <netinet/tcp.h>



@interface ServiceViewController ()


@end


static CFWriteStreamRef outputStream;

static ServiceViewController *selfClass=nil;



@implementation ServiceViewController

{

    CFSocketRef _socket;

    NSMutableArray *addressArr;


}

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view.

    

    selfClass=self;

    addressArr=[NSMutableArray array];

    

//    安装时我的真机不申请网络,很无奈加了一句这

    [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=false&word=风景电脑壁纸&step_word=&hs=0&pn=0&spn=0&di=56105480860&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=1363482873%2C1492771985&os=2200545678%2C3916134210&simid=4263289627%2C720205809&adpicid=0&lpn=0&ln=3994&fr=&fmq=1501838304131_R&fm=rs3&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&ist=&jit=&cg=wallpaper&bdtype=0&oriquery=风景&objurl=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2Fe%2F53ec732e6142b.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Botg9aaa_z%26e3Bv54AzdH3Fowssrwrj6_kt2_cd9dm_8_z%26e3Bip4s&gsm=0&rpstart=0&rpnum=0"]];

     outputStream=NULL;

    [self creatConnect];

//    if ([self creatConnect]) {

//        

//        CFRunLoopRun();

//    }

    

}



-(BOOL)creatConnect{

    

    _socket=CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, TCPServerAcceptCallBack, NULL);

    

    if (_socket==NULL) {

        NSLog(@"cannot creat socket");

        return 0;

    }

    

    int optval=1;

    setsockopt(CFSocketGetNative(_socket), SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(optval));

    

    struct sockaddr_in addr4;

    memset(&addr4, 0, sizeof(addr4));

    addr4.sin_len=sizeof(addr4);

    addr4.sin_family=PF_INET;

    addr4.sin_port=htons(8888);

    addr4.sin_addr.s_addr=htonl(INADDR_ANY);

    

    CFDataRef address=CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr4, sizeof(addr4));

    

    

    if (CFSocketSetAddress(_socket, address) != kCFSocketSuccess) {

         NSLog(@"Bind to address failed!");

        if (_socket) {

            CFRelease(_socket);

            _socket=NULL;

        }

        return 0;

    }

    

    CFRunLoopRef cRunLoop=CFRunLoopGetCurrent();

    CFRunLoopSourceRef source=CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);

    CFRunLoopAddSource(cRunLoop, source, kCFRunLoopCommonModes);

    CFRelease(source);

    

    

    return 1;

    

    

    

}

static void TCPServerAcceptCallBack(CFSocketRef socket,CFSocketCallBackType type,CFDataRef address,const void *data,void *info){


    if (kCFSocketAcceptCallBack == type) {


        CFSocketNativeHandle nativeSocketHandle=*(CFSocketNativeHandle *)data;

        uint8_t name[SOCK_MAXADDRLEN];

        socklen_t nameLen=sizeof(name);

        

        if (getpeername(nativeSocketHandle, (struct sockaddr*)name, &nameLen)) {

            NSLog(@"error");

            exit(1);

        }

        NSLog(@"%s connected",inet_ntoa(((struct sockaddr_in*)name)->sin_addr));

        CFReadStreamRef readStream;

        CFWriteStreamRef writeStream;

        

        CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);

        

        if (readStream && writeStream) {

            [selfClass->addressArr addObject:(__bridge id _Nonnull)(writeStream)];

            NSString *addre=[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in*)name)->sin_addr)];

            selfClass.listlabl.text=[selfClass.listlabl.text stringByAppendingString:addre];

            

            CFStreamClientContext streamContext={0,(__bridge void*)(addre),NULL,NULL};

            if (!CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable, readStreamFunc, &streamContext)) {

                exit(1);

            }

            

            if (!CFWriteStreamSetClient(writeStream, kCFStreamEventCanAcceptBytes, writeStreamFunc, &streamContext)) {

                exit(1);

            }

            

            CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);

            CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);

            CFReadStreamOpen(readStream);

            CFWriteStreamOpen(writeStream);

            

            

        }else{

            close(nativeSocketHandle);

        }

        

        

        

    }

    

    

}


/**

 读取数据


 */

void readStreamFunc(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo){

    UInt8 buff[255];

    CFReadStreamRead(stream, buff, 255);

    printf("received %s",buff);

    NSString *str=[NSString stringWithCString:(char *)buff encoding:NSUTF8StringEncoding];

    [selfClass showReciveData:str];

    

}


void writeStreamFunc(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo){

}



-(void)sendStream{

    

    int index=[self.clientTf.text intValue];


    UInt8 buff[1024];

    memcmp(buff, [self.textF.text UTF8String], sizeof(buff));

    

//    给指定客户端发送消息

    outputStream=(__bridge CFWriteStreamRef)(addressArr[index]);

    CFWriteStreamWrite(outputStream, (UInt8 *)self.textF.text.UTF8String, strlen(_textF.text.UTF8String)+1 + 1);

    

    

    [self.view endEditing:YES];

    

    

}



-(void)showReciveData:(NSString *)str{

    self.receLa.text=str;

}



- (IBAction)send:(id)sender {

    

    [self sendStream];

}

@end



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值