Windows Practice_Socket

TCP/IP

在世界各地,各种各样的电脑运行着各自不同的操作系统,从而为大家服务,这些电脑在表达同一种信息的时候使用的方法千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样。计算机使用者意识到,计算机只是单兵作战并不能发挥太大的作用。也只有把它们联合在一起,电脑才会发挥出它最大的潜力。于是人们就想方设法的用电线或者光纤将电脑连接到了一起。
但是简单的连到一起是远远不够的,就好像语言不通的两个人互相见了面,完全不能交流信息。因而他们需要定义一些共通的东西来进行交流,TCP/IP就是应运而生。TCP/IP不是一个协议,而是一个协议族的统称。里面包括了IP协议、IMCP协议TCP协议以及我们更加熟悉的http、ftp、pop3协议等等。电脑有了这些协议,就好像学会了外语一样,就可以和其他的计算机终端做自由的交流了。

OSI参考模型

这里写图片描述

上图中给出的是OSI七层理想网络模型,但是5、6层对应会话层和表示层在实际运用中并不存在。
而物理层一般的我们也不会太关心,所以就会导致TCP/IP4层模型(不包括物理层)和TCP/IP5层模型(包括物理层)的说法,这些说法都没有错。

TCP/IP协议

TCP/IP协议族按照层次由上到下。层层包装。最上面的就是应用层了,这里面有http、ftp等我们熟悉的协议。
而第二层则是传输层,著名的TCP和UDP协议就是在这个层次。
第三层是网络层,IP协议就在这里,它负责对数据加上IP地址和其它的数据以确定传输的目标。
第四层是数据链路层,这个层次为待传输的数据加入一个以太网协议头并进行CRC编码,为最后的数据传输做准备。再往下则是硬件层了,负责网络的传输,这个层次的定义包括网线的制式,网卡的定义等等(这些我们就不用关心了因为我们也不做网卡),所以有些书并不把这个层次放在TCP/IP协议族里面,因为它几乎和TCP/IP协议族的编写者没有任何关系。发送协议的主机自上而下将数据按照协议封装,而接收数据的主机则按照协议从得到的数据包解开,最后拿到需要的数据。这种结构非常有栈的味道,所以某些文章也把TCP/IP协议族成为TCP/IP协议栈。
这里写图片描述

这里写图片描述

IP地址

网络上每一个节点都必须有一个独立的Internet地址(也叫做IP地址)。现在,通常使用的IP地址是一个32bit的数字,也就是我们常说的IPV4标准,这32bit的数字分成四组,也就是常见的255.255.255.255的样式。IPV4标准上,地址被分为五类,我们常用的是B类地址。具体的分类请参考其它文档。需要注意的是IP地址是网络号+主机号的组合,这非常重要。

域名系统

域名系统是一个分布的数据库,它提供将主机名(就是网址了)转换成IP地址的服务。

端口号

注意,这个号码是用在TCP,UDP上的一个逻辑号码,并不是一个硬件端口,我们平时说的某某端口封掉了,也只是在IP层次把带有这个号码的IP包给过滤掉了而已。

应用编程接口

现在常用的编程接口有SOCKET,还有一种现在已经淘汰了,它是TLI接口。

TCP通讯简单示例

下面使用TCP协议实现的服务端和客户端的简单通讯,当服务端连接到10客户端的时候,自动退出连接,同时也给所有的客户端发送一条退出命令,确保它们都能退出。如果服务端已经退出,还有客户端没有退出,那么客户端就会出现错误。
服务端

// EasyWinSocketDemoServer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>

#pragma comment(lib, "ws2_32.lib")


const int MAX_CLIENT_NUMBER = 10;

int main()
{
    WSAData wsaData;
    int iErrorNumber = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iErrorNumber != 0)
    {
        printf("WSAStartup failed with error: %d\n", iErrorNumber);
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
    {
        printf("The Winsock 2.2 dll was found okay\n");
    }


    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    sockaddr_in sockAddr;
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(10086);
    sockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
    sockAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
    bind(s, (sockaddr *)&sockAddr, sizeof(sockaddr_in));

    listen(s, 1);


    SOCKET socketCilentArray[MAX_CLIENT_NUMBER] = { INVALID_SOCKET };
    int iNum = 0;
    while (true)
    {
        socketCilentArray[iNum++] = accept(s, nullptr, nullptr);
        char strBuf[MAXBYTE] = { 0 };
        printf("client %d connected!    %s\r\n", socketCilentArray[iNum - 1], strBuf);
        if (iNum == MAX_CLIENT_NUMBER)
        {
            break;
        }
    }

    for (int i = 0; i<MAX_CLIENT_NUMBER; ++i)
    {
        send(socketCilentArray[i], "Exit", 5, 0);
    }

    for (int i=0; i<MAX_CLIENT_NUMBER; ++i)
    {
        closesocket(socketCilentArray[i]);
    }
    Sleep(1000);
    closesocket(s);

    WSACleanup();

    system("pause");
    return 0;
}

客户端

// EasyWinSocketDemoClient.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>

#pragma comment(lib, "ws2_32.lib")


int main()
{
    WSAData wsaData;
    int iErrorNumber = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iErrorNumber != 0)
    {
        printf("WSAStartup failed with error: %d\n", iErrorNumber);
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
    {
        printf("The Winsock 2.2 dll was found okay\n");
    }

    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET)
    {
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }


    sockaddr_in sockAddr;
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(10086);
    sockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
    sockAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;

    int iResult = connect(s, (sockaddr *)&sockAddr, sizeof(sockAddr));
    if (iResult == SOCKET_ERROR)
    {
        wprintf(L"connect failed with error: %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    iResult = send(s, "Hello", sizeof("Hello") + 1, 0);
    if (iResult == SOCKET_ERROR)
    {
        wprintf(L"send failed with error: %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("Bytes Sent: %d\n", iResult);


    char buf[MAXBYTE] = { 0 };

    while (true)
    {
        recv(s, buf, MAXBYTE, 0);
        printf("Recv:%s\r\n", buf);
        if (strcmp(buf, "Exit") == 0)
            break;
    }

    closesocket(s);
    WSACleanup();

    system("pause");
    return 0;
}

这里需要注意的是:

  • 头文件的包含顺序,WinSock2.h一定要在Windows.h之前,否则编译就会出错;
  • lib库的包函数,#pragma comment(lib, “ws2_32.lib”);
  • 服务端需要等到所有客户端退出之后才能退出,否则客户端就会出问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值