ios是服务器发送消息,iOS 使用 socket 实现即时通信示例(非第三方库)

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

发布时间:2020-09-27 23:23:20

来源:脚本之家

阅读:71

作者:gwk_ios

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

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

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

效果

11a11a8b4e6adfe4e983abb4e7820865.gif

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

c6386ad88d30dfd5c48998525ef3f9e9.png

0f17d151427f841fbe9bb1acf4fe46b6.png

客户端A

87cd3d5b97b12cb77e7f2725d6f04d10.png

客户端B

模型图

d1932aa479a7a63c3455198870b8fb37.png

分析

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

服务器

抽象一点分为:

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

#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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值