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