iOS Socket(2) —— BSDSocket的使用

BSDSocket

BSDSocket是UNIX的Socket框架,iOS的系統內核即是UNIX,Xcode能夠直接使用BSDSockethtml

BSDSocket 的函數

  • 建立Socket:

int socket(int domain, int type, int protocol)git

  • domain: 協議族,例如:AF_INET(ipv4),AF_INET6(ipv6)
  • type:Socket 類型,例如:SOCK_STREAM(流式/TCP),SOCK_DGRAM (數據報式/UDP)
  • protocol:指定協議,例如:IPPROTO_TCP(TCP 傳輸協議),IPPROTO_UDP(UDP 傳輸協議),當protocol = 0 時,更加type參數自動選擇類型
  • 返回值:若是調用成功就返回新建立的套接字的描述符(大於 0),若是失敗就返回 INVALID_SOCKET(Linux 下失敗返回 -1)
  • 綁定ip 地址 + 端口

int bind(int sockfd, const struct sockaddr * addr, socklen_t addrlen)github

  • sockfd :套接字描述符,就是上面socket()的返回值
  • addr :是一個 sockaddr 結構指針,該結構中包含了要結合的地址和端口號
  • addrlen:addr 的長度
  • 返回值:若是函數執行成功,返回值爲 0,不然爲 -1
  • 監聽端口

int listen(int sockfd, int backlog)bash

  • sockfd :套接字描述符
  • backlog:socket 能夠排隊鏈接的最大數
  • 返回值:若是函數執行成功,返回值爲 0,不然爲 -1
  • 接受鏈接

int accept(int sockfd, struct sockaddr * addr, socklen_t * addrlen)服務器

  • sockfd :監聽套接字描述符
  • addr :返回客戶端的地址
  • addrlen:addr 的長度
  • 返回值:成功返回由內核自動生成的一個全新的描述符(即鏈接成功的客戶端的套接字描述符),失敗返回 -1
  • 鏈接

int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen)網絡

  • sockfd :套接字描述符
  • addr :指定數據發送的目的地,也就是服務器端的地址
  • addrlen:addr 的長度
  • 返回值:若是函數執行成功,返回值爲 0,不然爲 -1
  • 斷開鏈接

int close(int sockfd)數據結構

  • sockfd :套接字描述符
  • 返回值:若是函數執行成功,返回值爲 0,不然爲 -1
  • 發送數據

ssize_t write(int sockfd, const void * buf, size_t size)框架

  • sockfd:套接字描述符
  • buf :發送內容地址,message.UTF8String 將字符串轉換成 UTF8 的 ASCII 碼,一個漢字須要 3 個字節
  • size :發送內容長度,是字節的個數,需使用 strlen() 計算全部字節的長度
  • 返回值:若是成功,則返回發送的字節數,失敗則返回 -1,並設置 error 變量。 若是錯誤爲 EINTR 表示在寫的時候出現了中斷錯誤。若是爲 EPIPE 表示網絡鏈接出現了問題(對方已經關閉了鏈接)

ssize_t send(int sockfd, const void * buf, size_t size, int flags)dom

  • sockfd:套接字描述符
  • buf :發送內容地址,message.UTF8String 將字符串轉換成 UTF8 的 ASCII 碼,一個漢字須要 3 個字節
  • size :發送內容長度,是字節的個數,需使用 strlen() 計算全部字節的長度
  • 返回值:若是成功,則返回發送的字節數,失敗則返回 -1

ssize_t sendto(int sockfd, const void * buf, size_t size, int flags, const struct sockaddr * dest_addr, socklen_t addrlen)socket

  • sockfd :套接字描述符
  • buf :待發送數據的緩衝區
  • size :緩衝區長度,是字節的個數,需使用 strlen() 計算全部字節的長度
  • flags :調用方式標誌位, 通常爲 0, 改變 Flags,將會改變 Sendto 發送的形式
  • dest_addr:可選指針,指向目的套接字的地址
  • addrlen :dest_addr 的長度
  • 返回值:若是成功,則返回發送的字節數,失敗則返回 -1

ssize_t sendmsg(int sockfd, const struct msghdr * msg, int flags)

  • sockfd:套接字描述符
  • msg :送數據的內容
  • flags :調用方式標誌位
  • 返回值:若是成功,則返回發送的字節數,失敗則返回 -1
  • 接受數據

ssize_t read(int sockfd, void * buf, size_t size)

  • sockfd:套接字描述符
  • buf :用於接收數據的緩衝區
  • size :緩衝區長度
  • 返回值:若是成功,返回實際所讀的字節數,若是返回的值是 0 表示已經讀到文件的結束了,小於 0 表示出現了錯誤。 若是錯誤爲 EINTR 說明讀是由中斷引發的,若是是 ECONNREST 表示網絡鏈接出了問題

ssize_t recv(int sockfd, void * buf, size_t size, int flags)

  • sockfd:套接字描述符
  • buf :用於接收數據的緩衝區
  • size :緩衝區長度
  • flags :指定調用方式,MSG_PEEK:查看當前數據,數據將被複制到緩衝區中,但並不從輸入隊列中刪除;MSG_OOB :指示接收到 out-of-band 數據(即須要優先處理的數據)
  • 返回值:若是成功,返回實際所讀的字節數,若是返回的值是 0 表示已經讀到文件的結束了,小於 0 表示出現了錯誤

ssize_t recvfrom(int sockfd, void * buf, size_t size, int flags, struct sockaddr * src_addr, socklen_t * addrlen)

ssize_t recvmsg(int sockfd, struct msghdr * msg, int flags)

  • sockfd:套接字描述符
  • buf :用於接收數據的緩衝區
  • size :緩衝區長度
  • 返回值: 若是成功,返回實際所讀的字節數,若是返回的值是 0 表示已經讀到文件的結束了,小於 0 表示出現了錯誤

ssize_t recvfrom(int sockfd, void * buf, size_t size, int flags, struct sockaddr * src_addr, socklen_t * addrlen);

  • sockfd :套接字描述符
  • buf :接收數據緩衝區
  • size :緩衝區長度
  • flags :調用操做方式。是如下一個或者多個標誌的組合體,可經過 or 操做連在一塊兒: MSG_DONTWAIT:操做不會被阻塞 MSG_ERRQUEUE:指示應該從套接字的錯誤隊列上接收錯誤值,依據不一樣的協議,錯誤值以某種輔佐性消息的方式傳遞進來, 使用者應該提供足夠大的緩衝區。致使錯誤的原封包經過 msg_iovec 做爲通常的數據來傳遞。致使錯誤 的數據報原目標地址做爲 msg_name 被提供。錯誤以 sock_extended_err 結構形態被使用 MSG_PEEK :指示數據接收後,在接收隊列中保留原數據,不將其刪除,隨後的讀操做還能夠接收相同的數據 MSG_TRUNC :返回封包的實際長度,即便它比所提供的緩衝區更長, 只對 packet 套接字有效 MSG_WAITALL :要求阻塞操做,直到請求獲得完整的知足。然而,若是捕捉到信號,錯誤或者鏈接斷開發生,或者下次被 接收的數據類型不一樣,仍會返回少於請求量的數據 MSG_EOR :指示記錄的結束,返回的數據完成一個記錄 MSG_CTRUNC :指明因爲緩衝區空間不足,一些控制數據已被丟棄 MSG_OOB :指示接收到 out-of-band 數據(即須要優先處理的數據) MSG_ERRQUEUE:指示除了來自套接字錯誤隊列的錯誤外,沒有接收到其它數據
  • src_addr:可選指針,指向裝有源地址的緩衝區
  • addrlen :可選指針,指向 address 緩衝區長度值
  • 返回值: 若是成功,返回實際所讀的字節數,若是返回的值是 0 表示已經讀到文件的結束了,小於 0 表示出現了錯誤。
  • 其餘
struct sockaddr_in {
        __uint8_t       sin_len;            // 地址長度
        sa_family_t     sin_family;         // IP 地址協議族,必須設爲 AF_INET
        in_port_t       sin_port;           // 通訊端口
        struct in_addr  sin_addr;           // 以網絡字節排序的 4 字節 IPv4 地址
        char            sin_zero[8];        // 填充項,是爲了讓 sockaddr 與 sockaddr_in 兩個數據結構保持大小相同而保留的空字節
    };

    struct sockaddr {
        __uint8_t       sa_len;             // 地址長度
        sa_family_t     sa_family;          // IP 地址協議族,必須設爲 AF_INET
        char            sa_data[14];        // 地址值
    };

    struct in_addr {
        uint32_t        s_addr;             // 按照網絡字節順序存儲 IP 地址
    };

        sockaddr_in 和 sockaddr 是並列的結構,指向 sockaddr_in 的結構體的指針也能夠指向 sockaddr 的結構體,並代替它。
    也就是說,你可使用 sockaddr_in 創建你所須要的信息。
複製代碼
struct sockaddr_in address;

    // 清空內存
    memset(&address, 0, sizeof(address));                     // 清空指向的內存中的存儲內容,由於分配的內存是隨機的,
                                                              // 若是不清空可能會由於垃圾數據產生沒必要要的麻煩
    bzero(& address, sizeof(address));                        // 清空指向的內存中的存儲內容

    // 設置地址值
    ser_addr.sin_len        = sizeof(address);                // 地址長度
    address.sin_family      = AF_INET;                        // 協議族
    address.sin_port        = htons(12345);                   // 端口數據高位在前低位在後 12345 => 3039 => 3930
    address.sin_addr.s_addr = inet_addr("192.168.88.100");    // inet_addr 函數能夠把 ip 地址轉換成一個整數

    // 設置全部地址值
    address.sin_addr.s_addr = INADDR_ANY;                     // 指定地址爲 0.0.0.0 的地址,這個地址事實上表示不肯定地址,
                                                              // 或全部地址、全部 ip、任意地址。通常來講,在各個系統中均定義成爲 0 值

    // 獲取地址值
    int ip_port   = ntohs(address.sin_port);                  // 獲取端口,如獲取到:53746
    char *ip_addr = inet_ntoa(address.sin_addr);              // 獲取 IP 地址,如獲取到:192.168.88.100

    // 獲取本地地址
    socklen_t addrLen = sizeof(address);
    getsockname(self.clientSockfd, (struct sockaddr *)&address, &addrLen);
    int ip_port   = ntohs(address.sin_port);                  // 本地端口
    char *ip_addr = inet_ntoa(address.sin_addr);              // 本地 IP 地址
複製代碼

實踐

  1. iOS中使用BSDSocket,首先要引入頭文件:
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <ifaddrs.h>

複製代碼
  1. 建立服務端,我使用的是Mac界面開發。
#import "ViewController.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
@interface ViewController ()
{
    NSInteger _protocolIndex;//0:TCP,1:UDP
    NSString *_loc_ipAdr,*_loc_port,*_des_ipAdress,*_des_port;
    int _tcp_serverSockfd,_udp_serverSockfd;//服務端套接字描述符
    int _clientSockfd;//客戶端端套接字描述符
    int _errCode;//綁定時的返回值
    
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _loc_ipAdr = @"127.0.0.1";
    _loc_port = @"10000";
    
    _des_ipAdress = [self getIPAddress];
    _des_port = @"10001";
    [NSThread detachNewThreadSelector:@selector(creatTCPSocket) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(creatUDPSocket) toTarget:self withObject:nil];
    // Do any additional setup after loading the view.
}

#pragma mark - 建立Socket
- (void)creatTCPSocket{
    
   _tcp_serverSockfd  = socket(AF_INET, SOCK_STREAM, 0);
    if (_tcp_serverSockfd > 0 ) {
        NSLog(@"TCP socket建立成功");
        [self TCPBind];
    }else {
        NSLog(@"TCP socket建立失敗");
        
    }
}

- (void)creatUDPSocket{
    _udp_serverSockfd  = socket(AF_INET, SOCK_DGRAM, 0);
    if (_udp_serverSockfd > 0 ) {
        NSLog(@"UDP socket建立成功");
        [self UDPBind];
    }else {
        NSLog(@"UDP socket建立失敗");
        
    }
}


#pragma mark - 綁定IP地址和端口號

- (void)TCPBind {
    //獲取本地地址
    struct sockaddr_in loc_addr;
    //清空指向的內存中的存儲內容,由於分配的內存是隨機的
    memset(&loc_addr, 0, sizeof(loc_addr));
//    loc_addr.sin_len = sizeof(struct sockaddr_in);
    //設置協議族
    loc_addr.sin_family = AF_INET;
    //設置端口
    loc_addr.sin_port = htons(_loc_port.intValue);
    //設置IP地址
    loc_addr.sin_addr.s_addr = inet_addr(_loc_ipAdr.UTF8String);
    //綁定
    _errCode = bind(_tcp_serverSockfd, (const struct sockaddr *)&loc_addr,sizeof(loc_addr) );
    if (_errCode == 0) {
        NSLog(@"TCP socket綁定成功");
        [self listen];
        
    }else {
        NSLog(@"TCP socekt綁定失敗");
        
    }
    
    
  
    
}
- (void)UDPBind {
    //獲取本地地址
    struct sockaddr_in loc_addr;
    //清空指向的內存中的存儲內容,由於分配的內存是隨機的
    memset(&loc_addr, 0, sizeof(loc_addr));
    //    loc_addr.sin_len = sizeof(struct sockaddr_in);
    //設置協議族
    loc_addr.sin_family = AF_INET;
    //設置端口
    loc_addr.sin_port = htons(_loc_port.intValue);
    //設置IP地址
    loc_addr.sin_addr.s_addr = inet_addr(_loc_ipAdr.UTF8String);
    //綁定
    int udpCode = bind(_udp_serverSockfd, (const struct sockaddr *)&loc_addr,sizeof(loc_addr) );
    
    if (udpCode == 0) {
        NSLog(@"UDP socket綁定成功");
        [self UDPRecv];
        
    }else{
        NSLog(@"UDP socekt綁定失敗");
        
    }
}
#pragma mark - 監聽、阻塞等待客服端的鏈接請求、接收消息

- (void)listen {
    _errCode = listen(_tcp_serverSockfd, 9);//9:最大鏈接個數
    
    if (_errCode == 0) {
        NSLog(@"socket監聽成功");
        //使用循環,持續監聽
        while (YES) {
            //鏈接的客戶端的地址
            struct sockaddr_in client_addr;
            socklen_t cli_addr_len = sizeof(client_addr);
            //阻塞等待客服端的鏈接請求
            _clientSockfd = accept(_tcp_serverSockfd, (struct sockaddr *)&client_addr, &cli_addr_len);
            if (_clientSockfd != -1) {
                //鏈接成功
                NSLog(@"socket鏈接成功");

            }
            //建立一個字符串接收
            char buf[1024];
            do {
                // 返回讀取的字節數
                ssize_t recvLen = recv(_clientSockfd, buf, sizeof(buf), 0);
                
                if (recvLen > 0) {
                    NSString *recvStr = [NSString stringWithFormat:@"[TCP消息][來自客戶端%@:%@]:%@",_des_ipAdress,_des_port, [NSString stringWithUTF8String:buf]];
                    NSLog(@"%@",recvStr);
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self->_recTextView.string = recvStr;
                    });
                    
                }
                
            } while (strcmp(buf, "exit") != 0);
        }
        
        
    }else {
        NSLog(@"socekt監聽失敗");
        
    }
}

#pragma mark - UDP接收
- (void)UDPRecv{
    // 目標地址
    struct sockaddr_in des_addr;
    bzero(&des_addr, sizeof(des_addr));
    des_addr.sin_family      = AF_INET;
    des_addr.sin_port        = htons(_des_port.intValue);
    des_addr.sin_addr.s_addr = inet_addr(_des_ipAdress.UTF8String);
    
    char buf[1024];
    //清空指向的內存中的存儲內容
    bzero(buf, sizeof(buf));
    
    while(1) {
        
        // 接收數據
        socklen_t des_addr_len = sizeof(des_addr);
        ssize_t recvLen = recvfrom(_udp_serverSockfd, buf, sizeof(buf), 0, (struct sockaddr*)&des_addr, &des_addr_len);
        
        if (recvLen > 0) {
            NSString *recvStr = [NSString stringWithFormat:@"[UDP消息][來自客戶端%@:%@]:%@",_des_ipAdress,_des_port, [NSString stringWithUTF8String:buf]];
            NSLog(@"%@",recvStr);

            dispatch_async(dispatch_get_main_queue(), ^{
                self->_recTextView.string = recvStr;
            });
        }
    }
}



#pragma mark - 發送
- (IBAction)sendMsg:(id)sender {
    if (!self.textInput.stringValue.length) {
        return;
    }
    
    NSString *sendMsg = self.textInput.stringValue;
    
    ssize_t sendLen = 0;
    if (_protocolIndex == 0) {
        
        // 發送數據
        sendLen = send(_clientSockfd, sendMsg.UTF8String, strlen(sendMsg.UTF8String), 0);
        
    }
    
    if (_protocolIndex == 1) {
        
        // 發送數據
        // 目標地址
        struct sockaddr_in des_addr;
        bzero(&des_addr, sizeof(des_addr));
        des_addr.sin_family      = AF_INET;
        des_addr.sin_port        = htons(_des_port.intValue);
        des_addr.sin_addr.s_addr = inet_addr(_des_ipAdress.UTF8String);
        
        // 發送數據
        sendLen = sendto(_udp_serverSockfd, sendMsg.UTF8String, strlen(sendMsg.UTF8String), 0,
                         (struct sockaddr *)&des_addr, sizeof(des_addr));
        
    }
    
    if (sendLen > 0) {
        NSLog(@"發送成功");
    }else{
        NSLog(@"發送失敗");
    }
}
#pragma mark - 選擇協議
- (IBAction)choseProtocol:(NSComboBox *)sender {
    if ([sender.stringValue isEqualToString:@"TCP"]) {
        NSLog(@"選擇TCP");
        _protocolIndex = 0;

    }
    if ([sender.stringValue isEqualToString:@"UDP"]) {
        NSLog(@"選擇UDP");
        _protocolIndex = 1;

    }

    
}

- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}


#pragma mark - 獲取本地 IP 地址

- (NSString *)getIPAddress {
    
    NSString *address = @"error";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(&interfaces);
    
    if (success == 0) {
        
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        
        while (temp_addr != NULL) {
            
            if (temp_addr->ifa_addr->sa_family == AF_INET) {
                
                // Check if interface is en0 which is the wifi connection on the iPhone
                if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    
    // Free memory
    freeifaddrs(interfaces);
    return address;
}

複製代碼
  1. 建立客戶端
#import "ViewController.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
@interface ViewController (){
    NSInteger _protocolIndex;//0:TCP,1:UDP
    int _tcp_clientSockfd,_udp_clientSockfd;//客戶端端套接字描述符
    NSString *_loc_ipAdr,*_loc_port,*_des_ipAdress,*_des_port;

}
@property (weak, nonatomic) IBOutlet UITextField *sendTF;
@property (weak, nonatomic) IBOutlet UITextView *recvTextView;
@property (weak, nonatomic) IBOutlet UISegmentedControl *segmentControl;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _loc_ipAdr = [self getIPAddress];
    _loc_port = @"10001";
    
    _des_ipAdress = @"127.0.0.1";
    _des_port = @"10000";
    [self chose:self.segmentControl];
    
    [NSThread detachNewThreadSelector:@selector(creatTCPSocket) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(creatUDPSocket) toTarget:self withObject:nil];

    // Do any additional setup after loading the view, typically from a nib.
}

#pragma mark - 建立Socket
- (void)creatTCPSocket{
    
    _tcp_clientSockfd  = socket(AF_INET, SOCK_STREAM, 0);
    if (_tcp_clientSockfd > 0 ) {
        NSLog(@"TCP socket建立成功");
        [self connect];
        
    }else {
        NSLog(@"TCP socket建立失敗");
        
    }
}

- (void)creatUDPSocket{
    
    _udp_clientSockfd  = socket(AF_INET, SOCK_DGRAM, 0);
    if (_udp_clientSockfd > 0 ) {
        NSLog(@"UDP socket建立成功");
        
        [self UDPRecv];

        
    }else {
        NSLog(@"UDP socket建立失敗");
        
    }
}

#pragma mark - 鏈接服務器

- (void)connect {
    //獲取服務器地址
    struct sockaddr_in des_addr;
    //清空指向的內存中的存儲內容,由於分配的內存是隨機的
    memset(&des_addr, 0, sizeof(des_addr));
    //設置協議族
    des_addr.sin_family = AF_INET;
    //設置端口
    des_addr.sin_port = htons(_des_port.intValue);
    //設置IP地址
    des_addr.sin_addr.s_addr = inet_addr(_des_ipAdress.UTF8String);
    //鏈接
    
    int errCode = connect(_tcp_clientSockfd, (struct sockaddr *)&des_addr, sizeof(des_addr));
    
    if (errCode == 0) {
        NSLog(@"socket鏈接成功");
        [self TCPRecv];
        
    }else {
        NSLog(@"socekt鏈接失敗");
        
    }
    
}

#pragma mark - TCP接收
- (void)TCPRecv {
    char buf[1024];
    do {
        // 接收數據
        ssize_t recvLen = recv(_tcp_clientSockfd, buf, sizeof(buf), 0);
        
        if (recvLen > 0) {
            NSString *recvStr = [NSString stringWithFormat:@"[TCP消息][來自服務端%@:%@]:%@",_des_ipAdress,_des_port, [NSString stringWithUTF8String:buf]];
            NSLog(@"%@",recvStr);
            dispatch_async(dispatch_get_main_queue(), ^{
                self->_recvTextView.text = recvStr;
            });
        }
        
    } while (strcmp(buf, "exit") != 0);
}

#pragma mark - UDP接收

- (void)UDPRecv {
    // 本地地址
    struct sockaddr_in loc_addr;
    bzero(&loc_addr, sizeof(loc_addr));
    loc_addr.sin_port        = htons(_loc_port.intValue);
    loc_addr.sin_addr.s_addr = inet_addr(_loc_ipAdr.UTF8String);
    // 綁定
    int err = bind(_udp_clientSockfd, (const struct sockaddr *)&loc_addr, sizeof(loc_addr));
    
    if (err != 0) {
        NSLog(@"socket 綁定失敗");
        
    } else {
        
        NSLog(@"socket 綁定成功");
        // 目標地址
        struct sockaddr_in des_addr;
        bzero(&des_addr, sizeof(des_addr));
        des_addr.sin_family      = AF_INET;
        des_addr.sin_port        = htons(_des_port.intValue);
        des_addr.sin_addr.s_addr = inet_addr(_des_ipAdress.UTF8String);
        
        char buf[256];
        bzero(buf, sizeof(buf));
        
        while(1) {
            
            // 接收數據
            socklen_t des_addr_len = sizeof(des_addr);
            ssize_t recvLen = recvfrom(_udp_clientSockfd, buf, sizeof(buf), 0, (struct sockaddr*)&des_addr, &des_addr_len);
            
            if (recvLen > 0) {
                NSString *recvStr = [NSString stringWithFormat:@"[UDP消息][來自服務端%@:%@]:%@",_des_ipAdress,_des_port, [NSString stringWithUTF8String:buf]];
                NSLog(@"%@",recvStr);
                dispatch_async(dispatch_get_main_queue(), ^{
                    self->_recvTextView.text = recvStr;
                });
            }
        }
    }
    
}



- (IBAction)send:(id)sender {
    
    if (!self.sendTF.text.length) {
        return;
    }
    
    ssize_t sendLen = 0;
    if (_protocolIndex == 0) {
        
        // 發送數據
        sendLen = send(_tcp_clientSockfd, _sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String), 0);
        
    }
    
    if (_protocolIndex == 1) {
        
        // 發送數據
        // 目標地址
        struct sockaddr_in des_addr;
        bzero(&des_addr, sizeof(des_addr));
        des_addr.sin_family      = AF_INET;
        des_addr.sin_port        = htons(_des_port.intValue);
        des_addr.sin_addr.s_addr = inet_addr(_des_ipAdress.UTF8String);
        
        // 發送數據
        sendLen = sendto(_udp_clientSockfd, _sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String), 0,
                         (struct sockaddr *)&des_addr, sizeof(des_addr));
        
    }
    
    if (sendLen > 0) {
        NSLog(@"發送成功");
    }else{
        NSLog(@"發送失敗");
    }
    
    
}

- (IBAction)chose:(UISegmentedControl *)sender {
    
    _protocolIndex = sender.selectedSegmentIndex;
    
    if (sender.selectedSegmentIndex == 0) {
        NSLog(@"選擇TCP");
   
    }
    if (sender.selectedSegmentIndex == 1) {
        NSLog(@"選擇UDP");
    }
}

#pragma mark - 獲取本地 IP 地址

- (NSString *)getIPAddress {
    
    NSString *address = @"error";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(&interfaces);
    
    if (success == 0) {
        
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        
        while (temp_addr != NULL) {
            
            if (temp_addr->ifa_addr->sa_family == AF_INET) {
                
                // Check if interface is en0 which is the wifi connection on the iPhone
                if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    
    // Free memory
    freeifaddrs(interfaces);
    return address;
}



@end

複製代碼

Demo

  • 下載地址:github.com/namesubai/S…
  • 請先編譯服務端SocketDemoMac項目,再編譯客戶端SocketDemoMac項目
  • Demo界面效果截圖

image
image

參考資料

相關文章
相關標籤/搜索