热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

iOS使用socket实现即时通信示例(非第三方库)

这篇文章主要介绍了iOS使用socket即时通信示例(非第三方库)的资料,这里整理了详细的代码,有需要的小伙伴可以参考下。

其实写这个socket一开始我是拒绝的。

因为大家学C 语言和linux基础时肯定都有接触,客户端和服务端的通信也都了解过,加上现在很多开放的第三方库都不需要我们来操作底层的通信。

但是!还是想写。底层的东西最好了解下。

效果

由于5M的上传限制GIF可能看不清 我再截两张图吧

服务器

客户端A

客户端B

模型图

分析

由上图可以了解到服务器和客户端需要做哪些工作

服务器

抽象一点分为:

1.创建线程等待接收客户端的连接

2.接收并解析客户端发来的消息

3.给客户端发送消息

具体一点:

1.创建socket. 绑定端口.开始监听.

2.创建线程.等待接收客户端连接.

3.接收客户端发来的消息

4.解析消息内容

a.设置用户名

b.发送消息给指定客户端

客户端

抽象一点分为:

1.连接服务器

2.给服务器发送消息

3.接收服务器消息

4.解析消息内容

具体一点:

1.创建socket.绑定端口.连接服务器

2.发送消息

a.设置用户名

b.给指定用户发消息:按服务器格式拼接字符串

3.接收消息

a.普通消息

b.用户列表:保存至用户列表

UI方面

服务器:其实不用什么UI放个控件展示下日志就是了

客户端:比较简单,一个俗套聊天室的界面,直接storyboard里拖拖控件设置约束了

DEMO而已别太当真

代码部分

服务器

要使用scoket需要引用这三个头文件

#include 
#include 
#include 

只有一个model,用来绑定用户名和socket

@interface ClientModel : NSObject
@property(nonatomic,assign)int clientSocket;
@property(nonatomic,copy)NSString *clientName;
@end

只有一个文件全给你

#import "ViewController.h"

#include 
#include 
#include 
#import "ClientModel.h"
static int const kMaxCOnnectCount= 5;

@interface ViewController()
@property (weak) IBOutlet NSTextField *textField;
//@property (nonatomic,assign)int client_socket; //客户端socket
@property (unsafe_unretained) IBOutlet NSTextView *textView;
 @property (nonatomic,strong)NSMutableArray *clientArray;
 @property (nonatomic,strong)NSMutableArray *clientNameArray;
@end

@implementation ViewController

- (NSMutableArray *)clientArray {
 if (!_clientArray) {
  _clientArray = [NSMutableArray array];
 }
 return _clientArray;
}
- (NSMutableArray *)clientNameArray {
 if (!_clientNameArray) {
  _clientNameArray = [NSMutableArray array];
 }
 return _clientNameArray;
}

- (void)viewDidLoad {
 [super viewDidLoad];
 // Do any additional setup after loading the view.
 //创建socket
 int server_socket = socket(AF_INET, SOCK_STREAM, 0);
 if (server_socket == -1) {
  NSLog(@"创建失败");
  [self showLogsWithString:@"socket创建失败"];

 }else{
  //绑定地址和端口
  struct sockaddr_in server_addr;
  server_addr.sin_len = sizeof(struct sockaddr_in);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(1234);
  server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  bzero(&(server_addr.sin_zero), 8);

  int bind_result = bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
  if (bind_result == -1) {
   NSLog(@"绑定端口失败");
   [self showLogsWithString:@"绑定端口失败"];

  }else{
   if (listen(server_socket, kMaxConnectCount)==-1) {
    NSLog(@"监听失败");
    [self showLogsWithString:@"监听失败"];

   }else{
    for (int i = 0; i 0) {
   NSLog(@"客户端来消息了");
   NSString *str = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];
   [self showLogsWithString:[NSString stringWithFormat:@"客户端来消息了:%@",str]];
   [self checkRecvStr:str andClientSocket:client_socket];
  }else if (iReturn == -1){
   NSLog(@"读取消息失败");
   [self showLogsWithString:@"读取消息失败"];
   break;
  }else if (iReturn == 0){
   NSLog(@"客户端走了");
   [self showLogsWithString:[NSString stringWithFormat:@"客户端 out socket:%d",client_socket]];
   NSMutableArray *array = [NSMutableArray arrayWithArray:self.clientArray];
   for (ClientModel *model in array) {
    if (model.clientSocket == client_socket) {
     [self.clientNameArray removeObject:model.clientName];
     [self.clientArray removeObject:model];
    }
   }

   close(client_socket);

   break;
  }
 }
}

 //检查接受到的字符串
- (void)checkRecvStr:(NSString*)str andClientSocket:(int)socket{
 if ([str hasPrefix:@"name:"]) {
  NSString *name = [str substringFromIndex:5];

  ClientModel *model = [[ClientModel alloc] init];
  model.clientSocket = socket;
  model.clientName = name;


  if (self.clientArray.count > 0) {
   int flag = 999;
   //用户名不能相同
   int i = 0;

   for (ClientModel *client in self.clientArray) {

    //改名
    if (client.clientSocket == socket) {
     NSString *oldName = self.clientNameArray[i];
     self.clientNameArray[i] = name;
     self.clientArray[i] = model;

     for (ClientModel *oldclient in self.clientArray) {
      [self sendMsg:[NSString stringWithFormat:@"%@ 改名 %@",oldName,name] toClient:oldclient.clientSocket];
      [self showLogsWithString:[NSString stringWithFormat:@"%@ 改名 %@",oldName,name]];
      NSString *list = [self.clientNameArray componentsJoinedByString:@","];
      //向客户端推送当前在线列表
      [self sendMsg:[NSString stringWithFormat:@"list:%@",list] toClient:oldclient.clientSocket];
     }

     flag = 2;

    }else{
     if ([client.clientName isEqualToString:model.clientName]) {
      //用户名已存在
      flag = 1;
      break;
     }
    }
    i++;

   }
   if (flag != 1 & flag != 2) {
    [self.clientArray addObject:model];
    [self.clientNameArray addObject:model.clientName];
    //向客户端推送当前在线列表
    for (ClientModel *client in self.clientArray) {
     [self sendMsg:[NSString stringWithFormat:@"%@,上线了",name] toClient:client.clientSocket];
     NSString *list = [self.clientNameArray componentsJoinedByString:@","];
     //向客户端推送当前在线列表
     [self sendMsg:[NSString stringWithFormat:@"list:%@",list] toClient:client.clientSocket];
    }

    //给当前客户端发送一条欢迎信息
    NSString *msg = [NSString stringWithFormat:@"Welcome %@ !",name];
    [self sendMsg:msg toClient:socket];
    [self showLogsWithString:msg];

   }else if (flag == 1){
    [self sendMsg:@"注册用户名失败,用户名已经存在,请重新设置用户名" toClient:socket];
    [self showLogsWithString:[NSString stringWithFormat:@"socket %d 注册用户名失败,设置的用户名已经存在",socket]];

    for (ClientModel *model in self.clientArray) {

     [name isEqualToString:model.clientName];
    }



   }
  }else{
   [self.clientArray addObject:model];
   [self.clientNameArray addObject:model.clientName];
   //向客户端推送当前在线列表
   //给当前客户端发送一条欢迎信息
   NSString *msg = [NSString stringWithFormat:@"Welcome %@ !",name];
   [self sendMsg:msg toClient:socket];
   [self showLogsWithString:msg];

   NSString *list = [self.clientNameArray componentsJoinedByString:@","];
   //向客户端推送当前在线列表
   [self sendMsg:[NSString stringWithFormat:@"list:%@",list] toClient:socket];

  }

 }
 //给某人发消息
 else if ([str hasPrefix:@"to:"]){
  NSRange nameRange = [str rangeOfString:@"*"];
  NSString *name = [str substringWithRange:NSMakeRange(3, nameRange.location-3)];
  NSString *cOntent= [str substringFromIndex:nameRange.location+1];
  NSString *fromClientName;
  //找出发送者
  for (ClientModel *model in self.clientArray) {
   if (socket == model.clientSocket) {
    fromClientName = model.clientName;
    break;
   }
  }

  //给目标发送信息
  for (ClientModel *model in self.clientArray) {
   if ([name isEqualToString:model.clientName]) {
    NSString *msg = [NSString stringWithFormat:@"%@ to you\n%@",fromClientName,content];
    [self sendMsg:msg toClient:model.clientSocket];

    [self showLogsWithString:[NSString stringWithFormat:@"%@ 发送给 %@ 内容是:%@",fromClientName,name,content]];
    break;

   }
  }

 }
}

//给客户端发送信息
- (void)sendMsg:(NSString*)msg toClient:(int)socket{
 char *buf[1024] = {0};
 const char *p1 = (char*)buf;
 p1 = [msg cStringUsingEncoding:NSUTF8StringEncoding];
 send(socket, p1, 1024, 0);
}

 //在界面上显示日志
- (void)showLogsWithString:(NSString*)str {
 dispatch_async(dispatch_get_main_queue(), ^{
  NSString *newStr = [NSString stringWithFormat:@"\n%@",str];
  self.textView.string = [self.textView.string stringByAppendingString:newStr];
 });
}

@end

客户端

由于客户端设计的就比较简单,所以代码量也很少,全给你.

#import "ViewController.h"
#include 
#include 
#include 
@interface ViewController ()
//服务器socket
@property (nonatomic,assign)int server_socket;

//UI
@property (weak, nonatomic) IBOutlet UITextField *userNameField;
@property (weak, nonatomic) IBOutlet UITextView *chatView;
@property (weak, nonatomic) IBOutlet UITextField *msgField;
@property (weak, nonatomic) IBOutlet UILabel *toName;
@property (weak, nonatomic) IBOutlet UIView *onlineUserView;
@property (nonatomic,strong)UITableView *onlineTable;

//user列表
@property (nonatomic,strong)NSMutableArray *userArray;

@end

@implementation ViewController
- (NSMutableArray *)userArray {
 if (!_userArray) {
  _userArray = [NSMutableArray array];
 }
 return _userArray;
}
- (void)viewDidLoad {
 [super viewDidLoad];
 [self.userNameField becomeFirstResponder];
 self.userNameField.text = @"";
 self.msgField.text = @"";
 //添加table用户列表
 self.OnlineTable= [[UITableView alloc] initWithFrame:self.onlineUserView.frame style:UITableViewStylePlain];
 self.onlineTable.delegate = self;
 self.onlineTable.dataSource = self;
 [self.view addSubview:self.onlineTable];

 int server_socket = socket(AF_INET, SOCK_STREAM, 0);
 if (server_socket == -1) {
  NSLog(@"创建失败");
 }else{
  //绑定地址和端口
  struct sockaddr_in server_addr;
  server_addr.sin_len = sizeof(struct sockaddr_in);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(1234);
  server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  bzero(&(server_addr.sin_zero), 8);

  //接受客户端的链接
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  dispatch_async(queue, ^{
   //创建新的socket
   int aResult = connect(server_socket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_in));
   if (aResult == -1) {
    NSLog(@"链接失败");
   }else{
    self.server_socket = server_socket;
    [self acceptFromServer];
   }
  });
 }
}

//从服务端接受消息
- (void)acceptFromServer{
 while (1) {
  //接受服务器传来的数据
  char buf[1024];
  long iReturn = recv(self.server_socket, buf, 1024, 0);
  if (iReturn>0) {
   NSString *str = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];

   //筛选前缀
   if ([str hasPrefix:@"list:"]) {
    NSString *arrayStr = [str substringFromIndex:5];
    NSArray *list = [arrayStr componentsSeparatedByString:@","];
    self.userArray = [NSMutableArray arrayWithArray:list];
    dispatch_async(dispatch_get_main_queue(), ^{
     [self.onlineTable reloadData];
    });
    NSLog(@"当前在线用户列表:%@",arrayStr);
   }else{
    //回到主线程 界面上显示内容
    [self showLogsWithString:str];
   }

  }else if (iReturn == -1){
   NSLog(@"接受失败-1");
   break;
  }
 }
}

//在界面上显示日志
- (void)showLogsWithString:(NSString*)str {
 dispatch_async(dispatch_get_main_queue(), ^{
  NSString *newStr = [NSString stringWithFormat:@"\n%@",str];
  self.chatView.text = [self.chatView.text stringByAppendingString:newStr];
 });
}

- (void)didReceiveMemoryWarning {
 [super didReceiveMemoryWarning];
 // Dispose of any resources that can be recreated.
}

//设置用户名
- (IBAction)clickSetUserName:(id)sender {
 NSString *msg = [NSString stringWithFormat:@"name:%@",self.userNameField.text] ;
 [self sendMsg:msg];
// [self showLogsWithString:msg];
 [self.msgField becomeFirstResponder];
}

//发送信息
- (IBAction)clickSendMsg:(id)sender {
 if ([self.msgField.text isEqualToString:@""] || ![self.userArray containsObject:self.userNameField.text] || [self.toName.text isEqualToString:self.userNameField.text]) {
  [self showLogsWithString:@"请设置用户名、检查发送对象、消息不能为空"];
  return;
 }
 NSString *msg = [NSString stringWithFormat:@"to:%@*%@",self.toName.text,self.msgField.text];
 [self sendMsg:msg];
 NSString *displayMsg = [NSString stringWithFormat:@"to:%@\n%@",self.toName.text,self.msgField.text];
 [self showLogsWithString:displayMsg];
 self.msgField.text = @"";

}

//给客户端发送信息
- (void)sendMsg:(NSString*)msg {
 char *buf[1024] = {0};
 const char *p1 = (char*)buf;
 p1 = [msg cStringUsingEncoding:NSUTF8StringEncoding];
 send(self.server_socket, p1, 1024, 0);
}

#pragma mark - TableViewDelegate & dataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
 return self.userArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
 static NSString *cellId = @"onlinetableviewcellid";
 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
 if (cell == nil) {
  cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
 }else{
  NSLog(@"cell重用了");
 }
 cell.textLabel.text = self.userArray[indexPath.row];
 return cell;
}

//点击cell
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
 self.toName.text = self.userArray[indexPath.row];
 [self.msgField becomeFirstResponder];
}
@end

Demo地址:IMsocketDemo_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
author-avatar
mobiledu2502878137
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有