SOCKET WSAAPI socket(
int af,
int type,
int protocol
);
af
地址簇规范The values currently supported are AF_INET or AF_INET6, which are the Internet address family formats for IPv4 and IPv6.
), AF | Meaning |
---|---|
AF_UNSPEC 0 |
The address family is unspecified. |
AF_INET 2 |
The Internet Protocol version 4 (IPv4) address family. |
AF_INET6 23 | The Internet Protocol version 6 (IPv6) address family. |
type
新套接字的类型规范。windows socket 2支持的type
参数(部分)
Type | Meaning |
---|---|
SOCK_STREAM 1 |
一种套接字类型,提供带有OOB数据传输机制的顺序,可靠,双向,基于连接的字节流。 此套接字类型使用传输控制协议(TCP)作为Internet地址系列(AF_INET或AF_INET6)。 |
SOCK_DGRAM 2 |
一种支持数据报的套接字类型,它是固定(通常很小)最大长度的无连接,不可靠的缓冲区。 此套接字类型使用用户数据报协议(UDP)作为Internet地址系列(AF_INET或AF_INET6)。 |
SOCK_RAW 3 |
一种套接字类型,提供允许应用程序操作下一个上层协议头的原始套接字。 要操作IPv4标头,必须在套接字上设置IP_HDRINCL套接字选项。 要操作IPv6标头,必须在套接字上设置IPV6_HDRINCL套接字选项。 |
protocol
协议如果指定值0,则调用者不希望指定协议,服务提供者将选择要使用的协议。
common values for the protocol
如果没有错误发生,socket返回一个新套接字的描述符;如果发生错误,将返回INVALID_SOCKET
,通过调用 WSAGetLastError
,可以得到具体的错误代码。
demo实例
#pragma comment(lib,"Ws2_32.lib") //link Ws2_32.lib
#include
#include
#include
#include // Needed for _wtoi
int main(){
WSADATA wsaData = {0};
SOCKET sock = INVALID_SOCKET;
int iFamily = AF_UNSPEC;
int iType = 0;
int iProtocol = 0;
iFamily = _wtoi(argv[1]);
iType = _wtoi(argv[2]);
iProtocol = _wtoi(argv[3]);
int iReturn=0;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
wprintf(L"WSAStartup failed: %d\n", iResult);
return 1;
}
socket=socket(iFamily,iType,iProtocol);
if (sock == INVALID_SOCKET)
wprintf(L"socket function failed with error = %d\n", WSAGetLastError() );
else {
wprintf(L"socket function succeeded\n");
// Close the socket to release the resources associated
// Normally an application calls shutdown() before closesocket
// to disables sends or receives on a socket first
// This isn't needed in this simple sample
iResult = closesocket(sock);
if (iResult == SOCKET_ERROR) {
wprintf(L"closesocket failed with error = %d\n", WSAGetLastError() );
WSACleanup();
return 1;
}
}
WSACleanup();
}
sockaddr结构取决于所选的协议。 除sin * _family参数外,sockaddr内容以网络字节顺序表示。
下面的sockaddr结构和sockaddr_in结构与IPv4一起使用。 其他协议使用类似的结构。
struct sockaddr {
ushort sa_family;
char sa_data[14];
};
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
下面的sockaddr_in6和sockaddr_in6_old结构与IPv6一起使用。
struct sockaddr_in6 {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
u_long sin6_scope_id;
};
typedef struct sockaddr_in6 SOCKADDR_IN6;
typedef struct sockaddr_in6 *PSOCKADDR_IN6;
typedef struct sockaddr_in6 FAR *LPSOCKADDR_IN6;
struct sockaddr_in6_old {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
};
实例代码
// Declare variables
SOCKET ListenSocket;
struct sockaddr_in saServer;
hostent* localHost;
char* localIP;
// Create a listening socket
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Get the local host information
localHost = gethostbyname("");
localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list);
// Set up the sockaddr structure
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = inet_addr(localIP);
saServer.sin_port = htons(5150);
// Bind the listening socket using the
// information in the sockaddr structure
bind( ListenSocket,(SOCKADDR*) &saServer, sizeof(saServer) );
bind 函数将本地地址和一个套接字相关联
int bind(
SOCKET s,
const sockaddr *addr,
int namelen
);
s
一个未绑定的socket
addr
一个指向要分配给绑定套接字的sockaddr结构的指针。
namelen
name参数指向的值的长度(以字节为单位)。
如果没有错误发生,返回0,否则返回SOCKET_ERROR,同样调用WSAGetLastError可以得到具体错误代码。
int WSAAPI listen(
SOCKET s,
int backlog
);
s
一个绑定的,未连接(unconnected)套接字
blacklog
挂起连接队列的最大长度。 如果设置为SOMAXCONN,则负责套接字的底层服务提供程序会将backlog设置为最大合理值。 如果设置为SOMAXCONN_HINT(N)(其中N是数字),则积压值将为N,调整为在范围(200,65535)内。 请注意,SOMAXCONN_HINT可用于将积压设置为比SOMAXCONN更大的值。
SOMAXCONN_HINT仅受Microsoft TCP / IP服务提供商支持。 没有标准条款来获取实际的积压值。
如果没有错误发生,listen 返回 0,否则,将返回SOCKET_ERROR
, 同样调用 WSAGETLastError可以得到错误码。
#define WIN32_LEAN_AND_MEAN
#include
#include
#include
// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")
int wmain()
{
//----------------------
// Initialize Winsock
WSADATA wsaData;
int iResult = 0;
SOCKET ListenSocket = INVALID_SOCKET;
sockaddr_in service;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
wprintf(L"WSAStartup() failed with error: %d\n", iResult);
return 1;
}
//----------------------
// Create a SOCKET for listening for incoming connection requests.
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //获取一个新的socket实例
if (ListenSocket == INVALID_SOCKET) {
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port for the socket that is being bound.
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27015);
iResult = bind(ListenSocket, (SOCKADDR *) & service, sizeof (service));
if (iResult == SOCKET_ERROR) {
wprintf(L"bind function failed with error %d\n", WSAGetLastError());
iResult = closesocket(ListenSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// Listen for incoming connection requests
// on the created socket
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)
wprintf(L"listen function failed with error: %d\n", WSAGetLastError());
wprintf(L"Listening on socket...\n");
iResult = closesocket(ListenSocket);
if (iResult == SOCKET_ERROR) {
wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
SOCKET WSAAPI accept(
SOCKET s,
sockaddr *addr,
int *addrlen
);
s
socket
addr
addrlen
没有错误发生,accept返回一个SOCKET对象(新套接字对象),此返回值是进行实际连接的套接字的句柄。否则返回INVALID_SOCKET
, 可调用WSAGetLastError.
connect 建立和一个特定的socket的连接
int WSAAPI connect(
SOCKET s,
const sockaddr *name,
int namelen
);
s
name
namelen
如果没有错误发生返回0;否则返回SOCKET_ERROR,可调用WSAGetLastError
#ifndef UNICODE
#define UNICODE
#endif
#define WIN32_LEAN_AND_MEAN
#include
#include
#include
// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")
int wmain()
{
//----------------------
// Initialize Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
wprintf(L"WSAStartup function failed with error: %d\n", iResult);
return 1;
}
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
clientService.sin_port = htons(27015);
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
if (iResult == SOCKET_ERROR) {
wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
wprintf(L"Connected to server.\n");
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR) {
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
typedef struct timeval {
long tv_sec;
long tv_usec;
} TIMEVAL, *PTIMEVAL, *LPTIMEVAL;
tv_sec
tv_usec
使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
int WSAAPI select(
int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const timeval *timeout
);
nfds
Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.
readfds
指向要检查可读性的一组套接字的可选指针。
fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读;如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
writefds
指向要检查可写性的一组套接字的可选指针。
与readfds唯一不同的是关心写变化
exceptfds
指向要检查错误的一组套接字的可选指针。
同上面的意图,用来监视文件错我异常文件。
timeouot
select以等待的最长时间,以TIMEVAL结构的形式提供。对于阻塞操作,将timeout参数设置为null。
struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
select函数返回已准备好并包含在fd_set结构中的套接字句柄总数,如果时间限制已过期则返回零,如果发生错误则返回SOCKET_ERROR。如果返回值为SOCKET_ERROR,则可以使用WSAGetLastError检索特定的错误代码。
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set, FD_SET, *PFD_SET, *LPFD_SET;
fd_count
套接字的数量
fd_array
套接字数组
struct fd_set ,可以理解为一个集合,实际上是一个位图,每一个特定为来标志相应大小文件描述符,这个集合中存放的是文件描述符(file descriptor),即文件句柄(也就是位图上的每一位都能与一个打开的文件句柄(文件描述符)建立联系,这个工作由程序员来完成),这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,程序员通过操作4类宏,来完成最fd_set的操作:
深入的理解select模型的关键点在于理解fd_set,为了说明方便,我们取fd_set长度为1个字节,fd_set中的每一个bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set;FD_ZERO(&set);则set用位表示为 0000,0000 。
(2)若fd = 5 ,则执行 FD_SET(fd,&set)后,set变为 0001,0000 (第5位置为1)
(3)若再加入fd=2 ,fd=1,则set变为 0001.0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。没有可读事件发生时 fd = 5 被清空。
附录:
select function部分和fd_set部分参考了 select函数详解及实例分析
作者:eten
来源:CSDN
原文:>https://blog.csdn.net/leo115/article/details/8097143