?????????Socket 是網(wǎng)絡協(xié)議棧暴露給編程人員的 API,相比復雜的計算機網(wǎng)絡協(xié)議,API 對關鍵操作和配置數(shù)據(jù)進行了抽象,簡化了程序編程。
? ? ? ? 本文講述的 socket 內(nèi)容源自 Linux man。本文主要對各 API 進行詳細介紹,從而更好的理解 socket 編程。
connect
connect()????????? ?遵循 POSIX.1 - 2008
1.庫
標準 c 庫,libc, -lc
2.頭文件
<sys/socket.h>
3.接口定義
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
4.接口描述
? ? ? ? connect() 系統(tǒng)調(diào)用在 sockfd 指定的 socket 上連接 addr 指定的地址,addrlen 參數(shù)指定了 addr 的大小,addr 地址格式取決于 socket 的地址空間,可以參考 socket(2)。
? ? ? ? 如果 socket 是 SOCK_DGRAM?類型,那么 addr 是發(fā)送報文的默認地址,也是唯一接收報文的地址。如果 socket 類型是 SOCK_STREAM 或者 SOCK_SEQPACKET,那么這個調(diào)用就是嘗試和綁定了 addr 地址的 socket 建立連接。
? ? ? ? 一些協(xié)議套接字(比如 UNIX 流套接字)只能成功連接一次。
? ? ? ? 一些協(xié)議套接字(比如 UNIX TCP 套接字和網(wǎng)絡數(shù)據(jù)報套接字)可以多次 connect() 來修改連接。
? ? ? ? 一些協(xié)議套接字(比如 UNIX TCP 套接字和網(wǎng)絡數(shù)據(jù)報套接字)可以通過將 sockaddr 的 sa_family 設置為 AF_UNSPEC 來消除連接,之后 socket 就可以連接到其他地址了。(AF_UNSPEC 在 Linux 2.2 之后支持)。
5.返回值
? ? ? ? 如果連接或者綁定成功,那么返回 0。
? ? ? ? 發(fā)生錯誤時,返回 -1,并設置errno 來指示錯誤類型。
? ? ? ? 錯誤值定義如下(這里指示普通 socket 的錯誤,還可能存在 domain-specific 錯誤碼):
EACCES | UNIX 域套接字通過路徑名唯一標識,并且是套接字文件是沒有寫權限的,路徑中任何一級的搜索權限也是沒有的,可以參考 path_resolution(7) |
EACCES/EPERM | 用戶嘗試連接到一個廣播地址,卻沒有設置套接字的廣播標記,或者請求被防火墻規(guī)則攔截了 |
EACCES | 如果開啟了 SELinux 策略,也可能會導致連接被拒絕(比如策略規(guī)定 HTTP 代理只能連接到 HTTP 服務器關聯(lián)的端口,而 HTTP 代理卻連接了其他端口) |
EADDRINUSE | 本地地址已經(jīng)在用了 |
EADDRNOTAVAIL | (網(wǎng)絡域套接字)sockfd 指定的套接字沒有綁定到地址,并且在嘗試將其綁定到臨時端口時,臨時端口用盡了 |
EAFNOSUPPORT | 地址家族不正確 |
EAGAIN | 對于非阻塞的 UNIX 域套接字,套接字是非阻塞的,連接無法立即完成。對于其他套接字家族,這個錯誤標識路由緩存沒有足夠的條目了 |
EALREADY | 套接字是非阻塞的,并且之前的連接嘗試還沒有完成 |
EBADF | sockfd 不是一個打開的文件描述符 |
ECONNREFUSED | connect() 操作的流套接字發(fā)現(xiàn)沒有人在監(jiān)聽對應的遠程地址 |
EFAULT | 套接字結構地址超出用戶地址空間 |
EINPROGRESS | 套接字是非阻塞的,連接不能立即完成。(UNIX 域套接字會返回 EAGAIN)??梢酝ㄟ^ select(2) 或者 poll(2) 查看套接字的可寫事件,來確定連接完成。select(2) 指示可寫后,使用 getsockopt(2) 來讀取 SOL_SOCKET 級的 SO_ERROR 選項,來確定連接完全成功(SO_ERROR 為 0)或者未成功(SO_ERROR 為這里列出來的普通錯誤)。 |
EINTR | 系統(tǒng)調(diào)用被信號打斷 |
EISCONN | 套接字已經(jīng)連接 |
ENETUNREACH | 網(wǎng)絡不可達 |
ENOTSOCK | 文件描述符并沒有指向一個套接字 |
EPROTOTYPE | 該套接字不支持指定的通信協(xié)議。這個錯誤可能在出現(xiàn)在連接一個 UNIX 域報文套接字到一個流套接字 |
ETIMEDOUT | 連接超時??赡苁欠掌魈α艘灾劣跓o法接收新的連接。注意:當服務器開啟 syncookies 時,IP 套接字的超時可能會非常長。 |
6.注意
? ? ? ?如果 connect() 失敗,那么套接字的狀態(tài)是未知的。一個易于移植的程序應該關閉該套接字應該再創(chuàng)建一個新套接字,重新連接。
7.代碼
? ? ? ? 這里我們展示下 select() 的用法示例,來將最近幾篇內(nèi)容串起來:文章來源:http://www.zghlxwxcb.cn/news/detail-716033.html
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#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);
}
}
下一篇 【計算機網(wǎng)絡】網(wǎng)絡編程接口 Socket API 解讀(6)???????文章來源地址http://www.zghlxwxcb.cn/news/detail-716033.html
到了這里,關于【計算機網(wǎng)絡】網(wǎng)絡編程接口 Socket API 解讀(5)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!