zoukankan      html  css  js  c++  java
  • winsock编程学习笔记

    以下部分转自博客http://blog.csdn.net/phunxm/article/details/5085869

    套接字地址(sockaddr、sockaddr_in)

    1 /*
    2  * Structure used by kernel to store most addresses.
    3  */
    4 struct sockaddr {
    5        u_short sa_family;/* address family,AF_X */
    6        char    sa_data[14];/* up to 14 bytes of direct address */
    7 };

    包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是混杂在一起的。sa_data域的定义有些不确定性,注释暗示内容可能超过14个字节。这种不确定性是经过深思熟虑的。套接字是个非常强大的接口。多数人可能认为比Internet接口强不到哪里——大多数应用现在很可能都用它——套接字可被用于几乎任何种类的进程间通信,Internet(更精确的说是IP)只是其支持的协议簇中的一种。

    1 /*
    2  * Socket address, internet style.
    3  */
    4 struct sockaddr_in {
    5        short   sin_family;/* internet address family */
    6        u_short sin_port;/* port number */
    7        struct  in_addr sin_addr;/* internet address */
    8        char    sin_zero[8];/* padding bits */
    9 };

    这个结构提供了方便的手段来访问socket address(struct sockaddr)结构中的每一个元素。注意sin_zero[8]是为了使sockaddr和sockaddr_in结构具有相同的尺寸,使用sockaddr_in的时候要把sin_zero全部设为零(使用memset函数)。

    Winsock库的加载和卸载

    要使用Windows Socket API进行编程,首先必须调用WSAStartup()函数初始化Winsock动态库。

    int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

    • 参数一wVersionRequested:为我们要求初始化的Winsock版本号
    • 参数二lpWSAData:为实际初始化成功的WSA(Windows Socket API)版本信息。

    套接字的创建和释放

    要使用套接字,首先必须调用socket()函数创建一个套接字描述符,就如同操作文件时,首先得调用fopen()函数打开一个文件。

    1 // The socket function creates a socket that is bound to a specific service provider.
    2 SOCKET socket(int af,// [in] Address family specification.
    3            int type,// [in] Type specification for the new socket.
    4            int protocol// [in] Protocol to be used with the socket that is specific to the indicated address family.
    5            );
    1 // The closesocket function closes an existing socket.
    2 int closesocket(
    3     SOCKET s// [in] Descriptor identifying the socket to close.
    4 );

     af:通信协议的协议族 ,AF_INET(IPv4)或 AF_INET6(IPv6)            

     type:指定要创建的套接字的类型。SOCK_STREAM, SOCK_ DGRAM,SOCK_RAW

     protocol指定应用程序所使用的通信协议 :

             IPPROTO_TCP, IPPROTO_UDP, 0(如果不想指定)

    绑定套接字到指定的IP地址和端口

    对于传输套接字,在执行收发数据前需要对本地端口进行绑定,这是因为传输层需要使用端口来区分具体的通信端点

    1 // The bind function associates a local address with a socket.
    2 int bind(
    3        SOCKET s, // [in] Descriptor identifying an unbound socket.
    4        const struct sockaddr FAR *name, // [in] Address to assign to the socket from the SOCKADDR structure.
    5        int namelen // [in] Length of the value in the name parameter.                        
    6        );

    成功返回0,失败返回SOCKET_ERROR

    TCP服务器设置套接字进入监听状态

    服务器为了接受连接,首先使用socket()函数创建一个套接字,然后使用bind()函数将它绑定到一个本地地址(端口),再用listen()函数为到达的连接指定一个backlog。

    1 // The listen function places a socket a state where it is listening for an incoming connection.
    2 int listen(
    3         SOCKET s,// [in] Descriptor identifying a bound, unconnected socket.
    4         int backlog// [in] Maximum length of the queue of pending connections.
    5         );

    backlog参数指定了正在等待连接的最大队列长度。这个参数非常重要,因为服务器完全可能同时收到几个连接请求。假定backlog参数为2,如果三个客户机同时发出请求,那么头两个会被放在一个“待决”(等待处理)队列中,以便应用程序依次为它们提供服务。而第三个连接会造成一个WSAECONNREFUSED错误。注意,一旦服务器接受了一个连接(accept返回),那个连接请求就会从队列中删去,以便别人可继续发出请求。

    该函数执行成功返回0,失败返回SOCKET_ERROR 

    客户端主动连接

    1 // The connect function establishes a connection to a specified socket.
    2 int connect(
    3           SOCKET s,// [in] Descriptor identifying an unconnected socket.
    4           const struct sockaddr FAR *name,// [in] Name of the socket to which the connection should be established.
    5           int namelen// [in] Length of name.
    6          );

    客户端是连接的发起者initiate),它通过调用connect()函数主动(active)连接服务器。参数二填写欲连接的目标服务器的地址。如果连接的计算机并没有在指定端口上监听,则connect()调用返回SOCKET_ERROR,WSAGetLastError()=WSAECONNREFUSED;另一种错误是WSAETIMEOUT,例如由于路由或网络故障,客户端迟迟接受不到服务器回馈的[SYN,ACK]信号。

    TCP服务器接受客户连接请求

    1 // The accept function permits an incoming connection attempt on a socket.
    2 SOCKET accept(
    3            SOCKET s,// [in] Descriptor identifying a socket that has been placed in a listening state with the listen function.
    4            struct sockaddr FAR *addr,// [out] receives the address of the connecting entity, as known to the communications layer.
    5            int FAR *addrlen// [out] the length of addr.
    6            );

    服务器进入listen状态后,循环调用accept()接受客户的连接。参数一为监听套接字;参数二为远端客户的地址信息;该函数返回一个套接字句柄,负责后续与该远端客户的会话通信。监听套接字总是默默无闻的在门口守望(listen),迎接(accept)客户的到来并安排服务员(会话套接字)接客。

    该函数执行成功返回新的socket,失败返回SOCKET_ERROR 

    在一个已绑定或已连接的套接字上获取连接名和对方地址信息获取sockaddr

    1 int getsockname (SOCKET s, struct sockaddr *name, int* namelen);

    获取hostname

    Host即通常意义上的机器名(Machine Name)或域名(Domain Name)。

    1 int gethostname (char FAR *name, int namelen);

    gethostname()函数可以取得调用主机的机器名。返回的这个name传给gethostbyname()调用可以取得相应IP地址。

    1 struct hostent* gethostbyname(const char* name);

    gethostbyname()函数主要用来做DNS解析,传入域名(例如www.baidu.com),返回hostent结构。struct hostent存放主机信息。

     1 /*
     2  * Structures returned by network data base library, taken from the
     3  * BSD file netdb.h. All addresses are supplied in host order, and
     4  * returned in network order (suitable for use in system calls).
     5  */
     6 struct hostent {
     7        char   FAR *h_name;          /* official name of host */
     8        char   FAR *FAR *h_aliases; /* alias list */
     9        short  h_addrtype;            /* host address type */
    10        short  h_length;              /* length of address */
    11        char   FAR *FAR *h_addr_list;/* list of addresses */
    12 #defineh_addr h_addr_list[0]         /* address, for backward compat */
    13 };
    14 /* Microsoft Windows Extended data types */
    15 typedef struct hostent HOSTENT, *PHOSTENT, *LPHOSTENT;

    以下代码段获取百度(www.baidu.com)机器名和地址。

    1 struct hostent *pHostBaiDu = gethostbyname("www.baidu.com");
    2 printf("Host name: %s/n", pHostBaiDu->h_name);
    3 printf("IP Address: %s/n", inet_ntoa(*((struct in_addr*)pHostBaiDu->h_addr)));

    I/O通信

    从I/O的角度来看,套接字也是文件,它提供了同文件读写(fread()/fwrite())对应的收发数据操作接口:send()/recv()。

    发送数据

    1 // The send function sends data on a connected socket.
    2 int send(
    3        SOCKET s,// [in] Descriptor identifying a connected socket.
    4        const char FAR *buf,// [in] Buffer containing the data to be transmitted.
    5        int len,// [in] Length of the data in buf.
    6        int flags// [in] Indicator specifying the way in which the call is made.
    7        );

    send()函数在一个已连接的套接字s上执行数据发送操作。对于客户机而言,发送的目标地址即connect()调用时所指定的地址;对于服务器而言,发送的目标地址即accept()调用所返回的地址。发送的内容为保存在缓冲区buf中,发送的内容长度为len。最后一个参数flags,通常情况下填0。

    成功返回发送字节数,出错返回SOCKET_ERROR

    接收数据

    1 // The recv function receives data from a connected or bound socket.
    2 int recv(
    3        SOCKET s,// [in] Descriptor identifying a connected socket.
    4        char FAR *buf,// [out] Buffer for the incoming data.
    5        int len,// [in] Length of buf.
    6        int flags// [in] Flag specifying the way in which the call is made.
    7        );

    recv()函数在一个已连接的套接字s上执行数据接收操作。对于客户机而言,数据的源地址即connect()调用时所指定的地址;对于服务器而言,数据的源地址即accept()调用所返回的地址。接收的内容为保存至长度为len的缓冲区buf,最后一个参数flags,通常情况下填0。

    成功返回接收的数据的字节数量,失败返回SOCKET_ERROR,如果接受数据时网络中断返回0。

    关闭套接字(TCP连接)

    1 // The shutdown function disables sends or receives on a socket.
    2 int shutdown(
    3            SOCKET s,// [in] Descriptor identifying a socket.
    4            int how// [in] Flag that describes what types of operation will no longer be allowed.
    5            );

    WinSock TCP C/S通信示例

    简单通信示例:

    客户端代码:

     1 #include <winsock2.h>
     2 #include <stdio.h> 
     3 #define SERVPORT    5050              // 端口为5150
     4 #define MAXDATASIZE 100
     5 #define SERVIP      “127.0.0.1”      // 服务器IP地址为“127.0.0.1”,
     6 #pragma comment(lib,"ws2_32.lib")
     7  
     8 void main(int argc, char *argv[])
     9 {
    10     WSADATA              wsaData;
    11     SOCKET               sConnect;
    12     SOCKADDR_IN          serverAddr;    
    13     int            recvbytes;
    14     char            buf[MAXDATASIZE]; 
    15     WSAStartup(MAKEWORD(2,2), &wsaData);
    16     sConnect = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    17     serverAddr.sin_family = AF_INET;
    18     serverAddr.sin_port = htons(SERVPORT);    
    19     serverAddr.sin_addr.s_addr = inet_addr(SERVIP);
    20     memset(&(serverAddr.sin_zero), 0, sizeof(serverAddr.sin_zero));
    21     if(
    22     connect(sConnect,   (SOCKADDR*)&serverAddr,sizeof(SOCKADDR))==SOCKET_ERROR)
    23     {
    24         printf("connect failed!\n");
    25         return;
    26     }
    27     recvbytes = recv(sConnect, buf, MAXDATASIZE, 0);
    28     if (recvbytes == SOCKET_ERROR){    printf("recv failed!\n");}
    29     else{buf[recvbytes] = '\0';printf("%s\n",buf);    } 
    30     closesocket(sConnect);
    31     WSACleanup();
    32 }
    33     

    服务端代码:

     1 #include <winsock2.h>
     2 #include <stdio.h> 
     3 #define SERVPORT    5050
     4 #pragma comment(lib,"ws2_32.lib") 
     5 void main(void)
     6 {
     7     WSADATA              wsaData;
     8     SOCKET               sListen;        // 监听socket
     9     SOCKET               sClient;        // 连接socket
    10     SOCKADDR_IN          serverAddr;        // 本机地址信息
    11     SOCKADDR_IN          clientAddr;        // 客户端地址信息
    12     int                    clientAddrLen;    // 地址结构的长度
    13     int                  nResult;
    14     WSAStartup(MAKEWORD(2,2), &wsaData);   
    15     sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);         
    16     serverAddr.sin_family = AF_INET;
    17     serverAddr.sin_port = htons(SERVPORT);    
    18     serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    19     memset(&(serverAddr.sin_zero), 0, sizeof(serverAddr.sin_zero)); 
    20     nResult = bind(sListen, (SOCKADDR *)&serverAddr, sizeof(SOCKADDR));
    21    if (nResult == SOCKET_ERROR)
    22    {    
    23         printf("bind failed!\n"); 
    24     return;   
    25 26     listen(sListen, 5);  
    27     while(1)
    28    { 
    29         clientAddrLen = sizeof (SOCKADDR);
    30         sClient = accept(sListen, (SOCKADDR *)&clientAddr, &clientAddrLen);
    31         if(sClient == INVALID_SOCKET){printf("Accept failed!");    }
    32         else{
    33             printf("Accepted client: %s : %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));        
    34         nResult = send(sClient, "Connect success!", 16, 0);
    35         if (nResult == SOCKET_ERROR){printf("send failed!");}
    36     }
    37     closesocket(sClient);
    38     }
    39      closesocket(sListen); 
    40      WSACleanup();
    41 }
  • 相关阅读:
    读书笔记之理想设计的特征
    一些javascript 变量声明的 疑惑
    LINQ 使用方法
    Google MySQL tool releases
    读书笔记之设计的层次
    EF之数据库连接问题The specified named connection is either not found in the configuration, not intended to be used with the Ent
    转载 什么是闭包
    javascript面向对象起步
    Tips
    数据结构在游戏中的应用
  • 原文地址:https://www.cnblogs.com/chengyu404/p/5398386.html
Copyright © 2011-2022 走看看