windows socket函数详解

  本文章主要包括了C++语言框架下对windows socket 的总结。
TCP网络通信程序工作流程

0 引用头函数

#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")

1 WSAStartup

初始化Ws2_32.dll的函数
WSAStartup 函数用于初始化供进程调用的Winsock相关的dll。

int WSAStartup(
  __in          WORD wVersionRequested,
  __out         LPWSADATA lpWSAData
);

Parameters

wVersionRequested

The highest version of Windows Sockets specification that the caller can use. The high-order byte specifies the minor version number; the low-order byte specifies the major version number.

The current version of the Windows Sockets specification is version 2.2. The current Winsock DLL, Ws2_32.dll, supports applications that request any of the following versions of Windows Sockets specification,1.0、1.1、2.0、2.1、2.2。

标识了用户调用的Winsock的版本号。高字节指明辅版本编号,低字节指明主版本编号。通常使用MAKEWORD来生成一个版本号。 当前Winsock sockets的版本号为2.2,用到的dll是 Ws2_32.dll。

lpWSAData

A pointer to the WSADATA data structure that is to receive details of the Windows Sockets implementation.

指向WSADATA结构体的指针,lpWSAData返回了系统对Windows Sockets 的描述。

Return Value

If successful, the WSAStartup function returns zero. Otherwise, it returns one of the error codes listed below.

WSAVERNOTSUPPORTED, WSAVERNOTSUPPORTED, WSAEINPROGRESS, WSAEPROCLIM, WSAEFAULT。

The WSAStartup function directly returns the extended error code in the return value for this function. A call to the WSAGetLastError function is not needed and should not be used.

如果调用成功,WSAStartup 函数返回0。否则,将返回五种错误代码之一。但绝对不能使用WSAGetLastError获取错误代码。

WSAData wsa;
    if (::WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    {
        cout<<"WSAStartup error"<<endl;
        return 0;
    }

2 WSACleanup

释放Ws2_32.dl的l函数
该函数释放对Winsock链接库的调用

int WSACleanup(void);

Return Value

The return value is zero if the operation was successful. Otherwise, the value SOCKET_ERROR is returned, and a specific error number can be retrieved by calling WSAGetLastError.

In a multithreaded environment, WSACleanup terminates Windows Sockets operations for all threads.

返回值0表示正常退出,返回值SOCKET_ERROR表示异常。返回值是SOCKET_ERROR,可以调用 WSAGetLastError.查看错误代码。需要注意的是,在多线程环境下,WSACleanup 函数将终止所有线程的socket操作

3 socket

socket函数将创建指定传输服务的socket。

SOCKET WSAAPI socket(
  __in          int af,
  __in          int type,
  __in          int protocol
);

Parameters

af ( address family)

The address family specification. Possible values for the address family are defined in the Winsock2.h header file。 The table below lists common values for address family although many other values are possible。

  指明地址簇类型,常用的地址簇如下,其余地址簇在Winsock2.h中定义。

AF_UNSPEC(未指明)、AF_INET(IPv4)、AF_NETBIOS(NETBIOS地址簇)、AF_INET6(IPv6)、AF_IRDA(Infrared Data Association (IrDA)地址簇)、AF_BTM(Bluetooth)。

type

The type specification for the new socket. Possible values for the socket type are defined in the Winsock2.h header file.The following table lists the possible values for the type parameter supported for Windows Sockets 2:

  指明socket的类型,Windows Sockets 2常见类型如下:

SOCK_STREAM(流套接字,使用TCP协议)、SOCK_DGRAM(数据报套接字,使用UDP协议)、SOCK_RAW(原始套接字)、SOCK_RDM(提供可靠的消息数据报文,reliable message datagram)、SOCK_SEQPACKET(Provides a pseudo-stream packet based on datagrams,在UDP的基础上提供了伪流数据包)。

protocol

The protocol to be used. The possible options for the protocol parameter are specific to the address family and socket type specified. Possible values for the protocol are defined are defined in the Winsock2.h and Wsrm.h header files.

指明数据传输协议,该参数取决于af和type参数的类型。protocol参数在Winsock2.h and Wsrm.h定义。通常使用如下3中协议:

IPPROTO_TCP(TCP协议,使用条件,af是AF_INET or AF_INET6、type是SOCK_STREAM )

IPPROTO_UDP(UDP协议,使用条件,af是AF_INET or AF_INET6、type是SOCK_DGRAM)

IPPROTO_RM(PGM(Pragmatic General Multicast,实际通用组播协议)协议,使用条件,af是AF_INET 、type是SOCK_RDM)

Return Value

If no error occurs, socket returns a descriptor referencing the new socket. Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.

如果不出错,socket函数将返回socket的描述符(句柄),否则,将返回INVALID_SOCKET。

SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (s == INVALID_SOCKET)
    {
        int er = WSAGetLastError();
        return 0;
    }

4 bind

服务端将socket与地址关联。
bind函数将socket关联一个本地地址。

int bind(
  __in          SOCKET s,
  __in          const struct sockaddr* name,
  __in          int namelen
); 

Parameters

s

Descriptor identifying an unbound socket.

指定一个未绑定的socket。

name

Address to assign to the socket from the sockaddr structure.

指向sockaddr地址的指针,该结构含有IP和PORT

namelen

Length of the value in the name parameter, in bytes。

参数name的字节数。

Return Value

If no error occurs, bind returns zero. Otherwise, it returns SOCKET_ERROR, and a specific error code can be retrieved by calling WSAGetLastError.

无错误返回0,又错误返回SOCKET_ERROR。

sockaddr_in service;
  service.sin_family = AF_INET;
  service.sin_addr.s_addr = inet_addr("127.0.0.1");
  service.sin_port = htons(27015);

  //----------------------
  // Bind the socket.
  if (bind( ListenSocket, (SOCKADDR*) &service,sizeof(service)) == SOCKET_ERROR) {
    closesocket(ListenSocket);
    return;
  }

附注:地址转换与赋值的相关操作:

sockaddr结构体如下

typedef struct sockaddr {

#if (_WIN32_WINNT < 0x0600)
    u_short sa_family;
#else 
    ADDRESS_FAMILY sa_family;           // Address family.
#endif //(_WIN32_WINNT < 0x0600)

    CHAR sa_data[14];                   // Up to 14 bytes of direct address.
} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;

sockaddr内部使用数组来参数,赋值不方便,一般使用格式化后的结构体SOCKADDR_IN来赋值。

SOCKADDR_IN结构体

typedef struct sockaddr_in {

#if(_WIN32_WINNT < 0x0600)
    short   sin_family;    
#else //(_WIN32_WINNT < 0x0600)
    ADDRESS_FAMILY sin_family;
#endif //(_WIN32_WINNT < 0x0600)

    USHORT sin_port;
    IN_ADDR sin_addr;
    CHAR sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN;

SOCKADDR_IN里面又包含了in_addr 结构体

// IPv4 Internet address
// This is an 'on-wire' format structure.
//
typedef struct in_addr {
        union {
                struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
                struct { USHORT s_w1,s_w2; } S_un_w;
                ULONG S_addr;
        } S_un;
#define s_addr  S_un.S_addr /* can be used for most tcp & ip code */
#define s_host  S_un.S_un_b.s_b2    // host on imp
#define s_net   S_un.S_un_b.s_b1    // network
#define s_imp   S_un.S_un_w.s_w2    // imp
#define s_impno S_un.S_un_b.s_b4    // imp #
#define s_lh    S_un.S_un_b.s_b3    // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;

htons(host to unsigned short)和htonl(host to unsigned long)

各个机器cpu对数据存储和表示的方法不通,intel机器用littele-endian存数据,而IBM机器用big-endian存数据。网络协议为取消这种差异,一致采用big-endian方式。htons用于将unsigned short的数值从littele-endian转换为big-endian,由于short只有2字节,常用语port数值的转换。htons用于将unsigned long的数值从littele-endian转换为big-endian,long有4字节,常用于ipv4地址的转换。

u_short htons( __in     u_short hostshort);
u_long htonl(__in      u_long hostlong);

inet_addr和inet_ntoa

inet_addr用于将ipv4格式的字符串转换为unsigned long的数值。inet_ntoa用于将struct in_addr的地址转换为ipv4格式的字符串。

unsigned long inet_addr(  __in    const char* cp);
char* FAR inet_ntoa(  __in     struct   in_addr in);

一个常用的特殊值 INADDR_ANY
数值为0。用INADDR_ANY来配置IP地址,意味着不需要知道当前服务器的IP地址。对于多网卡的服务器,INADDR_ANY允许你的服务接收一个服务器上所有网卡发来的数据。下面例子也有写,如果某个socket使用INADDR_ANY和8000端口,那么它将接收该所有网卡传来的数据。而其他socket将无法再使用8000端口。

sockaddr_in service;
    ZeroMemory((char *)&service,sizeof(sockaddr_in));
    service.sin_family = AF_INET;
    //service.sin_addr.S_un.S_addr  =/*INADDR_ANY*/ inet_addr("127.0.0.1");
    service.sin_addr.s_addr  = INADDR_ANY;
    service.sin_port = htons(8278);
    if (bind(s,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
    {
        int er = WSAGetLastError();
        closesocket(s);
    }

4 listen

服务端网络监听

int listen(
  __in          SOCKET s,
  __in          int backlog
);

Parameters

s

Descriptor identifying a bound, unconnected socket.
socket描述符,该socket是一个未连接状态的socket

backlog

Maximum length of the queue of pending connections. If set to SOMAXCONN, the underlying service provider responsible for sockets will set the backlog to a maximum reasonable value. There is no standard provision to obtain the actual backlog value.

Return Value

If no error occurs, listen returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

没有错误发生将返回0,否则返回SOCKET_ERROR

if (listen(s,SOMAXCONN ) == SOCKET_ERROR)
{
    int er = WSAGetLastError();
    closesocket(s);
}

5 accept

SOCKET accept(
 __in          SOCKET s,
 __out         struct sockaddr* addr,
 __in_out      int* addrlen
 );

Parameters

s

A descriptor that identifies a socket that has been placed in a listening state with the listen function. The connection is actually made with the socket that is returned by accept.

listen函数用到的socket。accept函数将创建连接。

addr

An optional pointer to a buffer that receives the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family that was established when the socket from the sockaddr structure was created.

指向通信层连接实体地址的指针。addr 的格式取决于bind函数内地址簇的类型。

addrlen

An optional pointer to an integer that contains the length of structure pointed to by the addr parameter.

addr的长度。

Return Value

If no error occurs, accept returns a value of type SOCKET that is a
descriptor for the new socket. This returned value is a handle for the
socket on which the actual connection is made.

Otherwise, a value of INVALID_SOCKET is returned, and a specific error
code can be retrieved by calling WSAGetLastError.

The integer referred to by addrlen initially contains the amount of
space pointed to by addr. On return it will contain the actual length
in bytes of the address returned.

如果不发生错误,accept将返回一个新的SOCKET描述符,即新建连接的socket句柄。否则,将返回INVALID_SOCKET。传进去的addrlen应该是参数addr的长度,返回的addrlen是实际长度。

SOCKET ac = accept(s,NULL,NULL);
    if (ac == INVALID_SOCKET)
    {
        int er = WSAGetLastError();
        closesocket(s);
    }

如果不发生错误,accept将返回一个新的SOCKET描述符,即新建连接的socket句柄。否则,将返回INVALID_SOCKET。传进去的addrlen应该是参数addr的长度,返回的addrlen是实际长度。

6 connect

int connect(
    __in          SOCKET s,
    __in          const struct sockaddr* name,
    __in          int namelen
);

Parameters

s

Descriptor identifying an unconnected socket.

name

Name of the socket in the sockaddr structure to which the connection
should be established.

与bind函数的name参数类似,指明待连接的地址

namelen

Length of name, in bytes.

Return Value

If no error occurs, connect returns zero. Otherwise, it returns
SOCKET_ERROR, and a specific error code can be retrieved by calling
WSAGetLastError.

On a blocking socket, the return value indicates success or failure of
the connection attempt.

0表示正确,否则,将返回SOCKET_ERROR。如果是阻塞式的socket连接,返回值代表了连接正常与失败。

sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(8828);
        server.sin_addr.s_addr = inet_addr("127.0.0.1");
        if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
        {
            break;
        }

7 send、recv

int send(
  __in          SOCKET s,
  __in          const char* buf,
  __in          int len,
  __in          int flags
);
int recv(
  __in          SOCKET s,
  __out         char* buf,
  __in          int len,
  __in          int flags
);

Parameters

s

A descriptor identifying a connected socket.

buf

A pointer to a buffer containing the data to be transmitted.

数据buffer

len

The length, in bytes, of the data in buffer pointed to by the buf
parameter.

待发送(接受)数据长度

flags

A set of flags that specify the way in which the call is made. This
parameter is constructed by using the bitwise OR operator with any of
the following values.

send(recv)函数的发送(接收)数据方式。MSDN给了以下几种发送方式:

1:MSG_DONTROUTE(Specifies that the data should not be subject to routing. A Windows Sockets service provider can choose to ignore this flag.)

2:MSG_OOB(Sends OOB data (stream-style socket such as SOCK_STREAM only)

3:0

Return Value

If no error occurs, send returns the total number of bytes sent, which
can be less than the number requested to be sent in the len parameter.
Otherwise, a value of SOCKET_ERROR is returned, and a specific error
code can be retrieved by calling WSAGetLastError.

send的返回值标识已发送数据的长度,这个值可能比参数len小,这也意味着数据缓冲区没有全部发出去,要进行后续处理。返回SOCKET_ERROR标识send出错。

If no error occurs, recv returns the number of bytes received. If the
connection has been gracefully closed, the return value is zero.
Otherwise, a value of SOCKET_ERROR is returned, and a specific error
code can be retrieved by calling WSAGetLastError.

recv的返回值标识已接收数据的长度。如果连接已关闭,返回值将是0。返回SOCKET_ERROR标识recv出错。

Client 例子

sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(8828);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
{
    break;
}
str += "windows socket test!";
while (1)
{
    int len = send(cnetsocket,str.c_str(),str.length(),0);
    cout<<"send data:"<<str.c_str()<<" ,length = "<<str.length()<<endl;
    if (len < str.length())
    {
        cout<<"data send uncompleted"<<endl;
        str = str.substr(len+1,str.length());
        len = send(cnetsocket,str.c_str(),str.length(),0);
        cout<<"send data uncomplete,send remaining data :"<<str.c_str()<<" ,length = "<<str.length()<<endl;
    }
    else if (len == SOCKET_ERROR)
    {
        break;
    }
    Sleep(1);
}

Server例子

SOCKET acp = accept(listensocket,NULL,NULL);
if (acp == INVALID_SOCKET)
{
    cout<<"accept error,error code "<<WSAGetLastError()<<endl;
    break;
}
while (1)
{
    char buf[1024];
    int len = recv(acp,buf,1024,0);
    if (len == 0)
    {
        cout<<"connection has been closed "<<endl;
        break;
    }
    else if (len == SOCKET_ERROR)
    {
        cout<<"recv error,error code "<<WSAGetLastError()<<endl;
        break;
    }
    else
    {
        char* outbuf = new char[len+1];
        memcpy(outbuf,buf,len);
        outbuf[len] = 0;
        cout<<"recv data,"<<outbuf<<endl;
        delete outbuf;
    }
}

8 closesocket 

关闭socket

int closesocket(
     __in          SOCKET s
);

Parameters

s

A descriptor identifying the socket to close.

Return Value

If no error occurs, closesocket returns zero. Otherwise, a value of
SOCKET_ERROR is returned, and a specific error code can be retrieved
by calling WSAGetLastError.

如果无错误发生,函数返回0。否则,返回SOCKET_ERROR。

9 shutdown

禁止在socket收发数据

int shutdown(
    __in          SOCKET s,
    __in          int how
);

Parameters

s

Descriptor identifying a socket.

how

Flag that describes what types of operation will no longer be allowed.

how有几类取值:

1:SD_RECEIVE,不允许在socket上recv数据。

2:SD_RECEIVE,不允许在socket上send数据。

3:SD_RECEIVE,不允许在socket上recv和send数据。

Return Value

If no error occurs, shutdown returns zero. Otherwise, a value of
SOCKET_ERROR is returned, and a specific error code can be retrieved
by calling WSAGetLastError.

  shutdown并不关闭socket,只是禁止掉socket的recv或send行为。为保证数据收发完整性,在关闭socket之前应调用shutdown。

10 getsockname

int getsockname(
    __in          SOCKET s,
    __out         struct sockaddr* name,
    __in_out      int* namelen
);

Parameters

s

Descriptor identifying a socket.

name

Pointer to a SOCKADDR structure that receives the address (name) of
the socket.

namelen

Size of the name buffer, in bytes.

Return Value

If no error occurs, getsockname returns zero. Otherwise, a value of
SOCKET_ERROR is returned, and a specific error code can be retrieved
by calling WSAGetLastError.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值