Fork me on GitHub

2015年4月

LINQ中的Distinct

一、从去重说起

去重一直是数据处理中的一个重要操作,保证处理的数据中没有冗余,而在编写代码的时候更是经常需要剔除重复的数据避免重复计算。LINQ中的Distinct方法可以很好的处理基本数据类型的去重操作,如下所示:

// int类型
List<int> ints = new List<int> { 
    4, 5, 2, 1, 4, 6, 3, 2, 1, 3 
};
ints.Distinct().ToList().ForEach(x => Console.WriteLine(x));

// string类型
List<string> strings = new List<string> { 
    "Tom", "John", "Lily", "Tom", "Jess", "John" 
};
strings.Distinct().ToList().ForEach(x => Console.WriteLine(x));

但是在使用Distinct方法处理对象类型的数据时却没有这么好的体验了,假设我们有下面这个Person类,和类似的去重代码:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return string.Format("Id: {0}, Name: {1}, Age: {2}", 
            this.Id, this.Name, this.Age);
    }
}

List<Person> personList = new List<Person> 
{
    new Person { Id = 1, Name = "Zhangsan", Age = 23 },
    new Person { Id = 2, Name = "Lisi", Age = 22 },
    new Person { Id = 3, Name = "Wangwu", Age = 25 },
    new Person { Id = 2, Name = "Lisi", Age = 22 },
    new Person { Id = 1, Name = "Zhangsan", Age = 23 }
};
var dist = personList.Distinct().ToList();
dist.ForEach(x => Console.WriteLine(x));

得到的结果如下图,可以看到直接简单的使用Distinct来对对象列表进行去重,得到的结果和没去重的结果是一样的!原因在于Distinct方法默认的实现是根据对象的引用来进行的,而上面new了5个Person对象,是5个不同的引用。

distinct

我们查看Distinct方法的定义,发现该方法还可以带一个IEqualityComparer<TSource>类型的参数:

namespace System.Linq
{
    public static class Enumerable
    {
        // ...
        public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source);
        public static IEnumerable<TSource> Distinct<TSource>(
                this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
        // ...
    }
}

下面我们通过实现IEqualityComparer接口来实现对象列表的去重。

二、实现IEqualityComparer

IEqualityComparer接口的定义如下:

namespace System.Collections.Generic
{
    public interface IEqualityComparer<in T>
    {
        bool Equals(T x, T y);
        int GetHashCode(T obj);
    }
}

可以看到通过实现IEqualityComparer接口封装了EqualsGetHashCode这两个函数的实现,而这两个正是用于判断对象是否相等的重要函数。因为这里我们是想通过Id字段来去重,所以我们新建一个PersonIdComparer类:

public class PersonIdComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        if (x == null)
            return y == null;
        return x.Id == y.Id;
    }

    public int GetHashCode(Person obj)
    {
        if (obj == null)
            return 0;
        return obj.Id.GetHashCode();
    }
}

使用Distinct去重的时候只需要这样就可以了:

var dist1 = personList.Distinct(new PersonIdComparer()).ToList();

看起来很简单的一个去重操作,却要每次都要新建一个类,然后实现它的两个方法。而且在需求变更的同时,这个类还不能很好的适应变化,譬如现在我们需要根据Name字段去重,那么我们又需要新建一个PersonNameComparer类,当我们需要根据Age字段去重的时候,又需要新建一个PersonAgeComparer类,如此反复,非常繁琐,为什么我们不能写一个通用的类直接根据某个字段来去重呢? LoveJenny在《Linq的Distinct太不给力了》这篇博文中使用泛型、反射和表达式树的方法实现了一个PropertyComparer类,这个类可以很好的适应上面的变化。另外这篇文章也有类似的实现。

三、简化IEqualityComparer的实现

LoveJenny的博文给出的解决方法虽然满足了适应变化的能力,但在使用上仍然感觉不是很便捷,为什么我们不能像LINQ中的其他方法如OrderBy(x => x.Id)这样使用lambda表达式来去重呢?鹤冲天在他的两篇博文《c# 扩展方法奇思妙用基础篇八:Distinct 扩展》《何止 Linq 的 Distinct 不给力》中介绍了一种更简单通用的实现IEqualityComparer接口的方法,并通过结合C#的扩展方法使得LINQ中的Distinct使用起来非常便捷。下面直接上代码:

public static class Equality<T>
{
    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector)
    {
        return new CommonEqualityComparer<V>(keySelector);
    }
    public static IEqualityComparer<T> CreateComparer<V>(
            Func<T, V> keySelector, IEqualityComparer<V> comparer)
    {
        return new CommonEqualityComparer<V>(keySelector, comparer);
    }

    class CommonEqualityComparer<V> : IEqualityComparer<T>
    {
        private Func<T, V> keySelector;
        private IEqualityComparer<V> comparer;

        public CommonEqualityComparer(
                Func<T, V> keySelector, IEqualityComparer<V> comparer)
        {
            this.keySelector = keySelector;
            this.comparer = comparer;
        }
        public CommonEqualityComparer(Func<T, V> keySelector)
            : this(keySelector, EqualityComparer<V>.Default)
        { }

        public bool Equals(T x, T y)
        {
            return comparer.Equals(keySelector(x), keySelector(y));
        }
        public int GetHashCode(T obj)
        {
            return comparer.GetHashCode(keySelector(obj));
        }
    }
}

public static class DistinctExtensions
{
    public static IEnumerable<T> DistinctBy<T, V>(
            this IEnumerable<T> source, Func<T, V> keySelector)
    {
        return source.Distinct(Equality<T>.CreateComparer(keySelector));
    }
    public static IEnumerable<T> DistinctBy<T, V>(
            this IEnumerable<T> source, Func<T, V> keySelector, IEqualityComparer<V> comparer)
    {
        return source.Distinct(Equality<T>.CreateComparer(keySelector, comparer));
    }
}

原文中的方法名保持了LINQ中的Distinct不变,我这里改成了DistinctBy,这样和OrderByGroupBy相一致,而且在使用的时候personList.DistinctBy(x => x.Id)在语义上感觉更清晰。而且这里的Equality静态类,可以为我们很方便的创建IEqualityComparer接口,直接通过lambda表达式就可以而不需要再另外定义一个类了,譬如:var idComparer = Equality<Person>.CreateComparer(x => x.Id)

四、第三方库中的解决方法

4.1 morelinq

morelinq是一个关于linq的开源项目,添加了一些很实用的扩展方法,像ExcludeMinByMaxByTakeEvery等等,当然还有我们这里讲的DistinctBy,用法和上面的一样:personList.DistinctBy(x => x.Id)就可以了。我们这里主要关心它的实现方法,因为它并没有通过实现IEqualityComparer接口来,而是巧妙的利用了C#中的HashSet类型和yield关键字,实现了去重的目的。我将无关部分剔除,关键代码如下:

static partial class MoreEnumerable
{   
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
            this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        return source.DistinctBy(keySelector, null);
    }

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
            this IEnumerable<TSource> source,
            Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        return DistinctByImpl(source, keySelector, comparer);
    }

    private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>(
            IEnumerable<TSource> source,
            Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        var knownKeys = new HashSet<TKey>(comparer);
        foreach (var element in source)
        {
            if (knownKeys.Add(keySelector(element)))
            {
                yield return element;
            }
        }
    }
}

具体的实现在DistinctByImpl这个方法里,首先新建一个HashSet,然后通过Add方法将元素添加到HashSet中,如果元素已存在Add方法返回false,则会继续插入下一个元素直到下一个元素插入成功,然后通过yield返回。需要注意的是HashSet接受一个IEqualityComparer类型的参数,如果HashSet的key是个对象而不是简单类型,则需要和我们上面一样定义一个类实现IEqualityComparer接口了。

4.2 Miscellaneous Utility Library

MiscUtil是一个C#的工具包大杂烩,它有各种辅助方法可以方便我们的开发工作。其中有一个ProjectionEqualityComparer类是和上面介绍的鹤冲天的Equality<T>类几乎完全一样,具体实现可以去看下它的源代码。唯一的区别在于它提供了两个ProjectionEqualityComparer类的实现,一个是ProjectionEqualityComparer,另一个是ProjectionEqualityComparer<TSource>。如下:

public static class ProjectionEqualityComparer
{
    public static ProjectionEqualityComparer<TSource, TKey> 
            Create<TSource, TKey>(Func<TSource, TKey> projection)
    {
        return new ProjectionEqualityComparer<TSource, TKey>(projection);
    }
}

public static class ProjectionEqualityComparer<TSource>
{    
    public static ProjectionEqualityComparer<TSource, TKey> 
            Create<TKey>(Func<TSource, TKey> projection)
    {
        return new ProjectionEqualityComparer<TSource, TKey>(projection);
    }
}

这样当我们需要实现IEqualityComparer接口时可以有更多的选择,如下所示。我们注意到CreateComparer<int>可以简写成CreateComparer,所以ProjectionEqualityComparer<Person>.CreateComparer(x => x.Id)这种写法可能要更实用一点。

var c1 = ProjectionEqualityComparer.CreateComparer<Person, int>(x => x.Id);
var c2 = ProjectionEqualityComparer<Person>.CreateComparer<int>(x => x.Id);
var c3 = ProjectionEqualityComparer<Person>.CreateComparer(x => x.Id);

4.3 AnonymousComparer

最后我们再来看下codeplex上的一个项目:AnonymousComparer,它可以通过lambda表达式直接创建匿名类实现IComparer<T>IEqualityComparer<T>接口。所以通过AnonymousComparer不仅简化了IEqualityComparer接口的实现,还简化了IComparer接口,IComparer接口在LINQ中的很多方法如OrderByGroupByContains等中有着广泛的应用。AnonymousComparer实现IEqualityComparer的方式大同小异,但是有一点不同的是,它提供了一种Full IEqualtyComparer<T> overload,这样我们可以在代码里完全通过lambda实现IEqualtyComparer的两个方法:EqualsGetHashCode。而上面所介绍的其他方法中都是将这两个方法封装起来的。

public static IEqualityComparer<T> Create<T, TKey>(Func<T, TKey> compareKeySelector)
{
    if (compareKeySelector == null) throw new ArgumentNullException("compareKeySelector");

    return new EqualityComparer<T>(
        (x, y) =>
        {
            if (object.ReferenceEquals(x, y)) return true;
            if (x == null || y == null) return false;
            return compareKeySelector(x).Equals(compareKeySelector(y));
        },
        obj =>
        {
            if (obj == null) return 0;
            return compareKeySelector(obj).GetHashCode();
        });
}

public static IEqualityComparer<T> Create<T>(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
    if (equals == null) throw new ArgumentNullException("equals");
    if (getHashCode == null) throw new ArgumentNullException("getHashCode");

    return new EqualityComparer<T>(equals, getHashCode);
}

private class EqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> equals;
    private readonly Func<T, int> getHashCode;

    public EqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
    {
        this.equals = equals;
        this.getHashCode = getHashCode;
    }

    public bool Equals(T x, T y)
    {
        return equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        return getHashCode(obj);
    }
}

还是用Person列表根据Id去重的例子,这个Comparer可以写成这样(写起来可能会没有上面的方便快捷,但是可以实现更强大的定制功能,譬如要根据多列来去重或根据某种算法来去重而不是简单的根据单列去重):

var idComparer = AnonymousComparer.Create<Person>(
    (x, y) => x.Id == y.Id,         // Equals
    obj => obj.Id.GetHashCode()     // GetHashCode
);

五、最简单的解决方法

上面说了这么多,大多是通过实现IEqualityComparer接口的,也有根据HashSetyield实现的。但是其实通过LINQ自带的GroupBy方法也可以实现去重的目的,像下面这样:

var dist = personList.GroupBy(x => x.Id).Select(x => x.First());

尽管在性能上有一定的折扣,在可读性方面也不容易让人理解,但这确实应该算是最简单的做法。这让我想起了SQL中的去重,SQL语言中DISTINCT是根据所有字段来去重的,如果需要根据某一列或几列来去重也会使用GROUP BY,类似于:

SELECT * FROM Person GROUP BY Id

从这一点看上去,LINQ真的和SQL有着惊人的相似。

参考

  1. Distinct() with lambda?
  2. Can I specify my explicit type comparator inline?
  3. Distinct list of objects based on an arbitrary key in LINQ
  4. Linq的Distinct太不给力了
  5. c# 扩展方法奇思妙用基础篇八:Distinct 扩展
  6. 何止 Linq 的 Distinct 不给力
  7. morelinq - Extensions to LINQ to Objects
  8. Miscellaneous Utility Library
  9. AnonymousComparer - lambda compare selector for Linq
  10. 快速创建 IEqualityComparer 和 IComparer 的实例
  11. A Generic IEqualityComparer for Linq Distinct()
扫描二维码,在手机上阅读!

Win7下VMware的NAT网络模式不能正常工作

今天在Windows 7下实验VMware的NAT网络模式时遇到了障碍,Guest-OS不仅无法访问Internet,而且连Host-OS也ping不通。有时候仿佛能ping通Host-OS,但是却返回大量的(DUP!)包,如下图所示:

ping-dup

根据这个提示,在Google上搜索“ping DUP!”得到大量的结果,有的说禁用VMnet8网卡然后重启即可解决,有的说可能是局域网上有IP冲突,有的说可能ping的是广播地址,还有的说局域网有回路等等,除了第一个禁用/启用VMnet8具有可操作性之外,其他都是些莫名其妙的不知道具体如何解决的答案。但是,遗憾的是禁用/启用VMnet8后问题依旧。 在Guest-OS上执行iptables -L并未发现有设置防火墙规则,并且在Host-OS上已经禁用防火墙了,所以应该可以排除防火墙的原因。然后在vmware社区上看到了这篇帖子,有个人使用tcpdump来定位问题,实验后仍然没有发现什么异常,无果而终。
在ping DUP!这个问题上花费了大约整整一天的时间,最终意识到ping DUP!可能并不是虚拟机无法访问网络的症结所在,通过网络上给出的答案可以发现ping DUP!问题更多的原因可能是局域网内网络配置错误或者网卡异常导致的,而且出现这种现象的原因太多,很难入手。于是重新审视这个问题,把问题的所有现象一一列出来:

  • Host-OS IP: 192.168.0.107
  • Host-OS Gateway: 192.168.0.1
  • Guest-OS IP: 192.168.220.128
  • Guest-OS Gateway: 192.168.220.2
  • Guest-OS ping自身192.168.220.128正常,ping网关192.168.220.2正常
  • Guest-OS ping Host-OS 192.168.0.107返回大量DUP包
  • Guest-OS ping Host-OS的网关 192.168.0.1返回大量DUP包
  • Host-OS ping Guest-OS 192.168.220.128正常
  • 打开网络和共享中心,VMnet1和VMnet8属于未识别的网络,而且公共网络不能修改

最后两点现象很快就让人感到异常,特别是未识别网络这条,如下图所示:

unidentified-networks

于是Google关键字“VMnet8 未识别的网络”,根据第一条记录的结果修改注册表中的*NdisDeviceType=1然后重启VMnet8网卡,问题竟然就解决了!修改注册表参见下图:

ndis-device-type

原来是Windows 7网络访问的限制,也可以说是VMware的一个Bug,VMware在新建网卡时设备类型*NdisDeviceType使用了默认值0,0意味着网卡是标准的真实网卡,VMware应该将其设置为NDIS_DEVICE_TYPE_ENDPOINT(1)才对,这样Windows会知道这个网卡是个虚拟网卡并没有和真实的外部网络连接,在“网络和共享中心”中忽略这些网卡,不会受网络共享设置的限制。

通过解决这个问题,再次意识到真的“不能在一颗树上吊死”。解决问题往往有两种方式:

  • 第一种方式是:首先找出问题的一个异常现象,然后通过各种手段解决这个异常,最终会有两种结果:一是异常解决并且问题解决,二是异常解决问题并未解决。如果是情况一,则问题解决,结束。如果是情况二,则排除了这个异常和问题的联系,这时需要寻找另一个异常现象,重复上述步骤继续解决
  • 第二种方式是:首先列出所有发现的异常现象,然后针对每个异常现象快速确定和问题有联系的可能性(譬如利用搜索引擎),然后针对每个可能性再继续对问题进行分析

可以发现两种解决问题的方式正如图的两种遍历算法:深度优先算法和广度优先算法,方式一为深度优先,方式二为广度优先。深度优先的缺点是可能会导致无止境的往下延伸,越往下延伸解决方案离最初的问题可能就越远,直到排除所有可能性后才能解决最初的问题,在遇到奇葩的问题时,效率可能会非常低下。我们将每一次延伸称之为问题的解决路径,如果你的问题的解决路径很短,譬如我们这里的这个问题,根据现象“VMnet8 未识别网络”就可以直接解决问题,而根本不需要在ping DUP!问题上耗费无数的时间来研究。整个问题的解决示意图如下:

solve

可见,如果根本不能确定问题原因时,通过广度遍历的方式,加上问题之间的联系,可能会更好更快的解决问题。所以,在面临问题时不要沉陷在自己的某一个想法中,可以从多个角度去看待问题。

参考

  1. VMware Workstation环境中Linux ping返回出现大量“DUP!”的解决方法
  2. ping DUP! 错误
  3. Duplicate packets with ping on guest OS(Linux)
  4. VMware Network Adapter VMnet1和VMnet8未识别的网络修复
  5. Hide VMWare Virtual Network Interfaces from Windows Firewall and Network and Sharing Center [需翻墙]
扫描二维码,在手机上阅读!

实战VMware的三种网络模式

一、实验目的

最近在使用VMware搭建虚拟网络环境时遇到了很多问题,经过对VMware网络模式的一番学习和实战,总算是对其有了一定的认识。所以决定完成一次比较完整的针对VMware网络配置的实验,并写下这篇博客记录整个实验的过程。在进行虚拟机实验时,我们往往关注下面这三个问题:

  • 虚拟机和主机能不能互相访问
  • 虚拟机能不能访问Internet
  • 外部网络如何访问虚拟机中的服务

我们会通过后面的实验分别利用VMware的不同网络模式来解决这三个问题。VMware提供了三种不同的网络模式,分别是Bridged(桥接模式)、NAT(网络地址转换模式)、Host-only(主机模式)。不同的网络模式适用于不同的网络环境,实现不同的需求,你需要根据实际情况选择合适的模式。 就网络环境来说,我们常见的家庭网络拓扑结构有下面两种:

  • 主机通过拨号直接连接Internet
  • 主机处于局域网环境中,通过路由器拨号连接Internet

如果你是属于第一种网络环境,由于是ISP分配你的公网IP(假设只有一个地址),则不能使用桥接模式,因为桥接模式需要你拥有属于你机器相同网段内的另一个IP地址。这种情况下可以使用NATHost-only。而如果是属于第二种网络环境,则三种模式可以任意选用。 就需求来说,你可能只是想简单配置一个虚拟机来玩玩,也可能是为局域网内的其他机器提供服务,或者是进行一些特殊目的的网络实验,这个就要具体情况具体对待了。

二、实验环境

本次实验软硬件环境如下:

  • 操作系统:Windows 7 旗舰版(32位)
  • CPU:Intel(R) Core(TM) i3
  • 内存:4G
  • 虚拟机:VMware® Workstation 8.0.0 build-471780

网络环境为家庭局域网,由路由器通过PPPOE拨号上网,如下图所示:

network-basic

三、实验步骤

了解了我们的需求和网络环境之后,就开始我们的实验之旅吧。首先是下载并安装VMware Workstation,然后下载虚拟机上需要的操作系统镜像,并安装到虚拟机中。我这里创建了三个虚拟机,分别是Windows XP、Ubuntu和Backtrack操作系统。

3.1 桥接模式

桥接模式是三种模式中最简单的一种,VMware安装的时候默认就使用这种配置方式。在这种模式下,虚拟机相当于局域网中的一台独立机器,和主机处于同一个网段,公用同一个网关。桥接模式使用的是虚拟机的VMnet0网卡,一般情况下,在虚拟机中将网络设置成自动获取IP就能直接联网。示意图如下:

bridge

在桥接模式下,虚拟机和主机可以互相ping通,虚拟机可以访问Internet,虚拟机上的服务也可以通过虚拟机IP地址在本机直接访问,如果有任何问题,可以按下面的步骤检查各个配置:

  1. 检查本地连接的属性里,是否勾选了VMware Bridge Protocol,如果没勾选上,则虚拟机和本机不能互相ping通,如下图:

bridge-protocol

  1. 检查虚拟机的IP地址,看是否和本机处于同一个网段内,如果不是,可以手工设置下IP地址,网关和DNS服务器配置可以参考本机,和本机一样即可
  2. 检查本机防火墙和虚拟机防火墙

3.2 NAT模式

上面也说了,如果你不在局域网内,只有一个IP,那么NAT模式正适合你。当然如果你在局域网内,NAT模式也未尝不可,不过使用NAT模式后,主机就变成了双网卡:本身的网卡连接Internet或连接拨号的路由器,另一个虚拟网卡VMnet8连接由虚拟机组成的一个虚拟网络。从外部网络来看,无法直接访问这个虚拟网络。虚拟网络则通过本机上的NAT虚拟服务器进行转发访问Internet。示意图如下:

nat

NAT模式是让虚拟机实现访问Internet最快的方式,几乎不用任何配置,只要主机能上网,那么虚拟机也就肯定能上网。如果有任何问题,可以通过下面的步骤进行排查:

  1. 检查主机上VMware的NAT服务和DHCP服务是否开启,如下图:

nat-service

  1. 检查虚拟机的IP地址,是否和虚拟机NAT配置的Subnet Address在同一个网段内,选择Edit -> Virtual Network Editor可以看到NAT的配置信息
  2. 检查主机和虚拟机的防火墙设置 默认情况下,NAT配置好之后,主机和虚拟机之间应该可以互相访问,虚拟机也可以借助主机上的NAT访问Internet,但是外部网络无法访问虚拟机上的服务。如果需要让同一个局域网内的其他机器(譬如:192.168.0.100)访问虚拟机上的WEB服务,可以通过NAT的端口转发(Port Forwarding)功能来实现,如下图。具体的配置细节可以看参考链接

nat-port-forwarding

3.3 Host-only模式

Host-only模式和NAT一样,也相当于主机双网卡,网络拓扑和NAT也是一样,只是主机不提供NAT功能了,所以虚拟网络只能和主机访问,不能访问Internet。如果需要一个完全隔离的网络环境,则Host-only最合适不过。Host-only相当于使用双绞线直接连接虚拟机和主机,这是最原始的网络结构,当然也是最灵活的。这种情况下虚拟机就不能访问Internet了吗?局域网下的其他机器就不能访问虚拟机上的服务了吗?当然不是。如果我们自己在主机上搭建起我们自己的NAT服务和DHCP服务,那么Host-only其实和NAT是一样的。从下面的示意图也可以看出,Host-only和NAT的唯一区别就在于,主机上少了NAT这个部分。

host-only

类似于NAT,具体的配置这里略过。下面通过Windows上的ICS服务(Internet Connection Sharing,Internet连接共享)来实现Host-only模式的虚拟机访问Internet。ICS是Windows上的一种共享网络访问的服务,类似于mini版NAT,提供了NAT地址转换和DHCP的功能,但不支持端口转发(Port Forwarding)。 首先在网络连接里找到当前正在使用的连接,选择属性 -> 共享,选中“允许其他网络用户通过此计算机的Internet连接来连接”,然后在网络连接下拉框中选择Host-only对应的虚拟网卡(这里是VMnet1),如下图:

ics

在确定的时候,可能会弹出对话框提示错误:“Internet连接共享访问被启用时,出现了一个错误(null)”,这时去服务中找到Windows Firewall,启动即可。 ICS配置好之后,Host-only就和NAT一样了,在虚拟机中设置自动获取IP或手工设置IP,保证和VMnet1处于同一个网段内,如果一切顺利,就可以在虚拟机中访问Internet了。

四、总结

通过这次的实验,重新学习并巩固了计算机网络的相关知识。特别是NATPort Forwarding,是网络管理中很重要的手段。虽然模式各不相同,但是在局域网环境下,通过各种技术手段,最终都能实现相同的目的:虚拟机和主机互相访问(三种模式都可以),虚拟机访问Internet(Bridged和NAT直接可以访问,Host-only通过ICS也可以),外网访问虚拟机上的服务(Bridged直接访问,NAT通过端口转发也可以,Host-only通过架设自己的NAT服务也应该可以)。 本次实验环境比较简单,也没有考虑双网卡的情况,可以参考下面的链接进一步研究。

参考

  1. 简单区分Vmware的三种网络连接模式(bridged、NAT、host-only)
  2. Vmware虚拟机下三种网络模式配置
  3. VMWare虚拟机 网络连接模式 [汇总帖]
  4. VMware虚拟机上网络连接(network type)的三种模式--bridged、host-only、NAT [汇总帖]
  5. VMWare虚拟机下为Ubuntu 12.04.1配置静态IP(NAT方式)
  6. VMware Workstation虚拟机网络连接杂记、给Windows虚拟机配置固定IP
  7. 解决Windows 7/win8 使用VMware虚拟机的NAT 不能上网
  8. 使用 ICS(Internet 连接共享)
  9. internet连接共享访问被启用时,出现一个错误
  10. VMware Workstation虚拟机实例:让外网访问虚拟机
  11. VMware Workstation实例二:单IP的虚拟机提供外网访问
扫描二维码,在手机上阅读!