动手学习TCP: 环境搭建

前一段时间通过Wireshark抓包,定位了一个客户端和服务器之间数据传输的问题。最近就抽空看了看《TCP/IP详解 卷1》中关于TCP的部分,书中用了很多例子展示了TCP/IP协议中的一些基本概念。

所以,也准备自己动手,通过一些简单的实验来进一步了解一下TCP中的一些基本概念。

环境搭建和配置

在开始进行实验之前,首先看看实验环境的搭建:

  1. Wireshark:用来抓取网络上的包,可以清楚的看到TCP/IP协议层,以及每层的详细信息,通过此处下载
  2. 一台虚拟机:如果客户端和服务端都在本机,那么数据通信是不经过网卡的,所以Wireshark就抓不到任何数据包。方便的办法就是本机安装一个虚拟机,通过本机和虚拟机通信进行实验。我使用的是VirtualBox+winXP.
  3. Pcap.Net:是一个WinPcap的.NET wrapper,基于这个库,我们就可以很方便的通过C#代码来实现下面功能(通过此处下载):
    1. 获取网络设备
    2. 接收、解析数据包
    3. 创建、发送数据包

在建立好实验环境之后,还需要进行一些简单的配置,保证宿主机和虚拟机之间的网络是畅通的。

将虚拟机网络设置为"Host-only Adapter"模式。

虚拟机网络设置好之后,就可以配置本机和虚拟机IP地址了,然后保证宿主机可以ping通虚拟机。

环境验证

通过上面的步骤,简单的实验环境就建立完成了,下面就要来实现客户端和服务端了,试试实验环境是否能够正常工作。

服务端

首先,将虚拟机(192.168.56.102)作为服务端,运行下面一段代码创建一个简单的socket server,服务端绑定192.168.56.102:8081:

import sys
from socket import *

HOST = "192.168.56.102"
PORT = 8081
BUFSIZ = 1024
ADDR = (HOST, PORT)

server = socket(AF_INET, SOCK_STREAM)
print "Socket created"
try:
    server.bind(ADDR)
except error, msg:
    print ‘Bind failed. Error Code : ‘ + str(msg[0]) + ‘ Message ‘ + msg[1]
    sys.exit()

server.listen(10)
print ‘Socket now listening‘

while True:
    conn, addr = server.accept()
    try:
        data = conn.recv(100)
        if data:
            print data
    except Exception, e:
     print e
    conn.close()

客户端

客户端的实现在本机(192.168.56.101),使用一段基于Pcap.Net的代码向服务器发送一个[SYN]包(TCP连接建立需要进行三次握手,[SYN]包就是第一个握手包),来请求建立TCP连接。

在客户端代码中,通过Pcap.Net实现了两个工具函数,一个用来获取本机网卡设备列表,一个用在构造不同类型的TPC包。

获取本机网卡设备列表代码:

public static PacketDevice GetNICDevice()
{
    // Retrieve the device list from the local machine
    IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;

    if (allDevices.Count == 0)
    {
        Console.WriteLine("No interfaces found! Make sure WinPcap is installed.");
        return null;
    }

    // Print the device list
    for (int i = 0; i != allDevices.Count; ++i)
    {
        LivePacketDevice device = allDevices[i];
        Console.Write((i + 1) + ". " + device.Name);
        if (device.Description != null)
            Console.WriteLine(" (" + device.Description + ")");
        else
            Console.WriteLine(" (No description available)");
    }

    int deviceIndex = 0;
    do
    {
        Console.WriteLine("Enter the interface number (1-" + allDevices.Count + "):");
        string deviceIndexString = Console.ReadLine();
        if (!int.TryParse(deviceIndexString, out deviceIndex) ||
            deviceIndex < 1 || deviceIndex > allDevices.Count)
        {
            deviceIndex = 0;
        }
    } while (deviceIndex == 0);

    return allDevices[deviceIndex - 1];
}

另一段重要的代码就是构造TCP包的代码,根据OSI七层模型,下面代码中分别创建了链路层、网络层和传输层的部分,然后生成一个数据包:

public static Packet BuildTcpPacket(EndPointInfo endPointInfo, TcpControlBits tcpControlBits, List<TcpOption> tcpOptionList = null)
{
    EthernetLayer ethernetLayer =
        new EthernetLayer
        {
            Source = new MacAddress(endPointInfo.SourceMac),
            Destination = new MacAddress(endPointInfo.DestinationMac),
            EtherType = EthernetType.None, // Will be filled automatically.
        };

    IpV4Layer ipV4Layer =
        new IpV4Layer
        {
            Source = new IpV4Address(endPointInfo.SourceIp),
            CurrentDestination = new IpV4Address(endPointInfo.DestinationIp),
            Fragmentation = IpV4Fragmentation.None,
            HeaderChecksum = null, // Will be filled automatically.
            Identification = 123,
            Options = IpV4Options.None,
            Protocol = null, // Will be filled automatically.
            Ttl = 10,
            TypeOfService = 0,
        };

    TcpLayer tcpLayer =
        new TcpLayer
        {
            SourcePort = endPointInfo.SourcePort,
            DestinationPort = endPointInfo.DestinationPort,
            Checksum = null, // Will be filled automatically.
            SequenceNumber = seqNum,
            AcknowledgmentNumber = ackNum,
            ControlBits = tcpControlBits,
            Window = windowSize,
            UrgentPointer = 0,
            Options = (tcpOptionList == null) ? TcpOptions.None : new TcpOptions(tcpOptionList),
        };

    PacketBuilder builder = new PacketBuilder(ethernetLayer, ipV4Layer, tcpLayer);

    return builder.Build(DateTime.Now);
}

主程序中,首先配置了客户端和服务器的端口、IP/MAC地址信息,然后通过前面两个工具函数构造一个TCP连接建立请求包([SYN]包),并通过"VirtualBox Host-Only Network"网卡发送给服务端。

static void Main(string[] args)
{
    // Take the selected adapter
    PacketDevice selectedDevice = Utils.GetNICDevice();

    // Open the output device
    using (PacketCommunicator communicator = selectedDevice.Open(System.Int32.MaxValue, // name of the device
                                                                 PacketDeviceOpenAttributes.Promiscuous, // promiscuous mode
                                                                 1)) // read timeout
    {
        EndPointInfo endPointInfo = new EndPointInfo();
        endPointInfo.SourceMac = "08:00:27:00:C0:D5";
        endPointInfo.DestinationMac = "08:00:27:70:A6:AE";
        endPointInfo.SourceIp = "192.168.56.101";
        endPointInfo.DestinationIp = "192.168.56.102";
        endPointInfo.SourcePort = 3330;
        endPointInfo.DestinationPort = 8081;

        using (BerkeleyPacketFilter filter = communicator.CreateFilter("tcp port " + endPointInfo.DestinationPort))
        {
            // Set the filter
            communicator.SetFilter(filter);
        }

        communicator.SendPacket(Utils.BuildTcpPacket(endPointInfo, TcpControlBits.Synchronize, null));
        PacketHandler(communicator, endPointInfo);
    }

    Console.WriteLine("Press Enter to Quit!");
    Console.ReadLine();

}

private static void PacketHandler(PacketCommunicator communicator, EndPointInfo endPointInfo)
{
    Packet packet = null;

    do
    {
        PacketCommunicatorReceiveResult result = communicator.ReceivePacket(out packet);

        switch (result)
        {
            case PacketCommunicatorReceiveResult.Timeout:
                // Timeout elapsed
                continue;
            case PacketCommunicatorReceiveResult.Ok:
                Utils.PacketInfoPrinter(packet);
                break;
            default:
                throw new InvalidOperationException("The result " + result + " should never be reached here");
        }
    } while (true);
}

运行代码

代码完成了,下面看看运行效果,为了直观的看到数据包的传输,这是就可以打开Wireshark了。

为了避免抓到不相关的数据包,可以设置Wireshark中的filter,然后开始抓取。

下面运行代码,并选择正确的网卡。通过console和Wireshark的输出可以看到,我们成功的生产了一个[SYN]包并发送到了服务器。

根据TCP连接建立过程可以知道,客户端发送[SYN]包后,期待从服务器得到一个[SYN, ACK]包。

到这里,说明前面搭建的环境,以及客户端和服务端的代码都是可以正常工作的了。

谁的[RST]包

从上面的结果中看到,客户端在收到[SYN, ACK]包之后,发送了一个[RST]包重置这条TCP连接。

仔细查看了代码发现,客户端的代码中并没有发送[RST]包。那么这个[RST]包是哪里来的呢?

操作系统中有协议栈的概念,所以来自应用层的数据,都会一层层的经过操作系统协议栈处理,然后通过网卡发送出去。

当客户端网卡收到[SYN, ACK]包后,这个包会被我们的Pcap.Net程序捕获,也会被传送给客户端操作系统。由于通过Pcap.Net构造的[SYN]包是没有经过操作系统协议栈的,所以操作系统会认为[SYN, ACK]包是一个无效TCP包,并通过[RST]包重置TCP连接。

到这里,多余[RST]包就可以解释了。

避免[RST]包

为了避免操作系统协议栈对Pcap.Net程序的影响,通过IP安全策略(通过Run->"secpol.msc"打开设置)的设置,可以避免操作系统从本机(192.168.56.101)向虚拟机(192.168.56.102)发送数据包。

设置完成后,再次运行程序,这是程序就正常了。

由于客户端没有发送[ACK]包来确认来自服务端的[SYN, ACK]包,根据TCP工作原理,服务端会进行重传。

总结

本文中介绍了TCP实验环境的搭建,通过Pcap.Net创建了一个客户端,可以构造不同类型的TCP数据包,并通过特定网卡向服务器发送。

后面继续基于这个环境来看看TCP的一些基本概念,TCP连接、状态变迁等等。

时间: 2024-10-06 22:59:03

动手学习TCP: 环境搭建的相关文章

【golang学习】环境搭建

近期项目的需要开始了学习golang的旅程,在此将学习的过程做以记录,方便查看,也希望帮助到大家. 由于我使用的是64位linux环境(redhat 5.2),所以这里就介绍64位的安装方式,其他类似: 1. 下载安装包 自从2014年5月谷歌在国内被封杀之后,我们下载go安装包也得翻墙了,但是为了方便我上传了一个64位linux的包在(地址),你可以直接下载使用. 2.安装 下载完上传到你的linux系统中,比如/opt/go1.1.1.linux-amd64.tar.gz,这是就开始安装了,

Angularjs学习---angularjs环境搭建,ubuntu 12.04下安装nodejs、npm和karma

1.下载angularjs 进入其官网下载:https://angularjs.org/?,建议下载最新版的:https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.7/angular.js 所有版本:https://code.angularjs.org/ 2.示例1 HelloWorld ! 新建一个helloworld.html <!doctype html> <html ng-app> <head> &

Android学习之环境搭建

Android学习之环境搭建 园里有很多关于Android开发的环境搭建的资料,本人是安卓开发初学者,这里记录一下个人搭建Android环境的总结. 1.准备Eclipse IDE for Java Developers 网上可以下载的版本还是比较多的,本人选择了eclipse-java-luna-SR2-win32. 网址:http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/lunasr2 Note:官网点

CouchBase学习之环境搭建以及HelloWorld

一.引言 NoSQL(Not Only SQL),火了很久了,一直没空研究.最近手上一个项目对Cache有一定的要求,借此机会对NoSQL入门一下.支持NoSQL的数据库系统有很多,  比如Redis,MongoDB等.每种系统都有自己的优缺点,根据实际情况我们选用了CouchBase作为切入点学习.这里有几种数据库的比较:http://www.csdn.net/article/2013-04-15/2814886-nosql-benchmark CouchBase,是MemBase与couch

JavaWeb学习之环境搭建

1. HTML(Hyper Text Markup Language) , 超文本标记语言. HTML文件的后缀名一般是: .htm , .html 表单(form): 浏览器内核: WebKit , trident Javascript:alert(‘?’);  --->java脚本语言 我的第一个html程序: 代码如下: 1 <html> 2 <head> 3 <title> 龚细军的网站</title> 4 </head> 5 6

hbase_学习_01_HBase环境搭建(单机)

一.前言 本文承接上一篇:hadoop_学习_02_Hadoop环境搭建(单机)  ,主要是搭建HBase的单机环境 二.环境准备 在虚拟机中: 操作系统 : linux CentOS 6.8 jdk:1.7 hadoop:2.8.3 hbase:1.4.2 三.hbase下载 1.hbase下载地址 http://mirror.bit.edu.cn/apache/hbase/1.4.2/hbase-1.4.2-bin.tar.gz 2.下载hbase wget http://mirror.bi

深度学习开发环境搭建

深度学习开发环境搭建 https://www.cnblogs.com/ai-developer/p/10022115.html 工欲善其事,必先利其器.首先我们需要花费一些时间来搭建开发环境. 1.安装python.python是人工智能开发首选语言. 2.安装virtualenv.virtualenv可以为一个python应用创建一套隔离的运行环境,避免不同版本的python或第三方库互相影响.类似的虚拟环境还有anaconda,anaconda自带常用库,因此安装包有几百兆,与anacond

JAVA基础学习——1.环境搭建 (系统Win10,64bit)

大学里虽然老师教过JAVA,但我没学.后来工作了,断断续续的也碰到了JAVA的项目,都是拉过来就干的节奏.也没有好好系统的学习一下. 从今天开始系统学习整理一下JAVA,以后再碰到JAVA项目的时候,心里也踏实点.咱也是正儿八经学过的人了,发发博客就当做个笔记,有不足的地方,大家多多指点,先在这里谢谢大家. 1.  环境搭建第一步,安装JDK. JDK 是Java开发工具包 (Java Development Kit ) 的缩写. JDK 是一系列工具的集合,这些工具是编译Java源码.运行Ja

LINUX内核学习起步:学习开发环境搭建

我的环境配置如下: 电脑上安装虚拟机VMware10.0.1,虚拟机上安装了CentOS6.5,注意虚拟机VMware上面网络的配置选择为桥接模式 电脑windows上安装secureCRT6,事先登录虚拟机中的centos,查询机器IP地址,然后通过secureCRT登录到centos中. 这里为什么要通过secureCRT登录centos呢?因为在centos中直接运行终端或者alt+F1~5方式进入命令行模式,显示的界面太小,不方便操作. 如果你经常要在linux下编程或者学习,建议还是通