socket

mark1
mark2

iOS之 TCP socket网络编程 demo

iOS socket通信(不使用框架的简单实例)

gethostbyname()函数说明

这篇不错

这篇比较详尽,但是举的例子不是最底层的

关于iOS socket都在这里了

IOS Socket使用大全 -将持续更新

深入浅出Cocoa之Bonjour网络编程

深入浅出Cocoa-iOS网络编程之Socket


Cocoa层:NSURL,Bonjour,Game Kit,WebKit
Core Foundation层:基于 C 的 CFNetwork 和 CFNetServices
OS层:基于 C 的 BSD socket

通常我们无需与 socket 打交道,我们会使用经 Cocoa 封装的 CFNetwork 和 Bonjour 来完成大多数工作。注:cocoa 很多组件都有两种实现,一种是基于 C 的以 CF 开头的类(CF=Core Foundation),这是比较底层的;另一种是基于 Obj-C 的以 NS 开头的类(NS=Next Step),这种类抽象层次更高,易于使用。对于网络框架也一样。Bonjour 中 NSNetService 也有对应的 CFNetService,NSInputStream 有对应的 CFInputStream。

Socket 相当于一条通信信道,应用程序通过创建 socket,然后使用这个 socket 连接到其他应用程序进行数据交换。我们可以通过同一个 socket 来发送数据或者接收数据。每个 socket 有一个 ip 地址和 port(通信端口,介于 1 ~ 65535之间)。

Stream 是传送数据的单向通道,正因为是单向的,所以我们有输入/输出两种 streams:instream/outstream。stream 只是临时缓存数据,我们需要将它与文件或内存绑定,从而可以从/向文件或内存中读/写数据。

Stream 是传送数据的单向通道,正因为是单向的,所以我们有输入/输出两种 streams:instream/outstream。stream 只是临时缓存数据,我们需要将它与文件或内存绑定,从而可以从/向文件或内存中读/写数据。我们使用 stream 结合 socket 在网络上传送和接收数据。

至于为什么stream要结合socket来使用,我的初步判断是stream是其一种方式,一种工具来的吧

Bonjour(法语中的你好)是一种能够自动查询接入网络中的设备或应用程序的协议。Bonjour 抽象掉 ip 和 port 的概念,让我们聚焦于更容易为人类思维理解的 service。通过 Bonjour,一个应用程序 publish 一个网络服务 service,然后网络中的其他程序就能自动发现这个 service,从而可以向这个 service 查询其 ip 和 port,然后通过获得的 ip 和 port 建立 socket 链接进行通信。通常我们是通过 NSNetService 和 NSNetServiceBrowser 来使用 Bonjour 的,前者用于建立与发布 service,后者用于监听查询网络上的 service。

Bonjour的意思是不是仅仅对ip和port做一个封装呢?

Run loops 简介:
run loop 是 thread 中的消息处理循环,有事件来则处理,无事件则啥也不做。cocoa 中的 run loop 可以处理用户 UI 消息,网络连接消息,timer 消息等。我们也可以添加其他的消息来源,如 socket 和 stream,从而让 run loop 也可以处理它们。

从这张图中我们可以看出,socket的上层就是NSStream。

重点摘要:

1.客户端如何知道怎样连接到服务器呢?每一个网络终端必须有独一无二的 ip 和 port,ip 地址是由动态获取的或由用户设定的,因此我们在这里无需操心 ip 地址问题,因此在代码中我们使用了 INADDR_ANY。那又如何设定我们想要监听的 port 呢?一些服务必须监听约定的 port 才能工作,比如 80,20, 21等端口都是有约定用途的。在这里我们把端口设定问题交给 OS 来处理,OS 会为我们设定一个没有被占用的 port。为了实现这个目的,我们传入 port 为 0。为了让其他客户端能够连接到服务器,我们需要告知其他客户端服务器实际使用的 port,因此,我们在 createServer 方法 PART 3中获取实际使用 port。


代码举例:

服务器端:(OC版)
h

#import <Foundation/Foundation.h>

@interface MyServer : NSObject{
    BOOL isClosed;
}
// 初始化服务器
-(void) initServer;
// 读客户端数据
-(void) readData:(NSNumber*) clientSocket;
// 向客户端发送数据
-(void) sendData:(const char*) data;
// 在新线程中监听客户端
-(void) startListenAndNewThread;
-(void) closeServer;
@end

m


#import "MyServer.h"
#include<stdio.h>
#include<unistd.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>

#define PORT 6677 //port
#define MAXDATASIZE 100   //maxDataSize
#define LENGTH_OF_LISTEN_QUEUE  20   //length_of_listen_queue
#define BUFFER_SIZE 1024    //buffer_size
#define THREAD_MAX    5    //thread_max
NSLock *lock;  
@implementation MyServer
// 初始化服务器
-(void) initServer{
    
    /*
     服务器的代码:
     1.设置socket,设置好端口号,地址,长度。
     -最重要的是要调用bind()函数对socket进行绑定;
     2.绑定成功后success为YES,进入第二步,调用listen()开始监听
     3.如果有客户端连接到了服务器,进入第三步。
     -注意每个客户端连接服务器后,服务器都会分配一个端口给客户端,并在这个端口上进行通信。然后服务器继续在设定的端口上继续等待。
     */
    
    
    //设置一个socket地址结构server_addr,代表服务器internet地址, 端口
    //ps_1;socket的地址接口是结构体来的?
    struct sockaddr_in server_addr;
    
    bzero(&server_addr,sizeof(server_addr)); //把一段内存区的内容全部设置为0
    //ps_2;为什么一定要手动调用bzero函数将其设置为0
    
    server_addr.sin_family = AF_INET;
    //ps_3;协议族,称为family也是够
    
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    //ps_4;设定地址为 所有地址 本地网卡:回环网卡,。。。 无线网卡。什么鬼
    
    server_addr.sin_port = htons(PORT);
    //ps_5;端口,所有在控制台上显示的端口号并不是你所设定的。而是系统自定分配的。(如果没有对socket有比较好的了解,这里很容易会没注意到,系统的也就是说要调用系统的方法咯,htons)
    
    //创建用于internet的流协议(TCP)socket,
    //用server_socket代表服务器socket
    int server_socket = socket(AF_INET,SOCK_STREAM,0);//sock_stream
    //ps_6;这个方法就是创建socket了,貌似也不用传什么参数嘛,么么哒
    if( server_socket < 0)
    {
        printf("Create Socket Failed!");
        exit(1);
    }
    //ps_7;听说最重要的是bind(),绑定函数喔,首先要绑地址结构喔,(吐槽一下,这个socket对象,或许没有对象的概念,真麻烦,什么都要自己搞)
    //把socket和socket地址结构联系起来
    if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
    {
        printf("Server Bind Port : %d Failed!", PORT); 
        exit(1);
    }
    //ps_8;其实有个疑问,为什么很多东西都要知道尺寸能(sizeof呢)
    
    //server_socket用于监听
    if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )
    {
        printf("Server Listen Failed!"); 
        exit(1);
    }
    //ps_9;绑定后就开始监听了,大功告成了?然而并没有
    
    isClosed = NO;
    
    while(!isClosed) //服务器端要一直运行
    {
        printf("Server start......\n");
        //定义客户端的socket地址结构client_addr
        struct sockaddr_in client_addr;
        
        socklen_t length = sizeof(client_addr);
        
        //接受一个到server_socket代表的socket的一个连接
        //如果没有连接请求,就等待到有连接请求--这是accept函数的特性
        //accept函数返回一个新的socket,这个socket(new_server_socket)用于同连接到的客户的通信
        //new_server_socket代表了服务器和客户端之间的一个通信通道
        //accept函数把连接到的客户端信息填写到客户端的socket地址结构client_addr中
        //ps_11;说真的,这我真忍不住吐槽一下了,你说accept函数会把客户端的socket地址存到client_addr中,但是拜托,你现在是把人家当做参数传进去的哟,我就想问一句,那来的值?还填写到client_addr中,咋不上天啊
        int new_client_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);
        //ps_10;如果是oc的话,会说返回一个socket对象,不过这里很明显并没有,而是返回了一个int的基本数据类型,并且还称之为通信通道。把一个int数据称之为socket?称之为通道?简直不可思议
        
        if ( new_client_socket < 0)
        {
            printf("Server Accept Failed!/n");
            break;
        }
        
        printf(" one client connted..\n");
        [NSThread detachNewThreadSelector:@selector(readData:) 
            toTarget:self 
            withObject:[NSNumber numberWithInt:new_client_socket]];
    }
    //关闭监听用的socket
    close(server_socket);
    NSLog(@"%@",@"server closed....");
}

//ps_12;每个客户端连接服务器后,服务器都会分配一个端口给客户端,然后服务器继续在设定的端口上继续等待。


/****
 ***
 ps_13;客户端和服务器之间的数据格式是以NSData的哟,注意了哦,
 ***
 */
// 读客户端数据
-(void) readData:(NSNumber*) clientSocket{
    char buffer[BUFFER_SIZE];
    int intSocket = [clientSocket intValue];
    
    while(buffer[0] != '-'){
        //ps_14;不等于小横线?什么鬼
        
        bzero(buffer,BUFFER_SIZE);
        //ps_15;又把内存清空
        
        //接收客户端发送来的信息到buffer中
        recv(intSocket,buffer,BUFFER_SIZE,0);
        //ps_16;又见把传入参数当接收值的这种逆天的设定
        
        printf("client:%s\n",buffer);
    }
    //关闭与客户端的连接
    printf("client:close\n");
    close(intSocket); 

}
// 向客户端发送数据
-(void) sendData:(const char*) data{
    
}
// 在新线程中监听客户端
-(void) startListenAndNewThread{
    [NSThread detachNewThreadSelector:@selector(initServer) 
                             toTarget:self withObject:nil];
}
-(void) closeServer{
    isClosed = YES;
}
@end

客户端(如果是本地的进行测试的,记得写上本地的IP地址作为目标IP,否则连不上的)

h

#import <UIKit/UIKit.h>

@interface QQViewController : UIViewController{
    int toServerSocket;
}

@property(strong,nonatomic) IBOutlet UITextField* textIp;
@property(strong,nonatomic) IBOutlet UITextField* textPort;
@property(strong,nonatomic) IBOutlet UITextField* textMessage;

-(IBAction)btnConnServer:(id)sender;
-(IBAction)btnSendMessage:(id)sender;
-(IBAction) hiddenKeyboard:(id)sender;
-(void) sendToServer:(NSString*) message;

@end

m

#import "QQViewController.h"
#include<stdio.h>
#include<unistd.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#define BUFFER_SIZE 1024

@interface QQViewController ()

@end

@implementation QQViewController
@synthesize textIp;
@synthesize textMessage;
@synthesize textPort;

//ps_1;在客户端中,是不用绑定端口的
- (void)viewDidLoad
{
    [super viewDidLoad];
    toServerSocket = 0;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    if(toServerSocket !=0){
        [self sendToServer:@"-"];
        close(toServerSocket);
    }
}
-(IBAction)btnConnServer:(id)sender{
    NSLog(@"conn server...");
    
    if(toServerSocket !=0){
        [self sendToServer:@"-"];
        close(toServerSocket);
    }
    
    struct hostent *he; 
    struct sockaddr_in server;
    
    NSString *ip = self.textIp.text;
    NSString *port = self.textPort.text;
    
    /*
     ps_3;gethostbyname()函数说明——用域名或主机名获取IP地址,传出值,是一个hostent的结构
     */
    if((he = gethostbyname([ip cStringUsingEncoding:NSUTF8StringEncoding])) == NULL)
    {
        printf("gethostbyname error/n");
        //exit(1);
    }
    //ps_2;这里应该是判断是不是目标socket吧
    if((toServerSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("socket() error /n");
        //exit(1);
    }
    bzero(&server, sizeof(server));
    //ps_4;又清空内存
    
    server.sin_family = AF_INET;
    server.sin_port = htons([port intValue]);
    
    server.sin_addr = *((struct in_addr *)he->h_addr);
    //ps_5;这个是服务器的地址?
    
    if(connect(toServerSocket, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        printf("\n connetc() error ");
       // exit(1);
    }
}
-(IBAction)btnSendMessage:(id)sender{
    [self sendToServer:textMessage.text];
}
-(void) sendToServer:(NSString*) message{
    NSLog(@"send message to server...");
    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);
    //Byte b;
    const char* talkData = 
    [ message cStringUsingEncoding:NSUTF8StringEncoding];
    
    //发送buffer中的字符串到new_server_socket,实际是给客户端
    send(toServerSocket,talkData,[message length],0);

}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
-(void) hiddenKeyboard:(id)sender{
    [sender resignFirstResponder];
}
@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

  • 一: 网络各个协议:TCP/IP、SOCKET、HTTP 网络七层由下往上分别为物理层、数据链路层、网络层、传输层...
    iYeso阅读 1,435评论 0 13
  • Socket基础概念 网络中进程之间如何通信? 网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则...
    DiamondsAndRust阅读 4,750评论 2 54
  • 最近在学习Python看了一篇文章写得不错,是在脚本之家里的,原文如下,很有帮助: 一、网络知识的一些介绍 soc...
    qtruip阅读 2,708评论 0 6
  • 对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想...
    yuantao123434阅读 5,457评论 1 97
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,652评论 18 139