进程间通讯(七).socket(3)

2021-09-15 20:07:05 浏览数 (1)

__fd 指定地址与哪个套接字绑定,这是一个由之前的socket函数调用返回的套接字。调用bind的函数之后,该套接字与一个相应的地址关联,发送到这个地址的数据可以通过这个套接字来读取与使用

__addr 指定地址。这是一个地址结构,并且是一个已经经过填写的有效的地址结构。调用bind之后这个地址与参数sockfd指定的套接字关联,从而实现上面所说的效果

__len 正如大多数socket接口一样,内核不关心地址结构,当它复制或传递地址给驱动的时候,它依据这个值来确定需要复制多少数据。这已经成为socket接口中最常见的参数之一了

成功,返回0;出错,返回-1,相应地设定全局变量errno

代码语言:javascript复制
EACCESS:地址空间受保护,用户不具有超级用户的权限
EADDRINUSE:指定的地址已经在使用
EBADF:sockfd参数为非法的文件描述符
EINVAL:socket已经和地址绑定
ENOTSOCK:参数sockfd为文件描述符

Tip: bind函数并不是总是需要调用的,只有用户进程想与一个具体的地址或端口相关联的时候才需要调用这个函数。如果用户进程没有这个需要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择,而不需要调用bind的函数,同时也避免不必要的复杂度。在一般情况下,对于服务器进程问题需要调用bind函数,对于客户进程则不需要调用bind函数


listen

sys/socket.h 中有关于 listen 的定义

代码语言:javascript复制
/* Prepare to accept connections on socket FD.
   N connection requests will be queued before further requests are refused.
   Returns 0 on success, -1 for errors.  */
extern int listen (int __fd, int __n) __THROW;

listen函数在一般在调用bind之后-调用accept之前调用。用户在调用socket函数之后,返回一个套接字sockfd. sockfd默认一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事

listen函数可使得流套接字sockfd处于监听状态,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接

处于监听状态的套接字sockfd将维护一个客户连接请求队列,该队列最多容纳backlog个用户请求

__fd 套接字

__n 队列最多同时容纳用户请求的个数

返回:0 成功, -1 失败


accept

sys/socket.h 中有关于 accept 的定义

代码语言:javascript复制
/* Await a connection on socket FD.
   When a connection arrives, open a new socket to communicate with it,
   set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
   peer and *ADDR_LEN to the address's actual length, and return the
   new socket's descriptor, or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int accept (int __fd, __SOCKADDR_ARG __addr,
                   socklen_t *__restrict __addr_len);

服务器编程中最重要的一步是等待并接受客户的连接,那么这一步在编程中如何完成,accept函数就是完成这一步的。它从内核中取出已经建立的客户连接,然后把这个已经建立的连接返回给用户程序,此时用户程序就可以与自己的客户进行点到点的通信了

__fd 指定处于监听状态的流套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号

__addr 返回新创建的套接字的地址结构,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL

__addr_len 新创建的套接字的地址结构的长度,用来接受上述addr的结构的大小,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL

如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信

返回:非负描述字成功, -1失败

有人从很远的地方通过一个在侦听 (listen()) 的端口连接 (connect()) 到机器。它的连接将加入到等待接受 (accept()) 的队列中


recv

sys/socket.h 中有关于 recv 的声明

代码语言:javascript复制
/* Read N bytes into BUF from socket FD.
   Returns the number read or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t recv (int __fd, void *__buf, size_t __n, int __flags);

从TCP连接的另一端接收数据

__fd 指定接收端套接字描述符

__buf 指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据

__n 指明buf的长度

__flags 参数一般置0

返回值: <0 出错 ;==0 对方调用了close API来关闭连接 ;>0 接收到的数据大小

阻塞模式下recv会一直阻塞直到接收到数据,非阻塞模式下如果没有数据就会返回,不会阻塞着读,因此需要循环读取)

可能错误

代码语言:javascript复制
EAGAIN:套接字已标记为非阻塞,而接收操作被阻塞或者接收超时
EBADF:sock不是有效的描述词
ECONNREFUSE:远程主机阻绝网络连接
EFAULT:内存空间访问出错
EINTR:操作被信号中断
EINVAL:参数无效
ENOMEM:内存不足
ENOTCONN:与面向连接关联的套接字尚未被连接上
ENOTSOCK:sock索引的不是套接字

send

sys/socket.h 中有关于 send 的声明

代码语言:javascript复制
/* Send N bytes of BUF to socket FD.  Returns the number sent or -1.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t send (int __fd, __const void *__buf, size_t __n, int __flags);

向TCP连接的另一端发送数据

__fd 指定发送端套接字描述符

__buf 指明一个存放应用程序要发送数据的缓冲区

__n 指明实际要发送的数据的字节数

__flags 参数一般置0

flags

说明

recv

send

MSG_DONTROUTE

绕过路由表查找

MSG_DONTWAIT

仅本操作非阻塞

MSG_OOB

发送或接收带外数据

MSG_PEEK

窥看外来消息

MSG_WAITALL

等待所有数据

返回值 :>0 表示发送的字节数(实际上是拷贝到发送缓冲中的字节数);==0 对方调用了close API来关闭连接 ;<0 发送失败,错误原因存于全局变量errno中

代码语言:javascript复制
EBADF 参数s 非合法的socket处理代码
EFAULT 参数中有一指针指向无法存取的内存空间
ENOTSOCK 参数s为一文件描述词,非socket
EINTR 被信号所中断
EAGAIN 此操作会令进程阻断,但参数s的socket为不可阻断
ENOBUFS 系统的缓冲内存不足
ENOMEM 核心内存不足
EINVAL 传给系统调用的参数不正确

inet_addr

arpa/inet.h 中有关于 inet_addr 的声明

代码语言:javascript复制
/* Convert Internet host address from numbers-and-dots notation in CP
   into binary data in network byte order.  */
extern in_addr_t inet_addr (__const char *__cp) __THROW;

将一个ip地址字符串转换成一个整数值,一般的IP地址串格式为:’a.b.c.d’ 分成四段

__cp 字符串指针


connect

sys/socket.h 中有关于 connect 的声明

代码语言:javascript复制
/* Open a connection on socket FD to peer at ADDR (which LEN bytes long).
   For connectionless socket types, just set the default address to send to
   and the only address from which to accept transmissions.
   Return 0 on success, -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);

用于建立与指定socket的连接

__fd 标识一个未连接的socket

__addr 指向要连接套接字的sockaddr结构体的指针

__len sockaddr结构体的字节长度

返回值 : 成功则返回0,失败则返回非0,错误码GetLastError()

代码语言:javascript复制
EBADF 参数sockfd 非合法socket处理代码
EFAULT 参数serv_addr指针指向无法存取的内存空间
ENOTSOCK 参数sockfd为一文件描述词,非socket
EISCONN 参数sockfd的socket已是连线状态
ECONNREFUSED 连线要求被server端拒绝
ETIMEDOUT 企图连线的操作超过限定时间仍未有响应
ENETUNREACH 无法传送数据包至指定的主机
EAFNOSUPPORT sockaddr结构的sa_family不正确
EALREADY socket为不可阻断且先前的连线操作还未完成

总结

以下函数可以进行socket的创建与控制,是网络编程的基础

  • socket
  • htons
  • setsockopt
  • bind
  • listen
  • accept
  • recv
  • send
  • inet_addr
  • connect

通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础

原文地址

0 人点赞