首页 > 系统 > iOS > 正文

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

2020-07-26 02:56:07
字体:
来源:转载
供稿:网友

其实写这个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 <netinet/in.h>#include <sys/socket.h>#include <arpa/inet.h>

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

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

只有一个文件全给你

#import "ViewController.h"#include <netinet/in.h>#include <sys/socket.h>#include <arpa/inet.h>#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 < kMaxConnectCount; i++) {     //接受客户端的链接     [self acceptClientWithServerSocket:server_socket];    }   }  } }}- (void)setRepresentedObject:(id)representedObject { [super setRepresentedObject:representedObject]; // Update the view, if already loaded.}//创建线程接受客户端-(void)acceptClientWithServerSocket:(int)server_socket{ struct sockaddr_in client_address; socklen_t address_len; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{  //创建新的socket  while (1) {   int client_socket = accept(server_socket, (struct sockaddr*)&client_address,&address_len );   if (client_socket == -1) {    [self showLogsWithString:@"接受客户端链接失败"];    NSLog(@"接受客户端链接失败");   }else{    NSString *acceptInfo = [NSString stringWithFormat:@"客户端 in,socket:%d",client_socket];    [self showLogsWithString:acceptInfo];    //接受客户端数据    [self recvFromClinetWithSocket:client_socket];   }  } });}//接受客户端数据- (void)recvFromClinetWithSocket:(int)client_socket{ while (1) {  //接受客户端传来的数据  char buf[1024] = {0};  long iReturn = recv(client_socket, buf, 1024, 0);  if (iReturn>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 <netinet/in.h>#include <sys/socket.h>#include <arpa/inet.h>@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>//服务器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

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表