网络编程之Winsock2

1、在Windows操作系统环境下,使用Windows Sockets API进行网络程序开发时,需要调用Windows操作系统的Windows Sockets动态库 包含Sockets头文件 导入相应库文件;下面是相应的导入

具体用代码实现是这样的:

这是用1.1版本

#include <WINSOCK.h>

#pragma comment(lib, "wsock32.lib");

2.2版本

#include <WINSOCK2.h>

#pragma comment(lib, "WS2_32.lib");

也可在项目属性当中设置库文件,根据下面的步骤在附加依赖项里面写入上面库文件的名称

配置属性-》连接器-》输入-》附加依赖项

2、下面是这个Winsock程序的起始与结束

3、具体函数使用

1)int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData );

作用:完成WinSock的初始化任务,协商WinSock的版本支持,并分配必要的资源。

参数:

wVersionRequested参数用于指定准备加载的Winsock库的版本。高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。可用MAKEWORD(x,y)(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值

lpWSAData参数是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。即返回Winsock的实现信息。

返回:

如果WinSock.dll或底层网络子系统没有被正确初始化或没有被找到,WSAStartup将返回WSASYSNOTREADY。

调用成功WSAStartup将返回0。

此函数允许你的应用程序协商使用某种版本的WinSock规范,如果请求的版本等于或高于DLL所支持的最低版本,WSAData的wVersion成员中将包含你的应用程序应该使用的版本,它是DLL所支持的最高版本与请求版本中较小的那个。反之,如果请求的版本低于DLL所支持的最低版本,WSAStartup将返回WSAVERNOTSUPPORTED。

WSADATA的结构:

typedef struct WSAData { 
  WORD wVersion; 
  WORD wHighVersion; 
  char szDescription[WSADESCRIPTION_LEN+1]; 
  char szSystemStatus[WSASYS_STATUS_LEN+1]; 
  unsigned short iMaxSockets; 
  unsigned short iMaxUdpDg; 
  char FAR * lpVendorInfo; 
} WSADATA, *LPWSADATA;

各个成员说明:

注意:一般不要使用下面这两个字段:

iMaxSockets和iMaxUdpDg,它们是假定同时最多可打开多少套接字和数据报的最大长度。然而,要知道数据报的最大长度应该通过WSAEnumProtocols来查询协议信息。同时最多可打开套接字的数目不是固定的,很大程度上和可用物理内存的多少有关。

2)int WSACleanup() ;

作用:当不需WinSock DLL的服务,释放DLL所使用的资源。

参数:无

 

返回:调用成功返回0

对应于每一次WSAStartup()调用必须有一个WSACleanup()调用。

3)SOCKET socket( int af, int type, int protocol );

作用:创建不同类型的套接字用来通信

参数:

af:指定地址族,对于TCP/IPv4协议的套接字,它只能是AF_INET(也可写成PF_INET)

type:指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字

protocol:指定与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动选择一个合适的协议。

返回:

如果函数调用成功,它将返回一个新的SOCKET数据类型的套接字描述符。

如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。

下面是相关参数的对应说明:

4)WSAGetLastError();

作用:调用任何一个WinSock函数之后可用WSAGetLastError函数来获得详细的错误代码 int WSAGetLastError (void);

参数:无

 

返回值 详细的错误代码(可以通过相应的代码查到相应的错误)

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

作用:对创建的套接字绑定一个ip,一般用在服务器端

参数:

s:指定要绑定的套接字

name:指定了该套接字的本地地址信息,是指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能(通常会)随所使用的网络协议不同而不同,所以,要用

namelen:指定该地址结构的长度。

返回:

如果这个函数调用成功,它将返回0。

如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

6)地址结构体解析:

 struct sockaddr {
      u_short sa_family; 
      char sa_data[14]; 
     }; 

sa_family指定该地址家族,在这里IPv4必须设为AF_INET。

sa_data仅仅是表示要求一块内存分配区,起到占位的作用,该区域中指定与协议相关的具体地址信息。由于实际要求的只是内存区,所以对于不同的协议家族,用不同的结构来替换sockaddr。在IPv4中,我们用sockaddr_in结构替换sockaddr,以方便我们填写地址信息。

除了sa_family外,sockaddr是按网络字节顺序表示的。

sockaddr_in的结构如下:

struct sockaddr_in{ 
  short sin_family; 
  unsigned short sin_port; 
  struct in_addr sin_addr; 
  char sin_zero[8]; 
};

sin_family表示地址族,对于IPv4地址,sin_family成员将一直是AF_INET。

成员sin_port指定的是将要分配给套接字的端口

成员sin_addr给出的是套接字的主机IP地址。

成员sin_zero只是一个填充数,以使sockaddr_in结构和sockaddr结构的长度一样。

struct in_addr { 
  union {           
    struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;  
struct { u_short s_w1,s_w2; }S_un_w;  
       u_long S_addr;
   } S_un;
}

 

下面对sockaddr和sockaddr_in对比:

所以很多时候,我们绑定ip的时候是这样的:

sockaddr_in s;
s.sin_addr.s_un.s_addr = ip;

一般服务器这样设置ip

将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。 将地址指定为INADDR_ANY,允许一个独立应用接受发自多个接口的回应。

如果想要自己设置ip,需要用到下面的函数:

unsigned long inet_addr(constchar FAR * cp)
作用:该函数将一个点分十进制IP地址字符串转换成32位数字表示的IP地址,适合分配给S_addr的u_long类型的数值

这个函数是上面函数的反函数:

char * inet_ntoa ( struct in_addr in ) in为传入参数,表示一个结构型的IP主机地址,该函数将一个32位数字表示的IP地址转换成点分十进制IP地址字符串

这些结构的宏定义

#define s_addr  S_un.S_addr
#define s_host  S_un.S_un_b.s_b2
#define s_net   S_un.S_un_b.s_b1
#define s_imp   S_un.S_un_w.s_w2
#define s_impno S_un.S_un_b.s_b4
#define s_lh    S_un.S_un_b.s_b3

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

作用:服务器监听某个套接字,以便处理各个连接

参数:

s:要监听的套接字

backlog:等待连接队列所能放置的连接数的最大数

 

返回:如果这个函数调用成功,它将返回0。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

8)SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );

作用:用来接收某个连接的套接字

s:接收连接的Socket

addr:建立连接的对方地址信息

addrlen:addr的长度

返回:如果函数调用成功,它将返回一个新的SOCKET数据类型的套接字描述符。 如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。

9)int send ( SOCKET s, const char FAR * buf, int len, int flags );

作用:向目的主机发送数据

参数:

flags:用于控制数据传输方式,0表示按正常方式发送数据;宏MSG_DONTROUTE说明系统目标主机就在直接连接的本地网络中,无需路由选择;MSG_OOB指出数据是按带外数据发送的

s:发送的套接字

buf:发送数据所在缓冲区的首地址

len:发送数据的大小

返回:如果这个函数调用成功,它将返回发送的字节数。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

 10)int recv ( SOCKET s, char FAR* buf, int len, int flags );

作用:接受数据

参数:

flags:指定调用的方式。0表示接收的是正常数据,无特殊行为。MSG_PEEK表示会使有用的数据复制到所提供的接收端缓冲区内,但是没有从系统缓冲区中将数据删除。MSG_OOB表示处理带外数据。

s:接受数据的套接字

buf:接受缓冲区的首地址

len:接受缓冲的大小

返回:如果这个函数调用成功,它将返回接收到的字节数。如果连接被关闭,返回值为0。调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

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

作用:在客户端使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立一条到服务器TCP的连接。

参数:

s:要连接的套接字

name:地址结构

namelen:地址结构长度

返回:

如果这个函数调用成功,它将返回0。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

若TCP没有收到SYN分节的响应,则返回WSAETIMEDOUT

若对客户SYN的响应是RST,则表明该服务器主机在我们指定的端口上没有进程在等待与之连接(例如服务器进程也许没有运行) ,客户一收到RST就马上返回WSAECONNREFUSED

若客户发出的SYN在中间的某个路由器上引发了一个目的不可达ICMP错误,则客户主机内核保存该消息,并按一定时间间隔继续发送SYN,若在规定的时间仍未收到响应,则返回错误WSAEHOSTUNREACH 或WSAENETUNREACH

12) int closesocket( SOCKET s );

作用:关闭套接字,释放所占资源

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

作用:在一个套接字上的读写操作完成后,应该首先使用shutdown()函数来关闭套接字的读通道、写通道或读写通道,这样做的好处是当双方不再有数据要发送或接收时,可以通知对方, “优雅”地关闭连接。

参数:

s:关闭的套接字

how:用来规定关闭的方式

shutdown函数只关闭读写通道,并不关闭套接字,且套接字所占有的资源将被一直保留到closesocket()调用之前。

UDP接受发送数据与TCP接受数据大致相似,不过由于udp在通信之前不用连接,所以在发送的时候要加入地址信息:

int recvfrom ( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen );

int sendto ( SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen );

附送源码(服务器时间同步)实例(客户端):

#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib, "WS2_32.lib");

int main()
{
    WORD wVersionRequested;  
    WSADATA wsaData;  
    int err;  
    
    wVersionRequested = MAKEWORD(2, 2);  
   
    err = WSAStartup(wVersionRequested,&wsaData);  
    if (err != 0)
    {    
        printf("WSAStartup failed witherror: %d\n", err);  
        return 0;  
    } 
    
    SOCKET mySocket = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in s;
    s.sin_family = AF_INET;
    s.sin_port = 5555;
    s.sin_addr.S_un.S_addr = inet_addr("xxx.xxx.xxx.xxx");

    err = connect(mySocket, (struct sockaddr*)&s, sizeof(s));
    if (SOCKET_ERROR == err)
    {
        int err_num = WSAGetLastError();
        printf("connect error%d\n", err_num);
        system("pause");
        return 1;
    }
    
    char sendBuff[200];
    char recvBuff[200];
    memset(recvBuff, 0, 200);
    memset(sendBuff, 0, 200);

    while (1)
    {
        gets(sendBuff);
        send(mySocket, (const char*)sendBuff, 200, 0);
        
        err = recv(mySocket, recvBuff, 200, 0);
        if (SOCKET_ERROR == err)
        {
            int err_num = WSAGetLastError();
            printf("recv´íÎó%d\n", err_num);
            system("pause");
            return 1;
        }
        printf("%s", recvBuff);
        printf("echo once again?\n");
        memset(recvBuff, 0, 200);
        memset(sendBuff, 0, 200);
    }
    closesocket(mySocket);
    WSACleanup(); 
    return 0;
}

服务器端

#include <stdio.h>
#include <Winsock.h>
#include <time.h>
#include <ctime>
#include <stdlib.h>

#pragma comment(lib, "WS2_32.lib");

int main()
{
    WORD wVersionRequested;  
    WSADATA wsaData;  
    int err;  
    
    wVersionRequested = MAKEWORD(2, 2);  
   
    err = WSAStartup(wVersionRequested,&wsaData);  
    if (err != 0)
    {    
        printf("WSAStartup failed witherror: %d\n", err);  
        return 0;  
    } 
    
    SOCKET mySocket = socket(AF_INET, SOCK_STREAM, 0);
    SOCKET clientS;

    struct sockaddr_in s;
    s.sin_family = AF_INET;
    s.sin_port = 5555;
    s.sin_addr.S_un.S_addr = INADDR_ANY;

    err = bind(mySocket, (struct sockaddr*)&s, sizeof(s));
    if (SOCKET_ERROR == err)
    {
        int err_num = WSAGetLastError();
        printf("bind error %d\n", err_num);
        system("pause");
        return 1;
    }
    
    err = listen(mySocket, 2);
    if (SOCKET_ERROR == err)
    {
        int err_num = WSAGetLastError();
        printf("listen error %d\n", err_num);
        system("pause");
        return 1;
    }
    
    printf("Waiting for client connecting!\n");
    int size = sizeof(s);
    clientS = accept(mySocket, (struct sockaddr*)&s, &size);

    if (INVALID_SOCKET == clientS)
    {
        printf("accept error\n");return 1;
    }

    char sendBuff[200];
    char recvBuff[200];
    memset(recvBuff, 0, 200);
    memset(sendBuff, 0, 200);
    
    time_t t;

    while (1)
    {    
        err = recv(clientS, recvBuff, 200, 0);
        if (SOCKET_ERROR == err)
        {
            int err_num = WSAGetLastError();
            printf("recv error %d\n", err_num);
            system("pause");
            return 1;
        }
        
        t = time(&t);
        strcat(sendBuff, "echo:");
        strcat(sendBuff+sizeof("echo:")-1, ctime(&t));

        printf("send:%s", sendBuff+sizeof("echo:"));
        err = send(clientS, sendBuff, 200, 0);
        if (SOCKET_ERROR == err)
        {
            int err_num = WSAGetLastError();
            printf("send error %d \n", err_num);
            system("pause");
            return 1;
        }
        memset(sendBuff, 0, 200);
    }

    closesocket(mySocket);
    WSACleanup(); 
    return 0;
}

 

posted @ 2014-04-14 16:55  Hacker_MJW  阅读(566)  评论(0编辑  收藏  举报