asp.net core web 添加角色管理
新建asp.net core web应用
添加RolesAdminController
[Authorize(Roles = "Admin")] public class RolesAdminController : Controller { private UserManager<ApplicationUser> _userManager; private RoleManager<IdentityRole> _roleManager; private readonly ILogger _logger; public RolesAdminController(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, ILogger<AccountController> logger) { _userManager = userManager; _roleManager = roleManager; _logger = logger; } // // GET: /Roles/ public ActionResult Index() { return View(_roleManager.Roles); } // // GET: /Roles/Details/5 public async Task<ActionResult> Details(string id) { if (id == null) { throw new ApplicationException(); } var role = await _roleManager.FindByIdAsync(id); // Get the list of Users in this Role var users = new List<ApplicationUser>(); // Get the list of Users in this Role foreach (var user in _userManager.Users.ToList()) { if (await _userManager.IsInRoleAsync(user, role.Name)) { users.Add(user); } } ViewBag.Users = users; ViewBag.UserCount = users.Count(); return View(role); } // // GET: /Roles/Create public ActionResult Create() { return View(); } // // POST: /Roles/Create [HttpPost] public async Task<ActionResult> Create(IdentityRole roleViewModel) { if (ModelState.IsValid) { var role = new IdentityRole(roleViewModel.Name); var roleresult = await _roleManager.CreateAsync(role); if (!roleresult.Succeeded) { AddErrors(roleresult); return View(); } return RedirectToAction("Index"); } return View(); } // // GET: /Roles/Edit/Admin public async Task<ActionResult> Edit(string id) { if (id == null) { throw new ApplicationException(); } var role = await _roleManager.FindByIdAsync(id); if (role == null) { throw new ApplicationException(); } IdentityRole roleModel = new IdentityRole { Id = role.Id, Name = role.Name }; return View(roleModel); } [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Edit(IdentityRole roleModel) { if (ModelState.IsValid) { var role = await _roleManager.FindByIdAsync(roleModel.Id); role.Name = roleModel.Name; await _roleManager.UpdateAsync(role); return RedirectToAction("Index"); } return View(); } // // GET: /Roles/Delete/5 public async Task<ActionResult> Delete(string id) { if (id == null) { throw new ApplicationException(); } var role = await _roleManager.FindByIdAsync(id); if (role == null) { throw new ApplicationException(); } return View(role); } // // POST: /Roles/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task<ActionResult> DeleteConfirmed(string id, string deleteUser) { if (ModelState.IsValid) { if (id == null) { throw new ApplicationException(); } var role = await _roleManager.FindByIdAsync(id); if (role == null) { throw new ApplicationException(); } IdentityResult result; if (deleteUser != null) { result = await _roleManager.DeleteAsync(role); } else { result = await _roleManager.DeleteAsync(role); } if (!result.Succeeded) { AddErrors(result); return View(); } return RedirectToAction("Index"); } return View(); } #region Helpers private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } private IActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction(nameof(HomeController.Index), "Home"); } } #endregion }
添加对应的View
index
@model IEnumerable<IdentityRole> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.Name) </th> <th> </th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.Id }) | @Html.ActionLink("Details", "Details", new { id = item.Id }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id }) </td> </tr> } </table>
create
@model IdentityRole @{ ViewBag.Title = "Create"; } <h2>Create.</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Role.</h4> <hr /> @Html.ValidationSummary(true) <div class="form-group"> @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.TextBoxFor(model => model.Name, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Name) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") }
edit
@model IdentityRole @{ ViewBag.Title = "Edit"; } <h2>Edit.</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Roles.</h4> <hr /> @Html.ValidationSummary(true) @Html.HiddenFor(model => model.Id) <div class="form-group"> @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.TextBoxFor(model => model.Name, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Name) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Save" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") }
details
@model IdentityRole @{ ViewBag.Title = "Details"; } <h2>Role Details.</h2> <div> <h4>Roles.</h4> <hr /> <dl class="dl-horizontal"> <dt> @Html.DisplayNameFor(model => model.Name) </dt> <dd> @Html.DisplayFor(model => model.Name) </dd> </dl> </div> <h4>List of users in this role</h4> @if (ViewBag.UserCount == ) { <hr /> <p>No users found in this role.</p> } <table class="table"> @foreach (var item in ViewBag.Users) { <tr> <td> @item.UserName </td> </tr> } </table> <p> @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) | @Html.ActionLink("Back to List", "Index") </p>
delete
@model IdentityRole @{ ViewBag.Title = "Delete"; } <h2>Delete.</h2> <h3>Are you sure you want to delete this Role? </h3> <p>Deleting this Role will remove all users from this role. It will not delete the users.</p> <div> <h4>Delete Role.</h4> <hr /> <dl class="dl-horizontal"> <dt> @Html.DisplayNameFor(model => model.Name) </dt> <dd> @Html.DisplayFor(model => model.Name) </dd> </dl> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-actions no-color"> <input type="submit" value="Delete" class="btn btn-default" /> | @Html.ActionLink("Back to List", "Index") </div> } </div>
添加UsersAdminController
[Authorize(Roles = "Admin")] public class UsersAdminController : Controller { public UsersAdminController(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, ILogger<AccountController> logger) { _userManager = userManager; _roleManager = roleManager; _logger = logger; } private UserManager<ApplicationUser> _userManager; private RoleManager<IdentityRole> _roleManager; private readonly ILogger _logger; // // GET: /Users/ public async Task<ActionResult> Index() { return View(await _userManager.Users.ToListAsync()); } // // GET: /Users/Details/5 public async Task<ActionResult> Details(string id) { if (id == null) { throw new ApplicationException(); } var user = await _userManager.FindByIdAsync(id); ViewBag.RoleNames = await _userManager.GetRolesAsync(user); return View(user); } // // GET: /Users/Create public async Task<ActionResult> Create() { //Get the list of Roles ViewBag.RoleId = new SelectList(await _roleManager.Roles.ToListAsync(), "Name", "Name"); return View(); } // // POST: /Users/Create [HttpPost] public async Task<ActionResult> Create(RegisterViewModel userViewModel, params string[] selectedRoles) { if (ModelState.IsValid) { var user = new ApplicationUser { UserName = userViewModel.Email, Email = userViewModel.Email, }; // Then create: var adminresult = await _userManager.CreateAsync(user, userViewModel.Password); //Add User to the selected Roles if (adminresult.Succeeded) { if (selectedRoles != null) { var result = await _userManager.AddToRolesAsync(user, selectedRoles); if (!result.Succeeded) { AddErrors(result); ViewBag.RoleId = new SelectList(await _roleManager.Roles.ToListAsync(), "Name", "Name"); return View(); } } } else { AddErrors(adminresult); ViewBag.RoleId = new SelectList(_roleManager.Roles, "Name", "Name"); return View(); } return RedirectToAction("Index"); } ViewBag.RoleId = new SelectList(_roleManager.Roles, "Name", "Name"); return View(); } // // GET: /Users/Edit/1 public async Task<ActionResult> Edit(string id) { if (id == null) { throw new ApplicationException(); } var user = await _userManager.FindByIdAsync(id); if (user == null) { throw new ApplicationException(); } var userRoles = await _userManager.GetRolesAsync(user); return View(new EditUserViewModel() { Id = user.Id, Email = user.Email, RolesList = _roleManager.Roles.ToList().Select(x => new SelectListItem() { Selected = userRoles.Contains(x.Name), Text = x.Name, Value = x.Name }) }); } // // POST: /Users/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Edit(EditUserViewModel editUser, params string[] selectedRole) { if (ModelState.IsValid) { var user = await _userManager.FindByIdAsync(editUser.Id); if (user == null) { throw new ApplicationException(); } user.UserName = editUser.Email; user.Email = editUser.Email; var userRoles = await _userManager.GetRolesAsync(user); selectedRole = selectedRole ?? new string[] { }; var result = await _userManager.AddToRolesAsync(user, selectedRole.Except(userRoles).ToArray<string>()); if (!result.Succeeded) { AddErrors(result); return View(); } result = await _userManager.RemoveFromRolesAsync(user, userRoles.Except(selectedRole).ToArray<string>()); if (!result.Succeeded) { AddErrors(result); return View(); } return RedirectToAction("Index"); } ModelState.AddModelError("", "Something failed."); return View(); } // // GET: /Users/Delete/5 public async Task<ActionResult> Delete(string id) { if (id == null) { throw new ApplicationException(); } var user = await _userManager.FindByIdAsync(id); if (user == null) { throw new ApplicationException(); } return View(user); } // // POST: /Users/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task<ActionResult> DeleteConfirmed(string id) { if (ModelState.IsValid) { if (id == null) { throw new ApplicationException(); } var user = await _userManager.FindByIdAsync(id); if (user == null) { throw new ApplicationException(); } var result = await _userManager.DeleteAsync(user); if (!result.Succeeded) { AddErrors(result); return View(); } return RedirectToAction("Index"); } return View(); } #region Helpers private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } private IActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction(nameof(HomeController.Index), "Home"); } } #endregion }
添加对应的EditUserViewModel
public class EditUserViewModel { public string Id { get; set; } [Required(AllowEmptyStrings = false)] [Display(Name = "Email")] [EmailAddress] public string Email { get; set; } public IEnumerable<SelectListItem> RolesList { get; set; } }
添加对应的view
index
@model IEnumerable<WebApplication2.Models.ApplicationUser> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.UserName) </th> <th> </th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.UserName) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.Id }) | @Html.ActionLink("Details", "Details", new { id = item.Id }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id }) </td> </tr> } </table>
create
@model WebApplication2.Models.AccountViewModels.RegisterViewModel @{ ViewBag.Title = "Create"; } <h2>@ViewBag.Title.</h2> @using (Html.BeginForm("Create", "UsersAdmin", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() <h4>Create a new account.</h4> <hr /> @Html.ValidationSummary("", new { @class = "text-error" }) <div class="form-group"> @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) </div> </div> <div class="form-group"> <label class="col-md-2 control-label"> Select User Role </label> <div class="col-md-10"> @foreach (var item in (SelectList)ViewBag.RoleId) { <input type="checkbox" name="SelectedRoles" value="@item.Value" class="checkbox-inline" /> @Html.Label("Role", item.Value, new { @class = "control-label" }) } </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" class="btn btn-default" value="Create" /> </div> </div> } @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") }
edit
@model WebApplication2.Models.AdminViewModels.EditUserViewModel @{ ViewBag.Title = "Edit"; } <h2>Edit.</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Edit User Form.</h4> <hr /> @Html.ValidationSummary(true) @Html.HiddenFor(model => model.Id) <div class="form-group"> @Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Email) </div> </div> <div class="form-group"> @Html.Label("Roles","", new { @class = "control-label col-md-2" }) <span class=" col-md-10"> @foreach (var item in Model.RolesList) { <input type="checkbox" name="SelectedRole" value="@item.Value" checked="@item.Selected" class="checkbox-inline" /> @Html.Label("Role",item.Value, new { @class = "control-label" }) } </span> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Save" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") }
details
@model WebApplication2.Models.ApplicationUser @{ ViewBag.Title = "Details"; } <h2>Details.</h2> <div> <h4>User</h4> <hr /> <dl class="dl-horizontal"> <dt> @Html.DisplayNameFor(model => model.UserName) </dt> <dd> @Html.DisplayFor(model => model.UserName) </dd> </dl> </div> <h4>List of roles for this user</h4> @if (ViewBag.RoleNames.Count == ) { <hr /> <p>No roles found for this user.</p> } <table class="table"> @foreach (var item in ViewBag.RoleNames) { <tr> <td> @item </td> </tr> } </table> <p> @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) | @Html.ActionLink("Back to List", "Index") </p>
delete
@model WebApplication2.Models.ApplicationUser @{ ViewBag.Title = "Delete"; } <h2>Delete.</h2> <h3>Are you sure you want to delete this User?</h3> <div> <h4>User.</h4> <hr /> <dl class="dl-horizontal"> <dt> @Html.DisplayNameFor(model => model.UserName) </dt> <dd> @Html.DisplayFor(model => model.UserName) </dd> </dl> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-actions no-color"> <input type="submit" value="Delete" class="btn btn-default" /> | @Html.ActionLink("Back to List", "Index") </div> } </div>
修改共享模板页
Shared/_layout.cshtml
<div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li> <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li> <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li> @if (User!=null && User.IsInRole("Admin")) { <li>@Html.ActionLink("RolesAdmin", "Index", "RolesAdmin")</li> <li>@Html.ActionLink("UsersAdmin", "Index", "UsersAdmin")</li> } </ul> @await Html.PartialAsync("_LoginPartial") </div>
运行应用,注册两个账号
1@1.com具有Admin角色权限
2@2.com没有 Admin权限
1@1.com可以进入管理页面
2@2.com没有管理页面权限
Role具体实现
UserManager<TUser>类中的IsInRoleAsync(user, role.Name)是个虚方法
public virtual Task<bool> IsInRoleAsync(TUser user, string role);
具体的实现是在Microsoft.AspNet.Identity.EntityFramework 中,使用dotPeek 打开Microsoft.AspNet.Identity.EntityFramework.dll
public virtual async Task<bool> IsInRoleAsync(TUser user, string roleName) { this.ThrowIfDisposed(); if ((object) user == null) throw new ArgumentNullException(nameof (user)); if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, nameof (roleName)); TRole role = (TRole) await (TaskExtensions.CultureAwaiter<TRole>) TaskExtensions.WithCurrentCulture<TRole>(QueryableExtensions.SingleOrDefaultAsync<TRole>((IQueryable<M0>) this._roleStore.DbEntitySet, (Expression<Func<M0, bool>>) (r => r.Name.ToUpper() == roleName.ToUpper()))); if ((object) role == null) return false; TKey userId = user.Id; TKey roleId = role.Id; ; }
可以看到判断的依旧是r => r.Name.ToUpper() == roleName.ToUpper()
自定义用户、角色属性
参考:ASP.NET Identity 2.0: Customizing Users and Roles
这里需要修改几处
创建ApplicationRole
public class ApplicationRole:IdentityRole { public ApplicationRole() : base() { } public ApplicationRole(string roleName) : base(roleName) { } }
修改RolesAdminController和UsersAdminController
将之前的IdentityRole替换成ApplicationRole
修改ApplicationDbContext,加上新建的ApplicationRole及TKey--string
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole,string>
修改Startup中的ConfigureServices
services.AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders();
重新运行项目即可
接下来就可以在ApplicationUser和ApplicationRole中添加属性
public class ApplicationRole:IdentityRole { public ApplicationRole() : base() { } public ApplicationRole(string roleName) : base(roleName) { } public string Description { get; set; } }
之后更新对应的ViewModel和页面即可。
asp.net core web 添加角色管理的更多相关文章
- ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理
在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发.订阅和处理的流程.这种实现太简单了,百十行代码就展示了一个基本工作原理.然而,要将这样的解决方案运用到实际生产环境,还有很 ...
- asp.net core web api + Element-UI的Vue管理后台
后端:asp.net core web api + EF Core 前端:VUE + Element-UI+ Node环境的后台管理系统. 线上地址:http://www.wangjk.wang/ 密 ...
- ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现
很长一段时间以来,我都在思考如何在ASP.NET Core的框架下,实现一套完整的事件驱动型架构.这个问题看上去有点大,其实主要目标是为了实现一个基于ASP.NET Core的微服务,它能够非常简单地 ...
- 使用JWT创建安全的ASP.NET Core Web API
在本文中,你将学习如何在ASP.NET Core Web API中使用JWT身份验证.我将在编写代码时逐步简化.我们将构建两个终结点,一个用于客户登录,另一个用于获取客户订单.这些api将连接到在本地 ...
- Azure 部署 Asp.NET Core Web App
在云计算大行其道的时代,当你在部署一个网站时,第一选择肯定是各式各样的云端服务.那么究竟使用什么样的云端服务才能够以最快捷的方式部署一个 ASP.NET Core 的网站呢?Azure 的 Web A ...
- 使用 Swagger 自动生成 ASP.NET Core Web API 的文档、在线帮助测试文档(ASP.NET Core Web API 自动生成文档)
对于开发人员来说,构建一个消费应用程序时去了解各种各样的 API 是一个巨大的挑战.在你的 Web API 项目中使用 Swagger 的 .NET Core 封装 Swashbuckle 可以帮助你 ...
- Gitlab CI 自动部署 asp.net core web api 到Docker容器
为什么要写这个? 在一个系统长大的过程中会经历不断重构升级来满足商业的需求,而一个严谨的商业系统需要高效.稳定.可扩展,有时候还不得不考虑成本的问题.我希望能找到比较完整的开源解决方案来解决持续集成. ...
- ASP.NET Core Web 支付功能接入 支付宝-电脑网页支付篇
这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入支付宝-电脑网页支付接口及同步跳转及异步通知功能. 开发环境:Win 10 x64.VS2017 15.6.4..NET ...
- ASP.NET Core Web 支付功能接入 微信-扫码支付篇
这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步通知功能. 开发环境:Win 10 x64.VS2017 15.6.4..NET Core SDK ...
随机推荐
- RGB,YCBCR在HDMI传输线是数据排列
RGB4:4:4 YCbCr4:4:4 YCbCr4:2:2 YCbCr4:2:0
- Angularjs的directive封装ztree
一般我们做web开发都会用到树,恰好ztree为我们提供了多种风格的树插件. 接下来就看看怎么用Angularjs的directive封装ztree <!DOCTYPE html> < ...
- 阿里云对象存储oss上传文件夹
最近公司做工程项目,实现文件夹云存储上传. 网上找了一天,发现网上很多代码都存在相似问题,最后终于找到了一个满足我需求的项目. 工程如下: 这里对项目的大文件传输功能做出分析,怎么实现文件夹上传的,如 ...
- share pool 管理机制
Library cache是Shared pool的一部分,它几乎是Oracle内存结构中最复杂的一部分,主要存放shared curosr(SQL)和PLSQL对象(function,procedu ...
- 适配android和iOS上position:absolute和input问题
//适配android上absolute和input的问题var oHeight = $(document).height(); //屏幕当前的高度$(window).resize(function( ...
- 20145232 韩文浩 《Java程序设计》第6周学习总结
教材学习内容总结 Java是以串流(Stream)的方式来处理输入与输出. 串流是一种抽象观念,从键盘输入资料,将处理结果输入档案,以及读取档案的内容等动作皆视为串流的处理. 输入串流代表对象为jav ...
- 中国移动物联网平台数据转发 c# 控制台程序
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.N ...
- python编写producer、consumer
自主producer.consumer 首先在不同的终端,分别开启两个consumer,保证groupid一致 ]# python consumer_kafka.py 执行一次producer ]# ...
- Mining Twitter Data with Python
目录 1.Collecting data 1.1 Register Your App 1.2 Accessing the Data 1.3 Streaming 2.Text Pre-processin ...
- 【计算机网络】TCP通信的细节及TCP连接对HTTP事务处理性能影响
从三次握手的细节说起 刚开始尝试使用java等后端语言写IO流,或用套接字(socket)实现简单C/S通信的同学们,常常会接触到的一个概念:就是所谓的“三次握手”,socket作为一个API接口,封 ...