BSDSocket是UNIX的Socket框架,iOS的系统内核即是UNIX,Xcode能够直接使用BSDSockethtml
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)
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 地址
复制代码
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
复制代码
#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;
}
复制代码
#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
复制代码