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_第1张图片

从这张图中我们可以看出,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 

@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
#include
#include
#include
#include
#include
#include

#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 

@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
#include
#include
#include
#include
#include
#include
#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

你可能感兴趣的:(socket)