本博客是计算机网络课程设计任务----模拟TCP三次握手。
可能读者看到“模拟TCP三次握手”时首先会先入为主的认为这是一个很难的课题,要模拟TCP三次握手,会用到计算机通信接口什么的,其实不是这样的。所谓模拟我们只需要在有一个握手过程就行了,简单来说就是,我们能创建两个能相互通信的进程,某个进程率先发起通信请求,另外一个进程收到通信请求,返回一个消息,发起进程再回复一个消息,实现这样一个过程我们就一次“TCP三次握手”!
首先,介绍一下博主的思路:
? ? ? ? 因为要完成这个模拟,需要两个能相互通信的进程(或者说程序),博主首先想到的是Web前后端,因为Socket技术诞生后,前后端程序都能主动向另一方发送消息,这一特性完美解决了课题中两个进程需要通信的问题。
? ? ? ? 所以技术方面本博主采用node.js搭建了一个简易的后端,前端用原生html,css,以及js实现。
项目目录结构如下:
node_modules是导入的模块包使用npm自动安装的,public文件夹是用来放前端文件,app.js是项目入口文件,也是后端事务处理实现。
具体操作如下:
在创建的文件目录下执行npm指令:
按照目录结构所示创建app.js等文件。
主要代码实现如下:
app.js:
/*
服务端
*/
var app = require('express')()
var server = require('http').Server(app)
var io = require('socket.io')(server)
server.listen(3000,()=>{
console.log("服务器启动成功!")
})
//express处理静态资源
//把public目录设置为静态资源目录
app.use(require('express').static('public'))
app.get('/',function(req,res){
res.redirect('/index.html')
})
//定义一个模拟TCP报文的Json文件
var TCP_S={}
TCP_S.rport=9090
//定义是否建立连接
var isLink=false
io.on('connection',function(socket){
console.log("新用户连接了")
socket.on('asklink',data=>{
if(data.TCP_C.dport==TCP_S.rport){
if(data.TCP_C.SYN==1&&data.TCP_C.ACK==0){
if(isLink){
socket.emit('sechandshake',{
flag:1,
msg:"已建立连接!"
})
}else{
console.log("第一次握手")
TCP_S.dport=data.TCP_C.rport,
TCP_S.SYN=1,
TCP_S.ACK=1,
TCP_S.seq=Math.ceil(Math.random()*10)
TCP_S.ack=data.TCP_C.seq+1
socket.emit('sechandshake',{
flag:0,
TCP_S
})
}
}else{
console.log("第一握手失败")
}
}else{
console.log("report:"+data.TCP_C.rport)
socket.emit('failLink',{
msg:"该端口不存在"
})
}
})
socket.on('thirdlink',data=>{
//判断第三次握手的数据
//console.log("第三次请求")
if(data.TCP_C.SYN==1&&data.TCP_C.ACK){
if(data.TCP_C.seq==TCP_S.ack&&data.TCP_C.ack==(TCP_S.seq+1)){
isLink=true
console.log("第三次握手成功!确认建立连接")
socket.emit('affLink',{
msg:"确认建立连接!允许发送消息"
})
}else{
console.log("第三次握手失败,数据不匹配,断开连接!")
console.log("TCP_C:"+data.TCP_C.seq+" TCP_S:"+TCP_S.ack+" TCP_C:"+data.TCP_C.ack+" TCP_S"+TCP_S.seq)
}
}else{
console.log("第三次握手请求失败,断开连接!")
}
})
socket.on('lunxchmsg',data=>{
if(isLink){
console.log(data)
if(data.msg==1){
socket.emit('remsg',{
flag:1,
msg:`服务器信息如下:IPv6 地址 . . . . . . . . . . . . : 2001:da8:8011:371f:acee:16fb:8585:8dd1
临时 IPv6 地址. . . . . . . . . . : 2001:da8:8011:371f:a1bb:25d0:d11:5e3a
本地链接 IPv6 地址. . . . . . . . : fe80::acee:16fb:8585:8dd1%21
IPv4 地址 . . . . . . . . . . . . : 10.55.25.81
子网掩码 . . . . . . . . . . . . : 255.255.240.0
默认网关. . . . . . . . . . . . . : fe80::4abd:3dff:fe22:77e0%21
10.55.31.254 `
})
}else{
socket.emit('remsg',{
msg:"这是回复消息:"+data.msg,
flag:1
})
}
}else{
socket.emit('remsg',{
msg:"未建立连接,不支持发送消息",
flag:0
})
}
})
socket.on('disconnect',()=>{
console.log("用户断开连接!")
isLink=false
})
})
js文件夹下的index.js文件(jquery文件是从网上下载的,这里就不粘贴了)
/*
客户端
*/
//连接socket服务
var socket = io('http://localhost:3000')
var TCP_C = {}
TCP_C.rport=9000
// 需要告诉socket io服务,客户端请求登录
$('#asklink').on('click', function () {
//设置第一次请求参数
TCP_C.dport=$('#link_port').val().trim()
TCP_C.SYN=1
TCP_C.ACK=0
TCP_C.seq=Math.ceil(Math.random()*10)
$('.showmsg').append("<p>正在进行第一次握手,参数为:SYN="+TCP_C.SYN+" ACK="+TCP_C.ACK+" seq="+TCP_C.seq+"</p>")
socket.emit('asklink', {
TCP_C
})
sleep(1000)
})
socket.on('failLink',data=>{
alert(data.msg)
$('.showmsg').append("<p>请求的端口不存在,连接失败!</p>")
})
socket.on('sechandshake',data=>{
if(data.flag){
$('.showmsg').append("<p>"+data.msg+"</p>")
}else{
if(data.TCP_S.SYN==1&&data.TCP_S.ACK==1&&data.TCP_S.ack==(TCP_C.seq+1)){
$('.showmsg').append("<p>第二次握手成功!参数为:SYN="+data.TCP_S.SYN+" ACK="+data.TCP_S.ACK+" ack="+data.TCP_S.ack+" seq="+data.TCP_S.seq+"</p>")
TCP_C.ACK=1
TCP_C.seq=data.TCP_S.ack
TCP_C.ack=data.TCP_S.seq+1
sleep(1000)
socket.emit('thirdlink',{
TCP_C
})
sleep(1000)
$('.showmsg').append("<p>正在进行第三次握手,参数为:SYN="+TCP_C.SYN+" ACK="+TCP_C.ACK+" ack="+TCP_C.ack+" seq="+TCP_C.seq+"</p>")
}else{
console.log("第二次握手失败!")
$('.showmsg').text("第二次握手失败")
}
}
})
socket.on('affLink',data=>{
$('.showmsg').append("<p>"+data.msg+"</p>")
sleep(1000)
alert(data.msg)
})
//显示细节
var flag=1
$('#showdetail_B').on('click', function () {
if(flag){
$('#showdetail').fadeIn()
flag=0
}else{
$('#showdetail').hide()
flag=1
}
})
$('#LunchMsg').on('click', function () {
socket.emit('lunxchmsg',{
msg:$('#Lunch_txt').val().trim()
})
})
socket.on('remsg',data=>{
if(data.flag){
$('#socketremsg').text("<p>"+data.msg+"</p>")
}else{
alert(data.msg)
}
})
//睡眠函数
function sleep(delay) {
let now = new Date();
const exitTime = now.getTime() + delay;
let complite=true;
while (complite) {
now = new Date();
if (now.getTime() > exitTime) {
complite=false;
}
}
return;
}
前端页面显示index.html文件:
<!DOCTYPE html>
<html>
<head>
<title>TCP三次握手</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-2" id="sidePage"></div>
<!--正文-->
<div class="col-md-8">
<div class="emptyBox"></div>
<div class="sendBox">
<h1 id="title_h1">TCP三次握手模拟</h1>
<div class="input-group">
<input type="text" id="link_port" class="form-control" placeholder="Input a port you want">
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="asklink">Link</button>
</span>
</div>
<button class="btn btn-default" type="button" id="showdetail_B">显示细节</button>
<div id="showdetail">
<p id="showmsg" class="showmsg"></p>
</div>
<div class="input-group">
<input type="text" id="Lunch_txt" class="form-control" placeholder="Input a port you want">
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="LunchMsg">Lunch</button>
</span>
</div>
<p id="socketremsg"></p>
</div>
</div>
<!---->
<div class="col-md-2" id="sidePage"></div>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="js/jquery.js"></script>
<script src="js/index.js"></script>
</body>
</html>
写好后在该目录下使用指令启动项目:
本项目监听的是本机3000端口,打开浏览器出入:localhost:3000效果如下:
?输入我们设定的9090端口,点击link效果如下:
?
?
?
|