您的位置:首页 > 理论基础 > 计算机网络

网络编程CFSocketRef

2014-04-10 10:11 288 查看


CFSocketCreate

Creates a CFSocket object of a specified protocol and type.
CFSocketRef CFSocketCreate (    CFAllocatorRef allocator,    SInt32 protocolFamily,    SInt32 socketType,    SInt32 protocol,    CFOptionFlags callBackTypes,    CFSocketCallBack callout,    const CFSocketContext *context );


- (void)setTextInMainThread:(NSString *)text
{

NSRange endRange;

endRange.location =
[chatController.textView.text length];

endRange.length = [text length];

chatController.textView.text =
[chatController.textView.text stringByAppendingString:[@"server:
" stringByAppendingString:text]];;

[chatController.textView scrollRangeToVisible:endRange];

}

- (void)readStream

{

char buffer[255];

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

while (recv(CFSocketGetNative(_socket),
buffer, sizeof(buffer), 0))
{

NSString *s
= [NSString stringWithUTF8String:buffer];

[self performSelectorOnMainThread:@selector(setTextInMainThread:) withObject:s waitUntilDone:YES];

}

[pool release];

}

static void TCPServerConnectCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{

if (data != NULL)
{

UIAlertView *alert
= [[UIAlertView alloc] initWithTitle:@"" message:@"连接失败" delegate:nil cancelButtonTitle:@"关闭" otherButtonTitles:nil];

[alert show];

[alert release];

return;

}

TCPClientDemoAppDelegate *delegate = (TCPClientDemoAppDelegate *)info;

[delegate performSelectorInBackground:@selector(readStream) withObject:nil];

[delegate.connController dismissModalViewcontrollerAnimated:YES];

[[NSnotificationCenter defaultCenter] addObserver:delegate.chatController selector:@selector(keyboardWillShown

name:UIKeyboardWillShownotification object:nil];

[[NSnotificationCenter defaultCenter] addObserver:delegate.chatController selector:@selector(keyboardWillHidden

name:UIKeyboardWillHidenotification object:nil];

}

- (void) doConnect {

// CFSocketContext存放一些CFSocketRef的相关信息

CFSocketContext CTX = {0, self, NULL, NULL, NULL};

// CFSocketCreate函数是用来创建一个CFSocketRef,第一个参数是表示由系统默认的allocator来为CFSocketRef分配一个内存空间。

// 第二个参数表示profocolFamily(协议簇)

// 第三个参数表示socketType(socket的类型流、数据段、顺序包…)

// 第四个参数表示protocol(协议tcp、udp…)

// 第五个参数表示当socket的某个类型activity时,调用第六个参数的函数

// 第六个参数表示当socket发生第五个参数的类型的动作时,调用的函数

// 第七个参数表示一个保存CFSocket object对象的一些相关信息

_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketConnectCallBack,
TCPServerConnectCallBack, &CTX);

if (NULL == _socket)
{

UIAlertView *alert
= [[UIAlertView alloc] initWithTitle:@"" message:@"创建套接字失败" delegate:nil cancelButtonTitle:@"关闭" otherButtonTitles:nil];

[alert show];

[alert release];

}

// sockaddr_in 是一个struct,里面包含ip、端口等

struct sockaddr_in addr4;

memset(&addr4, 0, sizeof(addr4));//
memset表示将地址addr4结构里面的前sizeof()个内存地址里面的内容设置成int 0

addr4.sin_len = sizeof(addr4);

addr4.sin_family = AF_INET;

addr4.sin_port = htons(12345);

addr4.sin_addr.s_addr = inet_addr([connController.textField.text UTF8String]);

// CFDataCreate()是用来将一个申请一个sizeof()大小的内存空间,并将一个buffer中的数据拷贝到新申请的内存空间中

CFDataRef address = CFDataCreate(kCFAllocatorDefault,
(UInt8 *)&addr4, sizeof(addr4));

CFSocketConnectToAddress(_socket,
address, -1);

CFRunLoopRef cfrl
= CFRunLoopGetCurrent();

CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);

CFRunLoopAddSource(cfrl,
source, kCFRunLoopCommonModes);

CFRelease(source);

转载请标明出处,黄志勇的个人博客!

}

- (void) sendMessage {

NSString *stringToSend = [chatController.textField.text stringByAppendingString:@"\n"];

const char *data
= [stringToSend UTF8String];

send(CFSocketGetNative(_socket),
data, strlen(data) + 1, 0);

NSRange endRange;

endRange.location =
[chatController.textView.text length];

endRange.length = [stringToSend length];

chatController.textView.text =
[chatController.textView.text stringByAppendingString:[@"me:
" stringByAppendingString:stringToSend]];

[chatController.textView scrollRangeToVisible:endRange];

chatController.textField.text = @"";

}


recv(),
recvfrom()

Receive data on a socket


Prototypes

#include <sys/types.h> #include <sys/socket.h>  ssize_t recv(int s, void *buf, size_t len, int flags); ssize_t recvfrom(int s, void *buf, size_t len, int flags,                  struct sockaddr *from, socklen_t *fromlen);


Description

Once you have a socket up and connected, you can read incoming data from the remote side using the recv()(for
TCP SOCK_STREAM sockets) and recvfrom() (for
UDP SOCK_DGRAM sockets).

Both functions take the socket descriptor s,
a pointer to the buffer buf,
the size (in bytes) of the buffer len,
and a set of flags that
control how the functions work.

Additionally, the recvfrom() takes
a struct sockaddr*, from that
will tell you where the data came from, and will fill in fromlen with
the size of struct sockaddr. (You must also initialize fromlen to
be the size offrom or struct
sockaddr.)

So what wondrous flags can you pass into this function? Here are some of them, but you should check your local man pages for more information and what is actually supported on your system. You bitwise-or these together, or just set flags to 0 if
you want it to be a regular vanilla recv().

MSG_OOB
Receive Out of Band data. This is how to get data that has been sent to you with the MSG_OOB flag in send().
As the receiving side, you will have had signal SIGURG raised telling you there is urgent data. In your handler for
that signal, you could call recv() with
this MSG_OOB flag.
MSG_PEEK
If you want to call recv() "just
for pretend", you can call it with this flag. This will tell you what's waiting in the buffer for when you call recv() "for
real" (i.e. without the MSG_PEEK flag.
It's like a sneak preview into the nextrecv() call.
MSG_WAITALL
Tell recv() to
not return until all the data you specified in the lenparameter.
It will ignore your wishes in extreme circumstances, however, like if a signal interrupts the call or if some error occurs or if the remote side closes the connection, etc. Don't be mad with it.
When you call recv(),
it will block until there is some data to read. If you want to not block, set the socket to non-blocking or check with select() or poll() to
see if there is incoming data before calling recv() orrecvfrom().


Return Value

Returns the number of bytes actually received (which might be less than you requested in the len parameter),
or -1 on error (and errno will
be set accordingly.)

If the remote side has closed the connection, recv() will
return 0. This is the normal method for determining if the remote side has closed the connection. Normality is good,
rebel!

recv和recvfrom都是用来接受来自的网络的数据。来看看它们的原型:

 

int recv(

SOCKET s,

char FAR *buf,

int len,

int flags

);

int recvfrom(

SOCKET s,

char FAR* buf,

int len,

int flags,

struct sockaddr FAR *from,

int FAR *fromlen

);

 

这是在windows下面的定义。在linux下面的定义只是将SOCKET改成int,那么在linux下面的原型是这样:

 

int recv(

int s,

char FAR *buf,

int len,

int flags

);

int recvfrom(

int s,

char FAR* buf,

int len,

int flags,

struct sockaddr FAR *from,

int FAR *fromlen

);

 

 

其实要是看看windows中SOCKET的定义,就知道它们几乎是完全相同了,为什么是几乎?因为还是有点小区别,linux下面是int类型,而windows下面是unsigned
int。下面就是windows中SOCKET的定义:

typedef UINT_PTR SOCKET;

typedef unsigned int UINT_PTR, *PUINT_PTR;

插一句,在linux下面这里的int s, 其实代表的是文件描述符。在linux中所有的设备,如磁盘,光驱,U盘甚至我们这里的讨论的网络也都被看作是文件。

我们看看这两个函数在功用上的共同点和不同点:

共同点:

1. 都是用来接受来自网络的数据

2. 都可以 接受面向连接的流式套接字的 和 接受无连接的数据报套接字 的数据

3. 在成功接受到数据后,返回值都是实际接受的字节数; 套接字关闭时,返回都为0; 接受出错时,windows下面都返回SOCKET_ERROR , linux下面都返回-1, 其实你要是感兴趣可以查看SOCKET_ERROR
定义,它的值也是-1;

关于这里的“套接字关闭”需要注意,2个函数在用在流式套接字和数据报套接字时,套接字表示的含义不一样,前者表示客户端套接字,而后者表示的是自己的套接字。

4. 如果套接字为阻塞的,在系统缓冲中没有数据的情况下,都将阻塞;如果套接字为非阻塞的,在系统缓冲中没有数据的情况下,都将立即返回,返回值在linux下为-1,
errno被设置为EWOULDBLOCK,在windows下面返回SOCKET_ERROR, 通过WSAGetLastError返回WSAEWOULDBLOCK.

5. 如果用在流式套接字,则2者的操作是:将已在内核缓冲区的数据拷贝到应用程序自己的缓冲区,拷贝的最大的长度为调用函数时传入的缓冲区的长度,注意这里的长度不一定等于实际缓冲区的长度,可以小于缓冲区的长度,但是绝对不能大于,为什么不能大于,也许你比我更清楚。例如下面这段代码:

char szRecvBuf[1024] = { 0 };

recv( sockServer, szRecvBuf, 256, 0 );

这里虽然定义的缓冲区的长度为1024但是接受的时候只用其中的256. 如果内核缓冲区当时有10个字节,那么这次调用立刻返回,szRecvBuf被填充了10字节,返回值是10。 如果内核缓冲区有1500个字节,那么szRecvBuf将被填充256个字节,返回值就是256.

如果是数据报套接字,在内核缓冲区中的数据小于要求长度(这里是256)的情况下,和流式套接字结果一样。但是大于的情况就不一样了,首先将填充256到szRecvBuf中,并且产生一个WASEMSGSIZE的错误,并且剩下的部分被丢弃。假如内核缓冲区的数据为1000字节,那么前面的256被填充到szRecvBuf中,后面的1000-256将被丢弃。

recvfrom的执行效果也是同样的。

上面的结论是结合msdn和实际的测试得出的。

recvfrom的实际效果也是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: