Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。

        本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。

connect

connect()           遵循 POSIX.1 - 2008

1.库

标准 c 库,libc, -lc

2.头文件

3.接口定义

int connect(int sockfd, const struct sockaddr *addr,

socklen_t addrlen);

4.接口描述

        connect() 系统调用在 sockfd 指定的 socket 上连接 addr 指定的地址,addrlen 参数指定了 addr 的大小,addr 地址格式取决于 socket 的地址空间,可以参考 socket(2)。

        如果 socket 是 SOCK_DGRAM 类型,那么 addr 是发送报文的默认地址,也是唯一接收报文的地址。如果 socket 类型是 SOCK_STREAM 或者 SOCK_SEQPACKET,那么这个调用就是尝试和绑定了 addr 地址的 socket 建立连接。

        一些协议套接字(比如 UNIX 流套接字)只能成功连接一次。

        一些协议套接字(比如 UNIX TCP 套接字和网络数据报套接字)可以多次 connect() 来修改连接。

        一些协议套接字(比如 UNIX TCP 套接字和网络数据报套接字)可以通过将 sockaddr 的 sa_family 设置为 AF_UNSPEC 来消除连接,之后 socket 就可以连接到其他地址了。(AF_UNSPEC 在 Linux 2.2 之后支持)。

5.返回值

        如果连接或者绑定成功,那么返回 0。

        发生错误时,返回 -1,并设置errno 来指示错误类型。

        错误值定义如下(这里指示普通 socket 的错误,还可能存在 domain-specific 错误码):

EACCESUNIX 域套接字通过路径名唯一标识,并且是套接字文件是没有写权限的,路径中任何一级的搜索权限也是没有的,可以参考 path_resolution(7)EACCES/EPERM用户尝试连接到一个广播地址,却没有设置套接字的广播标记,或者请求被防火墙规则拦截了EACCES如果开启了 SELinux 策略,也可能会导致连接被拒绝(比如策略规定 HTTP 代理只能连接到 HTTP 服务器关联的端口,而 HTTP 代理却连接了其他端口)EADDRINUSE本地地址已经在用了EADDRNOTAVAIL(网络域套接字)sockfd 指定的套接字没有绑定到地址,并且在尝试将其绑定到临时端口时,临时端口用尽了EAFNOSUPPORT地址家族不正确EAGAIN对于非阻塞的 UNIX 域套接字,套接字是非阻塞的,连接无法立即完成。对于其他套接字家族,这个错误标识路由缓存没有足够的条目了EALREADY套接字是非阻塞的,并且之前的连接尝试还没有完成EBADFsockfd 不是一个打开的文件描述符ECONNREFUSEDconnect() 操作的流套接字发现没有人在监听对应的远程地址EFAULT套接字结构地址超出用户地址空间EINPROGRESS套接字是非阻塞的,连接不能立即完成。(UNIX 域套接字会返回 EAGAIN)。可以通过 select(2) 或者 poll(2) 查看套接字的可写事件,来确定连接完成。select(2) 指示可写后,使用 getsockopt(2) 来读取 SOL_SOCKET 级的 SO_ERROR 选项,来确定连接完全成功(SO_ERROR 为 0)或者未成功(SO_ERROR 为这里列出来的普通错误)。EINTR系统调用被信号打断EISCONN套接字已经连接ENETUNREACH网络不可达ENOTSOCK文件描述符并没有指向一个套接字EPROTOTYPE该套接字不支持指定的通信协议。这个错误可能在出现在连接一个 UNIX 域报文套接字到一个流套接字ETIMEDOUT连接超时。可能是服务器太忙了以至于无法接收新的连接。注意:当服务器开启 syncookies 时,IP 套接字的超时可能会非常长。

6.注意

       如果 connect() 失败,那么套接字的状态是未知的。一个易于移植的程序应该关闭该套接字应该再创建一个新套接字,重新连接。

7.代码

        这里我们展示下 select() 的用法示例,来将最近几篇内容串起来:

#include

#include

#include

#include

#include

#include

#include

#define SERVER_PORT 12345

#define TRUE 1

#define FALSE 0

main (int argc, char *argv[])

{

int i, len, rc, on = 1;

int listen_sd, max_sd, new_sd;

int desc_ready, end_server = FALSE;

int close_conn;

char buffer[80];

struct sockaddr_in6 addr;

struct timeval timeout;

struct fd_set master_set, working_set;

/*************************************************************/

/* Create an AF_INET6 stream socket to receive incoming */

/* connections on */

/*************************************************************/

listen_sd = socket(AF_INET6, SOCK_STREAM, 0);

if (listen_sd < 0)

{

perror("socket() failed");

exit(-1);

}

/*************************************************************/

/* Allow socket descriptor to be reuseable */

/*************************************************************/

rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,

(char *)&on, sizeof(on));

if (rc < 0)

{

perror("setsockopt() failed");

close(listen_sd);

exit(-1);

}

/*************************************************************/

/* Set socket to be nonblocking. All of the sockets for */

/* the incoming connections will also be nonblocking since */

/* they will inherit that state from the listening socket. */

/*************************************************************/

rc = ioctl(listen_sd, FIONBIO, (char *)&on);

if (rc < 0)

{

perror("ioctl() failed");

close(listen_sd);

exit(-1);

}

/*************************************************************/

/* Bind the socket */

/*************************************************************/

memset(&addr, 0, sizeof(addr));

addr.sin6_family = AF_INET6;

memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));

addr.sin6_port = htons(SERVER_PORT);

rc = bind(listen_sd,

(struct sockaddr *)&addr, sizeof(addr));

if (rc < 0)

{

perror("bind() failed");

close(listen_sd);

exit(-1);

}

/*************************************************************/

/* Set the listen back log */

/*************************************************************/

rc = listen(listen_sd, 32);

if (rc < 0)

{

perror("listen() failed");

close(listen_sd);

exit(-1);

}

/*************************************************************/

/* Initialize the master fd_set */

/*************************************************************/

FD_ZERO(&master_set);

max_sd = listen_sd;

FD_SET(listen_sd, &master_set);

/*************************************************************/

/* Initialize the timeval struct to 3 minutes. If no */

/* activity after 3 minutes this program will end. */

/*************************************************************/

timeout.tv_sec = 3 * 60;

timeout.tv_usec = 0;

/*************************************************************/

/* Loop waiting for incoming connects or for incoming data */

/* on any of the connected sockets. */

/*************************************************************/

do

{

/**********************************************************/

/* Copy the master fd_set over to the working fd_set. */

/**********************************************************/

memcpy(&working_set, &master_set, sizeof(master_set));

/**********************************************************/

/* Call select() and wait 3 minutes for it to complete. */

/**********************************************************/

printf("Waiting on select()...\n");

rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);

/**********************************************************/

/* Check to see if the select call failed. */

/**********************************************************/

if (rc < 0)

{

perror(" select() failed");

break;

}

/**********************************************************/

/* Check to see if the 3 minute time out expired. */

/**********************************************************/

if (rc == 0)

{

printf(" select() timed out. End program.\n");

break;

}

/**********************************************************/

/* One or more descriptors are readable. Need to */

/* determine which ones they are. */

/**********************************************************/

desc_ready = rc;

for (i=0; i <= max_sd && desc_ready > 0; ++i)

{

/*******************************************************/

/* Check to see if this descriptor is ready */

/*******************************************************/

if (FD_ISSET(i, &working_set))

{

/****************************************************/

/* A descriptor was found that was readable - one */

/* less has to be looked for. This is being done */

/* so that we can stop looking at the working set */

/* once we have found all of the descriptors that */

/* were ready. */

/****************************************************/

desc_ready -= 1;

/****************************************************/

/* Check to see if this is the listening socket */

/****************************************************/

if (i == listen_sd)

{

printf(" Listening socket is readable\n");

/*************************************************/

/* Accept all incoming connections that are */

/* queued up on the listening socket before we */

/* loop back and call select again. */

/*************************************************/

do

{

/**********************************************/

/* Accept each incoming connection. If */

/* accept fails with EWOULDBLOCK, then we */

/* have accepted all of them. Any other */

/* failure on accept will cause us to end the */

/* server. */

/**********************************************/

new_sd = accept(listen_sd, NULL, NULL);

if (new_sd < 0)

{

if (errno != EWOULDBLOCK)

{

perror(" accept() failed");

end_server = TRUE;

}

break;

}

/**********************************************/

/* Add the new incoming connection to the */

/* master read set */

/**********************************************/

printf(" New incoming connection - %d\n", new_sd);

FD_SET(new_sd, &master_set);

if (new_sd > max_sd)

max_sd = new_sd;

/**********************************************/

/* Loop back up and accept another incoming */

/* connection */

/**********************************************/

} while (new_sd != -1);

}

/****************************************************/

/* This is not the listening socket, therefore an */

/* existing connection must be readable */

/****************************************************/

else

{

printf(" Descriptor %d is readable\n", i);

close_conn = FALSE;

/*************************************************/

/* Receive all incoming data on this socket */

/* before we loop back and call select again. */

/*************************************************/

do

{

/**********************************************/

/* Receive data on this connection until the */

/* recv fails with EWOULDBLOCK. If any other */

/* failure occurs, we will close the */

/* connection. */

/**********************************************/

rc = recv(i, buffer, sizeof(buffer), 0);

if (rc < 0)

{

if (errno != EWOULDBLOCK)

{

perror(" recv() failed");

close_conn = TRUE;

}

break;

}

/**********************************************/

/* Check to see if the connection has been */

/* closed by the client */

/**********************************************/

if (rc == 0)

{

printf(" Connection closed\n");

close_conn = TRUE;

break;

}

/**********************************************/

/* Data was received */

/**********************************************/

len = rc;

printf(" %d bytes received\n", len);

/**********************************************/

/* Echo the data back to the client */

/**********************************************/

rc = send(i, buffer, len, 0);

if (rc < 0)

{

perror(" send() failed");

close_conn = TRUE;

break;

}

} while (TRUE);

/*************************************************/

/* If the close_conn flag was turned on, we need */

/* to clean up this active connection. This */

/* clean up process includes removing the */

/* descriptor from the master set and */

/* determining the new maximum descriptor value */

/* based on the bits that are still turned on in */

/* the master set. */

/*************************************************/

if (close_conn)

{

close(i);

FD_CLR(i, &master_set);

if (i == max_sd)

{

while (FD_ISSET(max_sd, &master_set) == FALSE)

max_sd -= 1;

}

}

} /* End of existing connection is readable */

} /* End of if (FD_ISSET(i, &working_set)) */

} /* End of loop through selectable descriptors */

} while (end_server == FALSE);

/*************************************************************/

/* Clean up all of the sockets that are open */

/*************************************************************/

for (i=0; i <= max_sd; ++i)

{

if (FD_ISSET(i, &master_set))

close(i);

}

}

下一篇 【计算机网络】网络编程接口 Socket API 解读(6)​​​​​​​

参考文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: