一直想写一个跨局域网聊天和文件传输,以及视频聊天的软件,这两天刚好闲着没啥事就把代码写完了,代码已经上传至github:https://github.com/vinllen/chat

其实之前想法P2P模式,P2P的话必须穿透NAT,现在的NAT有4种模式:

  1. 完全圆锥型NAT
  2. 受限圆锥型NAT
  3. 端口受限圆锥型NAT
  4. 对称NAT(双向NAT)

维基百科给出的定义如下:

  • 1.Full cone NAT,亦即著名的一对一(one-to-one)NAT

一旦一个内部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有发自iAddr:port1的包都经由eAddr:port2向外发送。任意外部主机都能通过给eAddr:port2发包到达iAddr:port1

  • 2.Address-Restricted cone NAT

一旦一个内部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有发自iAddr:port1的包都经由eAddr:port2向外发送。任意外部主机(hostAddr:any)都能通过给eAddr:port2发包到达iAddr:port1的前提是:iAddr:port1之前发送过包到hostAddr:any. "any"也就是说端口不受限制

  • 3.Port-Restricted cone NAT

类似受限制锥形NAT(Restricted cone NAT),但是还有端口限制。

一旦一个内部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有发自iAddr:port1的包都经由eAddr:port2向外发送。一个外部主机(hostAddr:port3)能够发包到达iAddr:port1的前提是:iAddr:port1之前发送过包到hostAddr:port3.

  • 4.Symmetric NAT(对称NAT)

每一个来自相同内部IP与端口,到一个特定目的地地址和端口的请求,都映射到一个独特的外部IP地址和端口。同一内部IP与端口发到不同的目的地和端口的信息包,都使用不同的映射
只有曾经收到过内部主机封包的外部主机,才能够把封包发回

对于第1种特别简单,因为端口存在映射,只要把包发网出口路由的端口即可,路由会帮你转发

对于第2,3种情况,可以采用如下办法(内容来自该博客:点击打开链接):

假设网络模型如下:

限制性锥NAT 和端口限制性锥NAT (简称限制性NAT ),穿透限制性锥NAT 会丢弃它未知的源地址发向内部主机的数据包。所以如果现在ClientA-1 直接发送UDP 数据包到ClientB-1 ,那么数据包将会被NAT-B 无情的丢弃。所以采用下面的方法来建立ClientA-1 和ClientB-1 之间的通信。

  • 1 .ClientA-1 (202.103.142.29:5000 )发送数据包给Server ,请求和ClientB-1 (221.10.145.84:6000 )通信。
  • 2. Server 将ClientA-1 的地址和端口(202.103.142.29:5000 )发送给ClientB-1 ,告诉ClientB-1 ,ClientA-1 想和它通信。
  • 3. ClientB-1 向ClientA-1 (202.103.142.29:5000 )发送UDP 数据包,当然这个包在到达NAT-A 的时候,还是会被丢弃,这并不是关键的,因为发送这个UDP 包只是为了让NAT-B 记住这次通信的目的地址:端口号,当下次以这个地址和端口为源的数据到达的时候就不会被NAT-B 丢弃,这样就在NAT-B 上打了一个从ClientB-1 到ClientA-1 的孔。
  • 4. 为了让ClientA-1 知道什么时候才可以向ClientB-1 发送数据,所以ClientB-1 在向ClientA-1 (202.103.142.29:5000 )打孔之后还要向Server 发送一个消息,告诉Server 它已经准备好了。
  • 5. Server 发送一个消息给ClientA-1 ,内容为:ClientB-1 已经准备好了,你可以向ClientB-1 发送消息了。
  • 6. ClientA-1 向ClientB-1 发送UDP 数据包。这个数据包不会被NAT-B 丢弃,以后ClientB-1 向ClientA-1 发送的数据包也不会被ClientA-1 丢弃,因为NAT-A 已经知道是ClientA-1 首先发起的通信。至此,ClientA-1 和ClientB-1 就可以进行通信了。
对于第4种情况,oh no,太难了,需要猜测端口号(博客地址:点击打开链接):
上面讨论的都是怎样穿透锥(Cone )NAT ,对称NAT 和锥NAT 很不一样。对于对称NAT ,当一个私网内主机和外部多个不同主机通信时,对称NAT并不会像锥(Cone ,全锥,限制性锥,端口限制性锥)NAT那样分配同一个端口。而是会新建立一个Session ,重新分配一个端口。参考上面穿透限制性锥NAT 的过程,在步骤3 时:ClientB-1 (221.10.145.84: ?)向ClientA-1 打孔的时候,对称NAT 将给ClientB-1 重新分配一个端口号,而这个端口号对于Server 、ClientB-1 、ClientA-1 来说都是未知的。同样, ClientA-1 根本不会收到这个消息,同时在步骤4 ,ClientB-1 发送给Server 的通知消息中,ClientB-1 的socket 依旧是(221.10.145.84:6000 )。而且,在步骤6 时:ClientA-1 向它所知道但错误的ClientB-1 发送数据包时,NAT-1 也会重新给ClientA-1 分配端口号。所以,穿透对称NAT 的机会很小。下面是两种有可能穿透对称NAT 的策略。
1.同时开放TCP ( Simultaneous TCP open )策略
如果一个 对称 NAT 接收到一个来自 本地 私有网 络 外面的 TCP SYN 包, 这 个包想 发 起一个 “ 引入” 的 TCP 连 接,一般来 说 , NAT 会拒 绝这 个 连 接 请 求并扔掉 这 个 SYN 包,或者回送一个TCP RST (connection reset ,重建 连 接)包 给请 求方。但是,有一 种 情况 却会接受这个“引入”连接。
RFC 规定:对于对称NAT , 当 这 个接收到的 SYN 包中的源IP 地址 : 端口、目 标 IP 地址 : 端口都与NAT 登 记 的一个已 经 激活的 TCP 会 话 中的地址信息相符 时 , NAT 将会放行 这 个 SYN 包。 需要 特 别 指出 的是:怎样才是一个已经激活的TCP 连接?除了真正已经建立完成的TCP 连接外,RFC 规范指出: 如果 NAT 恰好看到一个 刚刚发 送出去的一个 SYN 包和 随之 接收到的SYN 包中的地址 :端口 信息相符合的 话 ,那 么 NAT 将会 认为这 个 TCP 连 接已 经 被激活,并将允 许这 个方向的 SYN 包 进 入 NAT 内部。 同时开放TCP 策略就是利用这个时机来建立连接的。
如果 Client A -1 和 Client B -1 能 够 彼此正确的 预 知 对 方的 NAT 将会 给 下一个 TCP 连 接分配的公网 TCP 端口,并且两个客 户 端能 够 同 时 地 发 起一 个面向对方的 “ 外出 ” 的 TCP 连 接 请求 ,并在 对 方的 SYN 包到达之前,自己 刚发 送出去的 SYN 包都能 顺 利的穿 过 自己的 NAT 的 话 ,一条端 对 端的 TCP 连 接就 能 成功地建立了 。
2.UDP 端口猜测策略
同时开放TCP 策略非常依赖于猜测对方的下一个端口,而且强烈依赖于发送连接请求的时机,而且还有网络的不确定性,所以能够建立的机会很小,即使Server 充当同步时钟的角色。下面是一种通过UDP 穿透的方法,由于UDP 不需要建立连接,所以也就不需要考虑“同时开放”的问题。
为了介绍ClientB-1 的诡计,先介绍一下STUN 协议。STUN (Simple Traversal of UDP Through NATs )协议是一个轻量级协议,用来探测被NAT 映射后的地址:端口。STUN 采用C/S 结构,需要探测自己被NAT 转换后的地址:端口的Client 向Server 发送请求,Server 返回Client 转换后的地址:端口。
参考4.2 节中穿透NAT 的步骤2 ,当ClientB-1 收到Server 发送给它的消息后,ClientB-1 即打开3 个socket 。socket-0 向STUN Server 发送请求,收到回复后,假设得知它被转换后的地址:端口( 221.10.145.84:600 5 ),socket-1 向ClientA-1 发送一个UDP 包,socket-2 再次向另一个STUN Server 发送请求,假设得到它被转换后的地址:端口( 221.10.145.84:60 20 )。通常,对称NAT 分配端口有两种策略,一种是按顺序增加,一种是随机分配。如果这里对称NAT 使用顺序增加策略,那么,ClientB-1 将两次收到的地址:端口发送给Server 后,Server 就可以通知ClientA-1 在这个端口范围内猜测刚才ClientB-1 发送给它的socket-1 中被NAT 映射后的地址:端口,ClientA-1 很有可能在孔有效期内成功猜测到端口号,从而和ClientB-1 成功通信。
/************************************华丽分割线**********************************************************/
以上内容大部分参考维基百科和chengweiv5的博客,我自己也写了代码,尝试用第二种方式去穿透我们学校所在的NAT,结果悲剧了,找了好几天的问题也没找出来到底是代码的原因呢还是NAT限制为第4种的原因呢。
But项目不能这么搁浅了。我采用了另外一条曲线救国的路线,就跟现在QQ模式一样,用中转服务器进行消息转发实现。
1.首先搞了一台具有公网ip的服务器
2.配置好环境后(比如iptables等),把服务器代码放置服务器,运行
3.客户端连接后就可以发送文件和聊天了
That's all,就这么简单。
唯一需要说明的就是发送文件时我调用的是sendfile的系统调用,接收文件调用了Pat Patterson的包裹函数:利用splice接收文件,理论上这两个函数是直接在内核态进行文件传递,而不用在内核态和普通态进行数据的拷贝,减少时间。我测试了一下,对几M大小的文件都没问题,不过貌似文件大了貌似传不动了,目测sendfile有大小限制。。。这个就之后再改改了。
最后一点,本来还要实现视频传输的,我的想法就是用opencv调用本地摄像头,然后把图片按每秒固定帧数截下来,按文件方法传输,再在对端对图片进行拼接成视频。后来感觉有点麻烦,最后上网一查,果然我做的部分就是重造轮子的活,现在直接都有现成的文件传输协议。这部分也等以后有时间再去完善吧。
Finally, 如果大家有谁有想法,可以和我交流交流,一起学习进步

Linux socket跨局域网聊天和文件传输相关推荐

  1. android局域网聊天毕业设计,Android基于wifi模块的局域网聊天以及文件传输app

    [实例简介] 一款基于wifi模块的局域网实时聊天以及文件互传的安卓app,能实现热点创建,热点连接,文件传输,实时通讯等功能. [实例截图] [核心代码] MyFeiGe2.0 └── MyFeiG ...

  2. Linux下基于TCP的简易文件传输(socket编程)

    Linux下基于TCP的简易文件传输(socket编程) OSI和TCP/IP: 关于TCP/IP协议 关于TCP协议 TCP编程的一般步骤[^2] TCP文件传输实现 功能概述 服务器编程 客户端编 ...

  3. 项目实战:基于 TCP 的局域网内高性能文件传输系统设计与实现

    本项目开发基于 Red Hat Enterprise Linux(RHEL) 6.3 平台 ,通过本项目大家会深入理解下述内容: Socket 网络编程技术 基于TCP/IP 协议的网络编程技术 基于 ...

  4. Linux系统下的RZSZ(文件传输工具)

    yum install lrzsz.i386 (最方便的方法)  Linux系统下传输方式很多,比如:通过FTP SFTP - 等等.linux服务器大多是通过ssh客户端来进行远程的登陆和管理的,使 ...

  5. java socket 点对点_javaSocket点对点实现文件传输

    伟哥是学C++的,昨天做java老师布置的大作业,让他们做一个文件传输的程序,多线程实现,伟哥很是无语啊,然而他并没有学过太多java,虽然伟哥在我心中是个大神(计算机大神啊,面过了tecent,啧啧 ...

  6. 嵌入式linux与windows之间的tftp文件传输(保姆级)

    目录 前言 一.为什么要文件传输 二.利用共享文件夹(简要流程) 三.tftp传输与环境搭建 四.传输前的准备工作 五.测试准备工作是否完成 六.传输文件 6.1.windows中tftp32的配置 ...

  7. Linux 虚拟机与主机之间的文件传输(三)

    Linux  虚拟机与主机之间的文件传输(三) 一.简述        记录Linux  虚拟机与主机之间的文件传输方式,比如使用共享文件夹.使用Samba.SecureCRT.FileZilla.C ...

  8. linux端口扫描nc,Linux下nc命来实现文件传输、端口扫描

    今天在饮水思源上闲逛,看到了一个贴子关于Linux下nc命来实现文件传输,进行学习了解了一下. 发送端: cat test.txt | nc -l -p 6666 或者nc -l  -p 6666 & ...

  9. linux nc 传送文件,Linux下nc命来实现文件传输

    发送端: cat test.txt | nc -l -p 6666 或者nc -l -p 6666 < test.txt 有些版本不要在 -p [监听6666端口,等待连接](设发送端IP为10 ...

  10. 用C语言写一个通信软件,客户端可以实现文字聊天,文件传输,建立群聊;服务端可以创建账号吧...

    首先,需要为客户端和服务端分别编写代码,客户端的代码应该包括实现文字聊天.文件传输和建立群聊的功能,而服务端的代码则需要实现创建账号的功能.需要考虑到网络的安全性.客户端的用户体验以及服务端的可扩展性 ...

最新文章

  1. 应用随机过程张波商豪_Markov链的应用一:MCMC算法
  2. android studio运行时报错the selected device is incompatible
  3. 中控考勤机的二次开发之数据秒上传至服务器功能
  4. Libidn 简介 对国际化域名进行编码和解码
  5. pyV8不支持dom操作,关于PyV8的支持DOM的疑问
  6. SparkSQL之操作Mysql
  7. 解决SimpleButton被移除后保持OVER状态
  8. hive java导入CVS
  9. Null + Anything = null, 好奇怪的设定啊
  10. python 正则匹配电话与ip
  11. mysql 隐秘后门_Phpstudy被暴存在隐藏后门-检查方法
  12. 微信公众号——分享给朋友/分享至朋友圈(Vue)
  13. 如何在linux下玩lol_英雄联盟新手教程 教你怎么玩lol
  14. Module parse failed Unexpected token
  15. On the eighth day
  16. Java中的与或非、异或运算
  17. 【​观察】“数字广东”背后的力量 腾讯云创新政务服务新模式
  18. 流体机械特性曲线 水轮机运转特性曲线
  19. 计算机科学 在职双证,计算机专业在职研究生有双证的吗?
  20. 服务器和交换机物理连接_Brocade博科交换机 SAN存储区域网络

热门文章

  1. numpy中的revel和flatten
  2. 什么是 PaaS?“平台即服务“ 简介
  3. Linux 文件删除不了? 一招教你搞定!
  4. 免费建立一个自己的网站
  5. 清华大学计算机竞赛自主招生,清华大学自主招生竞赛有哪些要求
  6. 【转载】通过搜狗站长平台查看网站的搜狗流量及搜索关键字
  7. 【引用】43种名车标志及来历
  8. 自定义AutoTextView实现公告栏 文字3D 翻转动画
  9. Ubuntu中挂载使用nas服务器
  10. protocol buffer生成C语言的实现