iOS-Socket开发学习笔记-1

BSD socket API

/**
 socket 创建并初始化 socket,返回该 socket 的文件描述符,如果描述符为 -1 表示创建失败。

 @param addressFamily   是 IPv4(AF_INET) 或 IPv6(AF_INET6)。
 @param type 表示        socket 的类型,通常是流stream(SOCK_STREAM) 或数据报文datagram(SOCK_DGRAM)
 @param protocol        参数通常设置为0,以便让系统自动为选择我们合适的协议,对于 stream socket 来说会是 TCP 协议(IPPROTO_TCP),而对于 datagram来说会是   UDP 协议(IPPROTO_UDP)。
 @return                返回该 socket 的文件描述符
 *//
int socket(int addressFamily, int type, int protocol);

/**
 服务器端侦听客户端的请求

 @param socketFileDescriptor 服务端socket
 @param backlogSize 客户端连接请求缓冲区队列的大小
 @return 0 成功或者其他 错误代号,(不是非0即真)
 */
int listen(int socketFileDescriptor, int backlogSize) __DARWIN_ALIAS(listen);


/**
 接受客户端连接请求并将客户端的网络地址信息保存到 clientAddress 中。
 
 当客户端连接请求被服务器接受之后,客户端和服务器之间的链路就建立好了,两者就可以通信了

 @param socketFileDescriptor 服务器的socket描述字
 @param address 指向struct sockaddr *的指针,用于返回客户端的协议地址
 @param addressStructLength address结构体数据长度
 @return 由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
 */
int accept(int socketFileDescriptor, struct sockaddr * __restrict address, socklen_t * __restrict addressStructLength)
__DARWIN_ALIAS_C(accept);

/**
 客户端向特定网络地址的服务器发送连接请求,连接成功返回0,失败返回 -1。
 当服务器建立好之后,客户端通过调用该接口向服务器发起建立连接请求。对于 UDP 来说,该接口是可选的,如果调用了该接口,表明设置了该 UDP socket 默认的网络地址。对 TCP socket来说这就是传说中三次握手建立连接发生的地。
 注意:该接口调用会阻塞当前线程,直到服务器返回。

 @param socketFileDescriptor 客户端sockets
 @param serverAddress 向数据结构sockaddr的指针,其中包括目的端口和IP地址,服务器的"结构体"地址;提示:C 语言中没有对象
 @param serverAddressLength 结构体数据长度
 @return 0 成功或者其他 错误代号,(不是非0即真)
 */
int connect(int socketFileDescriptor, const struct sockaddr *serverAddress, socklen_t serverAddressLength) __DARWIN_ALIAS_C(connect);


/**
 从 socket 中读取数据,读取成功返回成功读取的字节数,否则返回 -1。
 一旦连接建立好之后,就可以通过 send或者receive 接口发送或接收数据了。注意调用 connect 设置了默认网络地址的 UDP socket 也可以调用该接口来发送数据。

 @param socketFileDescriptor 客户端sockets
 @param buf 接受数据的buffer
 @param bufferLength buffer长度
 @param flags 接收方式,0表示阻塞,必须等待服务器返回数据
 @return 如果成功,则返回读入的字节数,失败则返回SOCKET_ERROR
 */
ssize_t recv(int socketFileDescriptor, void *buf, size_t bufferLength, int flags) __DARWIN_ALIAS_C(recv);


/**
 通过 socket 发送数据,发送成功返回成功发送的字节数,否则返回 -1。
 一旦连接建立好之后,就可以通过 send/receive 接口发送或接收数据了。注意调用 connect 设置了默认网络地址的 UDP socket 也可以调用该接口来接收数据。

 @param socketFileDescriptor    指定发送端套接字描述符
 @param buf                     存放应用程序要发送数据的缓冲区
 @param bufferLength            发送的数据的字节数
 @param flags                   发送方式,0表示阻塞,必须等待服务器返回数据
 @return                        如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR
 */
ssize_t send(int socketFileDescriptor, const void *buf, size_t bufferLength, int flags) __DARWIN_ALIAS_C(send);


/**
 将 socket 与特定主机地址与端口号绑定,成功绑定返回0,失败返回 -1。
 
 成功绑定之后,根据协议(TCPUDP)的不同,我们可以对 socket 进行不同的操作:
 UDP:因为 UDP 是无连接的,绑定之后就可以利用 UDP socket 传送数据了。
 TCP:而 TCP 是需要建立端到端连接的,为了建立 TCP 连接服务器必须调用 listen(int socketFileDescriptor, int backlogSize) 来设置服务器的缓冲区队列以接收客户端的连接请求,backlogSize 表示客户端连接请求缓冲区队列的大小。当调用 listen 设置之后,服务器等待客户端请求,然后调用 accept 来接受客户端的连接请求。

 @param socketFileDescriptor    主机socket
 @param addressToBind           数据结构sockaddr的指针,其中包括目的端口和IP地址,服务器的"结构体"地址;提示:C 语言中没有对象
 @param addressStructLength     sockaddr结构体数据长度
 @return                        0 成功/其他 错误代号,(不是非0即真)
 */
int bind(int socketFileDescriptor, const struct sockaddr *addressToBind, socklen_t addressStructLength) __DARWIN_ALIAS(bind);


/**
 服务器端侦听客户端的请求

 @param socketFileDescriptor 服务端socket
 @param backlogSize 客户端连接请求缓冲区队列的大小
 @return 0 成功/其他 错误代号,(不是非0即真)
 */
int listen(int socketFileDescriptor, int backlogSize) __DARWIN_ALIAS(listen);


/**
 接受客户端连接请求并将客户端的网络地址信息保存到 clientAddress 中。
 
 当客户端连接请求被服务器接受之后,客户端和服务器之间的链路就建立好了,两者就可以通信了

 @param socketFileDescriptor 服务器的socket描述字
 @param address 指向struct sockaddr *的指针,用于返回客户端的协议地址
 @param addressStructLength address结构体数据长度
 @return 由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
 */
int accept(int socketFileDescriptor, struct sockaddr * __restrict address, socklen_t * __restrict addressStructLength)
__DARWIN_ALIAS_C(accept);

一、客户端操作

  • 1、绑定地址和端口操作
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_len = sizeof(struct sockaddr_in);// 结构体长度
server_addr.sin_family = AF_INET; //sin_family指代协议族,在socket编程中只能是AF_INET
server_addr.sin_port = htons(1234);//存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//存储IP地址,使用in_addr这个数据结构
bzero(&(server_addr.sin_zero), 8);//是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节, 初始值应该使用函数 bzero() 来全部置零
  • 2、接受客户端的链接
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];
            }
        });
  • 3、从服务端接受消息
- (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;
        }
    }
}
  • 4、给客户端发送信息
- (void)sendMsg:(NSString*)msg {
    char *buf[1024] = {0};
    const char *p1 = (char*)buf;
    p1 = [msg cStringUsingEncoding:NSUTF8StringEncoding];
    send(self.server_socket, p1, 1024, 0);
}

二、服务端操作

  • 1、绑定地址和端口
    //创建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];
                }
            }
        }
    }
  • 2、创建线程接受客户端
- (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];
            }
        }
    });
}
  • 3、接受客户端数据
- (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;
        }
    }
}

你可能感兴趣的:(iOS-Socket开发学习笔记-1)