WinSock网络通信

来源:互联网 发布:科幻小说 知乎 编辑:程序博客网 时间:2024/05/18 03:43

前言:

Winsock是Windows下的网络编程接口,它是由Unix下的BSD Socket发展而来,是一个与网络协议无关的编程接口。
构建编程环境
Winsock在常见的Windows平台上有两个主要的版本,即Winsock1和Winsock2。编写与Winsock1兼容的程序你需要引用头文件WINSOCK.H,如果编写使用Winsock2的程序,则需要引用WINSOCK2.H。此外还有一个MSWSOCK.H头文件,它是专门用来支持在Windows平台上高性能网络程序扩展功能的。使用WINSOCK.H头文件时,同时需要库文件WSOCK32.LIB,使用WINSOCK2.H时,则需要WS2_32.LIB,如果使用MSWSOCK.H中的扩展API,则需要MSWSOCK.LIB。正确引用了头文件,并链接了对应的库文件,你就构建起编写WINSOCK网络程序的环境了。
初始化Winsock
每个Winsock程序必须使用WSAStartup载入合适的Winsock动态链接库,如果载入失败,WSAStartup将返回SOCKET_ERROR


主要代码分析

服务端程序:
第一步:包含WinSocket的头文件和动态链接库(DLL)

#include <Winsock2.h>#include <windows.h>#pragma comment(lib,"ws2_32")

第二步:初始化WSAStartup

WSAStarup的原型如下:int WSAStartup(  __in   WORD wVersionRequested,  __out  LPWSADATA lpWSAData);

其中的第一个参数是版本,第二个参数是一个指向WSADATA结构体的指针,代码如下:

WORD Version = MAKEWORD(2,2);  //通过MAKEWORD来设定版本,高版本和低版本都为2,也就是限定了版本必须为2WSADATA wsadata ;    //结构体的一个成员 wsadata//初始化并判断是不是成功,如果成功的话返回的是0,不成功的话返回的值为非0,那么就要使用WSAleanup()函数了if(WSAStartup(Version,&wsadata)){cout<<" WSAStartup出错!"<<endl;WSACleanup();}

第三步,初始化一个Socket
在MSDN中socket的原型如下

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

其中第一个参数为网络层协议类型,第二个参数为socket类型,第三个参数为传输层的协议类型,代码如下:

//第一个参数多数时候为AF_INET,第二个参数可以为SOCK_STREAM适用于可信连接,也就是tcp链接,或者为SOCK_DGRAM,适用于UDP协议第三个参数为传输层协议,如果第二个参数为SOCK_STREAM,这里就为IPPROTO_TCP,如果是SOCK_DGRAM,这里就为IPPROTO_UDPSOCKET ServerSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(ServerSocket ==INVALID_SOCKET){    cout<<"套接字打开错误"<<endl;}

第四步:bind

在MSDN中bind的定义如下:int bind(  __in  SOCKET s,  __in  const struct sockaddr* name,  __in  int namelen);

第一个参数一个已经初始化的socket,第一个参数为执行sockaddr或者是sockaddr_in的结构体的起始地址,
第三个参数为结构体的大小,代码如下:

sockaddr_in  ServerAddr;//定义一个结构体成员,这里使用的是sockaddr_in结构体ServerAddr.sin_family=AF_INET;//初始化成员的sin_family为AF_INETServerAddr.sin_addr.S_un.S_addr=INADDR_ANY;//允许任意的IP地址进行连接int duankou;cout << "请输入端口号:" ;cin >> duankou;ServerAddr.sin_port=htons(duankou);//侦听端口为duankou,这里使用了htons函数//这里需要说明的是需要对第二个参数进行强制类型转换,(LPSOCKADDR)&ServerAddr,并且判断是不是bind成功,成功后返回的是为0if(bind(ServerSocket,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr))){    cout<<"接口捆绑失败(bind Fail)"<<endl;}

第五步:侦听

在MSDN中listen的原型如下:int listen(  __in  SOCKET s,  __in  int backlog);

依然第一个参数为初始化了的socket,第二个参数为最大允许的连接数,代码如下:

//这里依然使用的是上面的socket:ServerSocketif(listen(ServerSocket,5)){    cout<<"监听失败(listen Fail)"<<endl;}

第六步:accept

在MSDN中原型如下:SOCKET accept(  __in     SOCKET s,  __out    struct sockaddr* addr,  __inout  int* addrlen);

第一个参数一个新的客户端的Socket,这里需要定义一个,第二个参数依然为执行sockaddr或者是sockaddr_in的结构体的首地址,第三个参数为长度,这里accept负责对新定义的socket进行初始化,如果成功accept返回的一个可用的sokcet。
代码如下:

sockaddr_in Clientaddr;//定义一个结构体成员SOCKET ClientSocket;//定义一个socket用于接收accept的返回值int lAddrlen=sizeof(Clientaddr);//获取结构体的长度while(TRUE){//因为要一直的侦听,所以这里使用无限循环ClientSocket=accept(ServerSocket,(LPSOCKADDR)&Clientaddr,&lAddrlen);//与上面类似,需要强制转换,第一个参数为已经初始化的socketif (ClientSocket == INVALID_SOCKET){            cout << "连接失败" << endl;        }        else{            cout << "连接到地址:" << inet_ntoa(Clientaddr.sin_addr) << endl;        }

第七步:如果accept成功,recv消息
由于要多次交互,所以这是在一个while循环中实现
在MSDN中recv原型如下:

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

第一个参数为accept返回的socket,第二个参数为输出接收的消息的一个指针,第三个为消息的长度,第四个为标志,设定接收数据的方式,代码如下:

while (TRUE){            cout << "准备接受信息" << endl;            char RecvMessage[1024];            int MessageLen = recv(ClientSocket, RecvMessage, sizeof(RecvMessage), 0);            if (MessageLen < 0){                cout << "接受信息失败!" << endl;            }            else{                RecvMessage[MessageLen] = 0x00;                if (strcmp(RecvMessage, "bye") == 0)                {                    cout << "客户端已关闭,此次交流结束!" << endl;                    system("pause");//处理客户端关闭状态                    cout << "服务器继续监听端口:" << duankou << endl;                    break;                }                cout << "接受成功,信息为:" << RecvMessage << endl;            }

第八步:回复客户端消息 send
send在MSDN中的代码如下:

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

第一个参数为accept返回的socket,第二个参数为内容的开始地址,第三个为长度,第四个为发送的方式,代码如下:

    cout << "准备发送信息" << endl;            cout << "请输入信息:";            char  Message[100];            cin >> Message;            if (strcmp(Message, "bye") == 0)            {                send(ClientSocket, Message, strlen(Message), 0);                flag = 1;                break;            }            cout << "发送信息:" << Message << endl;            if (send(ClientSocket, Message, strlen(Message), 0) < 0){                cout << "发送信息失败!" << endl;            }            else{                cout << "发送信息成功!" << endl;            }        }

第九步:完成后,关闭客户端与服务端的socket:

        closesocket(ClientSocket);        if (flag)            break;

flag用于退出整个服务端。当flag为1时,退出。

第十步:“清理”现场:

closesocket(ServerSocket);WSACleanup();return 0;

整体代码:

#include <iostream>#include <Winsock.h>#pragma comment(lib,"ws2_32")using namespace std;int main(int argc, char argv[]){    WORD Version = MAKEWORD(2, 2);    WSADATA wsadata;    if (WSAStartup(Version, &wsadata)){        cout << "WSAStartup出错!" << endl;        WSACleanup();    }    SOCKET ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    if (ServerSocket == INVALID_SOCKET){        cout << "套接字打开错误" << endl;    }    sockaddr_in ServerAddr;    ServerAddr.sin_family = AF_INET;    ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;    int duankou;    cout << "请输入端口号:" ;    cin >> duankou;    ServerAddr.sin_port = htons(duankou);    if (bind(ServerSocket, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr))){        cout << "接口捆绑失败(bind Fail)" << endl;    }    if (listen(ServerSocket, 5)){        cout << "监听失败(listen Fail)" << endl;    }    sockaddr_in Clientaddr;    SOCKET ClientSocket;    int lAddrlen = sizeof(Clientaddr);    cout << "服务器初始化成功,正在监听" << endl;    int flag = 0;    while (TRUE){        ClientSocket = accept(ServerSocket, (LPSOCKADDR)&Clientaddr, &lAddrlen);        if (ClientSocket == INVALID_SOCKET){            cout << "连接失败" << endl;        }        else{            cout << "连接到地址:" << inet_ntoa(Clientaddr.sin_addr) << endl;        }        while (TRUE){            cout << "准备接受信息" << endl;            char RecvMessage[1024];            int MessageLen = recv(ClientSocket, RecvMessage, sizeof(RecvMessage), 0);            if (MessageLen < 0){                cout << "接受信息失败!" << endl;            }            else{                RecvMessage[MessageLen] = 0x00;                if (strcmp(RecvMessage, "bye") == 0)                {                    cout << "客户端已关闭,此次交流结束!" << endl;                    system("pause");                    cout << "服务器继续监听端口:" << duankou << endl;                    break;                }                cout << "接受成功,信息为:" << RecvMessage << endl;            }            cout << "准备发送信息" << endl;            cout << "请输入信息:";            char  Message[100];            cin >> Message;            if (strcmp(Message, "bye") == 0)            {                send(ClientSocket, Message, strlen(Message), 0);                flag = 1;                break;            }            cout << "发送信息:" << Message << endl;            if (send(ClientSocket, Message, strlen(Message), 0) < 0){                cout << "发送信息失败!" << endl;            }            else{                cout << "发送信息成功!" << endl;            }        }        closesocket(ClientSocket);        if (flag)            break;    }    closesocket(ServerSocket);    WSACleanup();    return 0;}

客户端程序:

第一步 包含头文件和DLL

#include <winsock.h>#include <iostream>#pragma comment(lib,"ws2_32")

第二步 初始化WSAStartup

WORDwVersion = MAKEWORD(2,2);WSADATA ClientData;if(WSAStartup(wVersion,&ClientData)){cout<<" WSAStartup打开失败l"<<endl;}

第三步 打开套接字

SOCKET ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (ClientSocket == INVALID_SOCKET){    cout << "套接字打开失败" << endl;}

第四步 connect 连接服务器

int duankou;    cout << "请输入端口号:";    cin >> duankou;    Clientaddr.sin_port = htons(duankou);    Clientaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//inet_addr(),参数为一个字符串,将IP字符串的值传递给inet_addr    Clientaddr.sin_family = AF_INET;    if (connect(ClientSocket, (LPSOCKADDR)&Clientaddr, sizeof(Clientaddr))){        cout << "连接失败!" << endl;        closesocket(ClientSocket);        WSACleanup();        return 0;    }    cout << "客户端初始化成功,准备交流" << endl;

第五步 信息交互

while (1)    {        cout << "请输入信息:";        char buff[1000];        cin >> buff;        if (strcmp(buff, "bye") == 0)        {            send(ClientSocket, buff, strlen(buff), 0);            break;        }        cout << "发送信息:" << buff << endl;        if (send(ClientSocket, buff, strlen(buff), 0) < 0){            cout << "发送失败!" << endl;        }        else{            cout << "发送成功!" << endl;        }        cout << "准备接受信息" << endl;        char  RecMessage[1024];        int MessageLen = recv(ClientSocket, RecMessage, sizeof(RecMessage), 0);        //cout << MessageLen << endl;测试用        if (MessageLen < 0){            cout << "接受失败" << endl;        }        else{            RecMessage[MessageLen] = 0X00;            if (strcmp(RecMessage, "bye") == 0)//处理bye过程            {                cout << "服务器已关闭,交流结束" << endl;                system("pause");                break;            }            cout << "接受成功,信息为:" << RecMessage << endl;        }    }

第七步 释放资源

    closesocket(ClientSocket);WSACleanup();

整体代码:

#include <windows.h>#include <Winsock.h>#include <iostream>#pragma comment(lib,"ws2_32")using namespace std;int main(int argc, char argv[]){    WORD wVersion = MAKEWORD(2, 2);    WSADATA ClientData;    if (WSAStartup(wVersion, &ClientData)){        cout << "WSAStartup打开失败" << endl;    }    SOCKET ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    if (ClientSocket == INVALID_SOCKET){        cout << "套接字打开失败" << endl;    }    sockaddr_in Clientaddr;    int duankou;    cout << "请输入端口号:";    cin >> duankou;    Clientaddr.sin_port = htons(duankou);    Clientaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//inet_addr(),参数为一个字符串,将IP字符串的值传递给inet_addr    Clientaddr.sin_family = AF_INET;    if (connect(ClientSocket, (LPSOCKADDR)&Clientaddr, sizeof(Clientaddr))){        cout << "连接失败!" << endl;        closesocket(ClientSocket);        WSACleanup();        return 0;    }    cout << "客户端初始化成功,准备交流" << endl;    while (1)    {        cout << "请输入信息:";        char buff[1000];        cin >> buff;        if (strcmp(buff, "bye") == 0)        {            send(ClientSocket, buff, strlen(buff), 0);            break;        }        cout << "发送信息:" << buff << endl;        if (send(ClientSocket, buff, strlen(buff), 0) < 0){            cout << "发送失败!" << endl;        }        else{            cout << "发送成功!" << endl;        }        cout << "准备接受信息" << endl;        char  RecMessage[1024];        int MessageLen = recv(ClientSocket, RecMessage, sizeof(RecMessage), 0);        //cout << MessageLen << endl;测试用        if (MessageLen < 0){            cout << "接受失败" << endl;        }        else{            RecMessage[MessageLen] = 0X00;            if (strcmp(RecMessage, "bye") == 0)            {                cout << "服务器已关闭,交流结束" << endl;                system("pause");                break;            }            cout << "接受成功,信息为:" << RecMessage << endl;        }    }    closesocket(ClientSocket);    WSACleanup();    return 0;}
0 0
原创粉丝点击