关于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