首页 百科知识 套接字编程的几个重要步骤

套接字编程的几个重要步骤

时间:2022-10-09 百科知识 版权反馈
【摘要】:socket()系统调用为套接字在sockfs文件系统中分配一个新的文件和dentry对象,并通过文件描述符把它们与调用进程联系起来。绑定操作由系统调用bind()完成:int bind对于采用面向连接的传输协议TCP实现通信来说,一个比较重要的步骤就是通信双方建立连接,由系统调用connect()完成:int connect第一个参数为本地调用socket后返回的描述符,第二个参数为服务器的地址结构指针。前面所有的准备工作都是为这一步骤准备的。

3.4.2 套接字编程的几个重要步骤

(1)创建套接字,由系统调用socket实现

int socket( int domain, int type, int ptotocol);

参数domain指明通信域,如PF_UNIX(unix域),PF_INET(IPv4),PF_INET6(IPv6)等;type指明通信类型,如SOCK_STREAM(面向连接方式)、SOCK_DGRAM(非面向连接方式)等。一般来说,参数protocol可设置为0。

socket()系统调用为套接字在sockfs文件系统中分配一个新的文件和dentry对象,并通过文件描述符把它们与调用进程联系起来。进程可以像访问一个已经打开的文件一样访问套接字在sockfs中的对应文件。但进程绝不能调用open()来访问该文件(sockfs文件系统没有可视安装点,其中的文件永远不会出现在系统目录树上),当套接字被关闭时,内核会自动删除sockfs中的inodes。

(2)绑定地址

根据传输层协议(TCP、UDP)的不同,客户机及服务器的处理方式也有很大不同。但是,不管通信双方使用何种传输协议,都需要一种标识自己的机制。通信双方一般由两个方面标识:地址和端口号(通常,一个IP地址和一个端口号常常被称为一个套接字)。根据地址可以寻址到主机,根据端口号则可以寻址到主机提供特定服务的进程,实际上,一个特定的端口号代表了一个提供特定服务的进程。

对于使用TCP传输协议通信方式来说,通信双方需要给自己绑定一个惟一标识自己的套接字,以便建立连接;对于使用UDP传输协议,只需要服务器绑定一个标识自己的套接字就可以了,用户则不需要绑定。绑定操作由系统调用bind()完成:

int bind( int sockfd, const struct sockaddr * my_addr, socklen_t my_addr_len)

第二个参数对于Ipv4来说,实际上需要填充的结构是struct sockaddr_in。

(3)请求建立连接(由TCP客户发起)

对于采用面向连接的传输协议TCP实现通信来说,一个比较重要的步骤就是通信双方建立连接(如果采用udp传输协议则不需要),由系统调用connect()完成:int connect( int sockfd, const struct sockaddr * servaddr, socklen_t addrlen)第一个参数为本地调用socket后返回的描述符,第二个参数为服务器的地址结构指针。connect()向指定的套接字请求建立连接。

与connect()相对应,在服务器端,通过系统调用listen(),指定服务器端的套接字为监听套接字,监听每一个向服务器套接字发出的连接请求,并通过握手机制建立连接。内核为listen()维护两个队列:已完成连接队列和未完成连接队列。

(4)接受连接请求(由TCP服务器端发起)

服务器端通过监听套接字,为所有连接请求建立了两个队列:已完成连接队列和未完成连接队列(每个监听套接字都对应这样两个队列,当然,一般服务器只有一个监听套接字)。通过accept()调用,服务器将在监听套接字的已连接队列头中,返回用于代表当前连接的套接字描述字。

int accept( int sockfd, struct sockaddr * cliaddr, socklen_t * addrlen)

第一个参数指明哪个监听套接字,一般是由listen()系统调用指定的(由于每个监听套接字都对应已连接和未连接两个队列,因此它的内部机制实质是通过sockfd指定在哪个已连接队列头中返回一个用于当前客户的连接,如果相应的已连接队列为空,accept进入睡眠)。第二个参数指明客户的地址结构,如果对客户的身份不感兴趣,可指定其为空。

(5)通信

客户机可以通过套接字接收服务器传过来的数据,也可以通过套接字向服务器发送数据。前面所有的准备工作(创建套接字、绑定等操作)都是为这一步骤准备的。

常用的从套接字中接收数据的调用有:recv、recvfrom、recvmsg等,常用的向套接字中发送数据的调用有send、sendto、sendmsg等。

int recv(int s, void *buf, size_t len, int flags)

int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr * from,

      socklen_t * fromlen)

int recvmsg(int s, struct msghdr *msg, int flags)

int send(int s,const void *msg, size_t len, int flags)

int sendto(int s, const void *msg, size_t len, int flags const struct sockaddr* to,

      socklen_t tolen)

int sendmsg(int s, const struct msghdr *msg, int flags)

recvfrom()以及recvmsg()可用于面向连接的套接字,也可用于面向非连接的套接字;而recv()一般用于面向连接的套接字。另外,在调用了connect()之后,就应该调用send()而不是sendto()了,因为调用了connect之后,目标就已经确定了。前面讲到,socket()系统调用返回套接字描述字,实际上它是一个文件描述符。所以,可以对套接字进行通常的读写操作,即使用read()及write()方法。在实际应用中,由于面向连接的通信(采用TCP传输协议)是可靠的,同时又保证字节流原有的顺序,所以更适合用read及write方法。而非面向连接的通信(采用UDP传输协议)是不可靠的,字节流也不一定保持原有的顺序,所以一般不宜用read及write方法。

(6)通信的最后一步是关闭套接字

由close()来完成此项功能,它惟一的参数是套接字描述字。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈