socket函数 整理

来源:互联网 发布:恺英网络待遇 编辑:程序博客网 时间:2024/06/07 09:40

#include<sys/socket.h>
int connect(int sockfd, const struct sockaddr* server_addr, socklen_t addrlen)
   为了理解connect函数,我们需要对connect函数的功能进行介绍。connect函数的功能可以用一句话来概括,就是完成面向连接的协议的连接过程,它是主要连接的。面向连接的协议,在建立连接的时候总会有一方先发送数据,那么谁调用了connect谁就是先发送数据的一方。如此理解connect三个参数是容易了,我必需指定数据发送的地址,同时也必需指定数据从哪里发送,这正好是connect的前两个参数,而第三个参数是为第二个参数服务的。
参数sockfd
指定数据发送的套接字,解决从哪里发送的问题。内核需要维护大量IO通道,所以用户必需通过这个参数告诉内核从哪个IO通道,此处就是从哪个socket接口中发送数据。sockfd是先前socket返回的值。
参数server_addr
指定数据发送的目的地,也就是服务器端的地址。这里服务器是针对connect说的,因为connect是主动连接的一方调用的,所以相应的要存在一个被连接的一方,被动连接的一方需要调用listen以接受connect的连接请求,如此被动连接的一方就是服务器了。
参数addrlen
       指定server_addr结构体的长度。我们知道系统中存在大量的地址结构,但socket接口只是通过一个统一的结构来指定参数类型,所以需要指定一个长度,以使内核在进行参数复制的时候有个有个界限。


在套接口中,一个套接字只是用户程序与内核交互信息的枢纽,它自身没有太多的信息,也没有网络协议地址和端口号等信息,在进行网络通信的时候,必须把一个套接字与一个地址相关联,这个过程就是地址绑定的过程。许多时候内核会我们自动绑定一个地址,然而有时用户可能需要自己来完成这个绑定的过程,以满足实际应用的需要,最典型的情况是一个服务器进程需要绑定一个众所周知的地址或端口以等待客户来连接。这个事由bind的函数完成。
#include<sys/socket.h>
int bind(int sockfd, struct sockaddr* addr, socklen_t addrlen)


参数sockfd
指定地址与哪个套接字绑定,这是一个由之前的socket函数调用返回的套接字。调用bind的函数之后,该套接字与一个相应的地址关联,发送到这个地址的数据可以通过这个套接字来读取与使用。
参数addr
指定地址。这是一个地址结构,并且是一个已经经过填写的有效的地址结构。调用bind之后这个地址与参数sockfd指定的套接字关联,从而实现上面所说的效果。
参数addrlen
正如大多数socket接口一样,内核不关心地址结构,当它复制或传递地址给驱动的时候,它依据这个值来确定需要复制多少数据。这已经成为socket接口中最常见的参数之一了。
bind函数并不是总是需要调用的,只有用户进程想与一个具体的地址或端口相关联的时候才需要调用这个函数。如果用户进程没有这个需要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择,而不需要调用bind的函数,同时也避免不必要的复杂度。在一般情况下,对于服务器进程问题需要调用bind函数,对于客户进程则不需要调用bind函数。


:listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
listen函数在一般在调用bind之后-调用accept之前调用,它的函数原型是:


#include<sys/socket.h>
int listen(int sockfd, int backlog)
返回:0──成功, -1──失败

参数sockfd
被listen函数作用的套接字,sockfd之前由socket函数返回。在被socket函数返回的套接字fd之时,它是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事。
参数backlog
这个参数涉及到一些网络的细节。在进程正理一个一个连接请求的时候,可能还存在其它的连接请求。因为TCP连接是一个过程,所以可能存在一种半连接的状态,有时由于同时尝试连接的用户过多,使得服务器进程无法快速地完成连接请求。如果这个情况出现了,服务器进程希望内核如何处理呢?内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限。这个backlog告诉内核使用这个数值作为上限。
毫无疑问,服务器进程不能随便指定一个数值,内核有一个许可的范围。这个范围是实现相关的。很难有某种统一,一般这个值会小30以内。
当调用listen之后,服务器进程就可以调用accept来接受一个外来的请求。


对于服务器编程中最重要的一步等待并接受客户的连接,那么这一步在编程中如何完成,accept函数就是完成这一步的。它从内核中取出已经建立的客户连接,然后把这个已经建立的连接返回给用户程序,此时用户程序就可以与自己的客户进行点到点的通信了。
accept函数等待并接受客户请求:
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
返回:非负描述字——成功, -1——失败
accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。此时我们需要区分两种套接字,一种套接字正如accept的参数sockfd,它是监听套接字,在调用listen函数之后,一个套接字会从主动连接的套接字变身为一个监听套接字;而accept返回是一个连接套接字,它代表着一个网络已经存在的点点连接。自然要问的是:为什么要有两种套接字?原因很简单,如果使用一个描述字的话,那么它的功能太多,使得使用很不直观,同时在内核确实产生了一个这样的新的描述字。
参数sockfd
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr
这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
参数len
如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。
如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。


对于网络TCP面向连接的程序,它需要在某个时候终止已经存在的连接。用户可以主动终止一个连接,这很重要,尤其对于服务器进程而言,因为一个进程可以同时打开的连接是有限的,如果不在某个时候主动终止已有的连接,那么对于服务器进程来说,它总会在某个时候因为无法打开新连接而失败。

对于UNIX系统而言,无论是一般的文件描述符,还是网络中使用的套接字都是描述字的范围,所以它们都可以用close函数来完成关闭的任务,然后对于网络套接字这一个特殊的描述字,我们却可以使用更加丰富的shutdown函数完成有选择的关闭。下面我们先来看看这个两个函数:

#include<unistd.h>
int close(int fd)
返回:0——成功, -1——失败

#include<sys/socket.h>
int shutdown(int sockfd, int howto)
返回:0——成功, -1——失败

让我们来回忆一下,一个文件描述符关联着一个实际的文件——不管这个文件是什么,普通文件或网络套接口等等,但是多个打字可以同时与一个文件关联,并且内核维护一个文件引用计数。正常情况下,close函数不武断地释放一个描述字关联的文件,除了这个引用计数为0的时候,并且无论如何,当对一个描述字调用了close函数,用户无法再次使用这个描述字。这是close相对shutdown的两点差别,相应地shutdown是针对socket套接口定制的函数,所以它会做的更好。

shutdown函数不是参考引用计数,它会直接关闭相应的socket套接口,无论引用计数是多少。我们还知道,socket套接口是全双工的,也就是用户可以读,也可以写。存在一个这样的情况,此时用户已经把所有要写的数据都写完了,他想告诉对等端这一点;或者用户把所有要读的数据都读完成了,同样要告诉对等端。此时就是关闭读这一半或写这一半,使用shutdown可以完成这一个。系统定义了3个宏,这3个宏分别用作shutdown的后一个参数:

  • SHUT_RD:关闭读这一半,此时用户不能再从这个套接字读数据,这个套接口接收到的数据都会被丢弃,对等方不知道这个过程。
  • SHUT_WR:相应地关闭写这一半,此时用户不能再向套接字中写数据,内核会把缓存中的数据发送出去,接着不会再发送数据,对等端将会知道这一点。当对等端试图去读的时候,可能会发生错误。
  • SHUT_RDWR:关闭读与写两半,此时用户不能从套接字中读或写。它相当于再次调用shutdown函数,并且一次指定SHUT_RD,一次指定SHUT_WR。

刚才我写完了服务器编程之fork并行模式,这个文章中我们使用close函数,试问一下:我们可以使用shutdown函数代替吗?简单的思考之后,我们知道不可以使用shutdown函数代替,因为我们在子进程中只是想解除sockfd与那个监听套接口的关联,并不想释放这个套接口,原因是在父进程还要使用它;相应在父进程我也只是想解除cfd与其套接口的关联,我们在子进程还需要使用cfd。从这个例子中可以看到,close与shutdown有各自的用处,并不能相互代替,就算在socket套接字这一特定情况下也如此。


0 0
原创粉丝点击