PCEtLXtzdWJ0ZW1wbGF0ZSBjb21tb24vaGVhZGVyX2NvbW1vbn0tLT4JPCEtLXtpZiAkX0dFVFsnbW9kJ10gPT0gJ3ZpZXd0aHJlYWQnICYmICFlbXB0eSgkX0dbJ3RpZCddKX0tLT4KCTxsaW5rIHJlbD0iY2Fub25pY2FsIiBocmVmPSJodHRwczovL3d3dy5xaWRhbzEyMy5jb20vYmJzL3RocmVhZC17JF9HWyd0aWQnXX0tMS0xLmh0bWwiIC8+Cgk8IS0te2Vsc2VpZiAkX0dFVFsnbW9kJ10gPT0gJ2ZvcnVtZGlzcGxheScgJiYgIWVtcHR5KCRfR1snZmlkJ10pfS0tPgoJPGxpbmsgcmVsPSJjYW5vbmljYWwiIGhyZWY9Imh0dHBzOi8vd3d3LnFpZGFvMTIzLmNvbS9iYnMvZm9ydW0teyRfR1snZmlkJ119LTEuaHRtbCIgLz4KCTwhLS17ZWxzZWlmICRfR0VUWydtb2QnXSA9PSAnZ3VpZGUnfS0tPgoJPGxpbmsgcmVsPSJjYW5vbmljYWwiIGhyZWY9Imh0dHBzOi8vd3d3LnFpZGFvMTIzLmNvbS9iYnMvZ3VpZGUvIiAvPgoJPCEtLXtlbHNlaWYgJF9HWydiYXNlc2NyaXB0J10gPT0gJ2ZvcnVtJyAmJiAoZW1wdHkoJF9HRVRbJ21vZCddKSB8fCAkX0dFVFsnbW9kJ10gPT0gJ2luZGV4Jyl9LS0+Cgk8bGluayByZWw9ImNhbm9uaWNhbCIgaHJlZj0iaHR0cHM6Ly93d3cucWlkYW8xMjMuY29tL2Jicy8iIC8+Cgk8IS0tey9pZn0tLT4KCXJgbmB0PG1ldGEgbmFtZT0iYXBwbGljYXRpb24tbmFtZSIgY29udGVudD0iJF9HWydzZXR0aW5nJ11bJ2JibmFtZSddIiAvPgoJPG1ldGEgbmFtZT0ibXNhcHBsaWNhdGlvbi10b29sdGlwIiBjb250ZW50PSIkX0dbJ3NldHRpbmcnXVsnYmJuYW1lJ10iIC8+Cgk8IS0te2lmICRfR1snc2V0dGluZyddWydwb3J0YWxzdGF0dXMnXX0tLT48bWV0YSBuYW1lPSJtc2FwcGxpY2F0aW9uLXRhc2siIGNvbnRlbnQ9Im5hbWU9JF9HWydzZXR0aW5nJ11bJ25hdnMnXVsxXVsnbmF2bmFtZSddO2FjdGlvbi11cmk9e2VjaG8gIWVtcHR5KCRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ3BvcnRhbCddKSA/ICRfR1snc2NoZW1lJ10uJzovLycuJF9HWydzZXR0aW5nJ11bJ2RvbWFpbiddWydhcHAnXVsncG9ydGFsJ10gOiAkX0dbc2l0ZXVybF0uJ3BvcnRhbC5waHAnfTtpY29uLXVyaT17JF9HW3NpdGV1cmxdfXtJTUdESVJ9L3BvcnRhbC5pY28iIC8+PCEtLXsvaWZ9LS0+Cgk8bWV0YSBuYW1lPSJtc2FwcGxpY2F0aW9uLXRhc2siIGNvbnRlbnQ9Im5hbWU9JF9HWydzZXR0aW5nJ11bJ25hdnMnXVsyXVsnbmF2bmFtZSddO2FjdGlvbi11cmk9e2VjaG8gIWVtcHR5KCRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2ZvcnVtJ10pID8gJF9HWydzY2hlbWUnXS4nOi8vJy4kX0dbJ3NldHRpbmcnXVsnZG9tYWluJ11bJ2FwcCddWydmb3J1bSddIDogJF9HW3NpdGV1cmxdLidmb3J1bS5waHAnfTtpY29uLXVyaT17JF9HW3NpdGV1cmxdfXtJTUdESVJ9L2Jicy5pY28iIC8+Cgk8IS0te2lmICRfR1snc2V0dGluZyddWydncm91cHN0YXR1cyddfS0tPjxtZXRhIG5hbWU9Im1zYXBwbGljYXRpb24tdGFzayIgY29udGVudD0ibmFtZT0kX0dbJ3NldHRpbmcnXVsnbmF2cyddWzNdWyduYXZuYW1lJ107YWN0aW9uLXVyaT17ZWNobyAhZW1wdHkoJF9HWydzZXR0aW5nJ11bJ2RvbWFpbiddWydhcHAnXVsnZ3JvdXAnXSkgPyAkX0dbJ3NjaGVtZSddLic6Ly8nLiRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2dyb3VwJ10gOiAkX0dbc2l0ZXVybF0uJ2dyb3VwLnBocCd9O2ljb24tdXJpPXskX0dbc2l0ZXVybF19e0lNR0RJUn0vZ3JvdXAuaWNvIiAvPjwhLS17L2lmfS0tPgoJPCEtLXtpZiBoZWxwZXJfYWNjZXNzOjpjaGVja19tb2R1bGUoJ2ZlZWQnKX0tLT48bWV0YSBuYW1lPSJtc2FwcGxpY2F0aW9uLXRhc2siIGNvbnRlbnQ9Im5hbWU9JF9HWydzZXR0aW5nJ11bJ25hdnMnXVs0XVsnbmF2bmFtZSddO2FjdGlvbi11cmk9e2VjaG8gIWVtcHR5KCRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2hvbWUnXSkgPyAkX0dbJ3NjaGVtZSddLic6Ly8nLiRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2hvbWUnXSA6ICRfR1tzaXRldXJsXS4naG9tZS5waHAnfTtpY29uLXVyaT17JF9HW3NpdGV1cmxdfXtJTUdESVJ9L2hvbWUuaWNvIiAvPjwhLS17L2lmfS0tPgoJPCEtLXtpZiAkX0dbJ2Jhc2VzY3JpcHQnXSA9PSAnZm9ydW0nICYmICRfR1snc2V0dGluZyddWydhcmNoaXZlciddfS0tPgoJCTxsaW5rIHJlbD0iYXJjaGl2ZXMiIHRpdGxlPSIkX0dbJ3NldHRpbmcnXVsnYmJuYW1lJ10iIGhyZWY9InskX0dbc2l0ZXVybF19YXJjaGl2ZXIvIiAvPgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmICFlbXB0eSgkcnNzaGVhZCl9LS0+JHJzc2hlYWQ8IS0tey9pZn0tLT4KCTwhLS17aWYgd2lkdGhhdXRvKCl9LS0+CgkJPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBpZD0iY3NzX3dpZHRoYXV0byIgdHlwZT0idGV4dC9jc3MiIGhyZWY9J3skX0dbJ3NldHRpbmcnXVsnY3NzcGF0aCddfXtTVFlMRUlEfV93aWR0aGF1dG8uY3NzP3tWRVJIQVNIfScgLz4KCQk8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+SFRNTE5PREUuY2xhc3NOYW1lICs9ICcgd2lkdGhhdXRvJzwvc2NyaXB0PgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmICRfR1snYmFzZXNjcmlwdCddID09ICdmb3J1bScgfHwgJF9HWydiYXNlc2NyaXB0J10gPT0gJ2dyb3VwJ30tLT4KCQk8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSJ7JF9HW3NldHRpbmddW2pzcGF0aF19Zm9ydW0uanM/e1ZFUkhBU0h9Ij48L3NjcmlwdD4KCTwhLS17ZWxzZWlmICRfR1snYmFzZXNjcmlwdCddID09ICdob21lJ30tLT4KCQk8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSJ7JF9HW3NldHRpbmddW2pzcGF0aF19aG9tZS5qcz97VkVSSEFTSH0iPjwvc2NyaXB0PgoJPCEtLXtlbHNlaWYgJF9HWydiYXNlc2NyaXB0J10gPT0gJ3BvcnRhbCd9LS0+CgkJPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ieyRfR1tzZXR0aW5nXVtqc3BhdGhdfXBvcnRhbC5qcz97VkVSSEFTSH0iPjwvc2NyaXB0PgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmICRfR1snYmFzZXNjcmlwdCddICE9ICdwb3J0YWwnICYmICRfR0VUWydkaXknXSA9PSAneWVzJyAmJiBjaGVja19kaXlfcGVybSgkdG9waWMpfS0tPgoJCTxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9InskX0dbc2V0dGluZ11banNwYXRoXX1wb3J0YWwuanM/e1ZFUkhBU0h9Ij48L3NjcmlwdD4KCTwhLS17L2lmfS0tPgoJPCEtLXtpZiAkX0dFVFsnZGl5J10gPT0gJ3llcycgJiYgY2hlY2tfZGl5X3Blcm0oJHRvcGljKX0tLT4KCQk8bGluayByZWw9InN0eWxlc2hlZXQiIHR5cGU9InRleHQvY3NzIiBpZD0iZGl5X2NvbW1vbiIgaHJlZj0ieyRfR1snc2V0dGluZyddWydjc3NwYXRoJ119e1NUWUxFSUR9X2Nzc19kaXkuY3NzP3tWRVJIQVNIfSIgLz4KCTwhLS17L2lmfS0tPgo8L2hlYWQ+Cgo8Ym9keSBpZD0ibnZfeyRfR1tiYXNlc2NyaXB0XX0iIGNsYXNzPSJwZ197Q1VSTU9EVUxFfXtpZiAkX0dbJ2Jhc2VzY3JpcHQnXSA9PT0gJ3BvcnRhbCcgJiYgQ1VSTU9EVUxFID09PSAnbGlzdCcgJiYgIWVtcHR5KCRjYXQpfSB7JGNhdFsnYm9keWNzcyddfXsvaWZ9IiBvbmtleWRvd249ImlmKGV2ZW50LmtleUNvZGU9PTI3KSByZXR1cm4gZmFsc2U7Ij4KCTxkaXYgaWQ9ImFwcGVuZF9wYXJlbnQiPjwvZGl2PjxkaXYgaWQ9ImFqYXh3YWl0aWQiPjwvZGl2PgoJPCEtLXtpZiAkX0dFVFsnZGl5J10gPT0gJ3llcycgJiYgY2hlY2tfZGl5X3Blcm0oJHRvcGljKX0tLT4KCQk8IS0te3RlbXBsYXRlIGNvbW1vbi9oZWFkZXJfZGl5fS0tPgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmIGNoZWNrX2RpeV9wZXJtKCR0b3BpYyl9LS0+CgkJPCEtLXt0ZW1wbGF0ZSBjb21tb24vaGVhZGVyX2RpeW5hdn0tLT4KCTwhLS17L2lmfS0tPgoJPCEtLXtpZiBDVVJNT0RVTEUgPT0gJ3RvcGljJyAmJiAkdG9waWMgJiYgZW1wdHkoJHRvcGljWyd1c2VoZWFkZXInXSkgJiYgY2hlY2tfZGl5X3Blcm0oJHRvcGljKX0tLT4KCQkkZGl5bmF2Cgk8IS0tey9pZn0tLT4KCTwhLS17aWYgZW1wdHkoJHRvcGljKSB8fCAkdG9waWNbJ3VzZWhlYWRlciddfS0tPgoJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ21vYmlsZSddWydhbGxvd21vYmlsZSddICYmICghJF9HWydzZXR0aW5nJ11bJ2NhY2hlaW5kZXhsaWZlJ10gJiYgISRfR1snc2V0dGluZyddWydjYWNoZXRocmVhZG9uJ10gfHwgJF9HWyd1aWQnXSkgJiYgKCRfR0VUWydkaXknXSAhPSAneWVzJyB8fCAhJF9HRVRbJ2luYWpheCddKSAmJiAoJF9HWydtb2JpbGUnXSAhPSAnJyAmJiAkX0dbJ2Nvb2tpZSddWydtb2JpbGUnXSA9PSAnJyAmJiAkX0dFVFsnbW9iaWxlJ10gIT0gJ25vJyl9LS0+CgkJCTxkaXYgY2xhc3M9InhpMSBibSBibV9jIj4KCQkJICAgIHtsYW5nIHlvdXJfbW9iaWxlX2Jyb3dzZXJ9PGEgaHJlZj0ieyRfR1snc2l0ZXVybCddfWZvcnVtLnBocD9tb2JpbGU9eWVzIj57bGFuZyBnb190b19tb2JpbGV9PC9hPiA8c3BhbiBjbGFzcz0ieGcxIj58PC9zcGFuPiA8YSBocmVmPSIkX0dbJ3NldHRpbmcnXVsnbW9iaWxlJ11bJ25vbW9iaWxldXJsJ10iPntsYW5nIHRvX2JlX2NvbnRpbnVlfTwvYT4KCQkJPC9kaXY+CgkJPCEtLXsvaWZ9LS0+CgkJPCEtLXtpZiAhZW1wdHkoJF9HWydzZXR0aW5nJ11bJ3Nob3J0Y3V0J10pICYmICRfR1snbWVtYmVyJ11bY3JlZGl0c10gPj0gJF9HWydzZXR0aW5nJ11bJ3Nob3J0Y3V0J119LS0+CgkJCTxkaXYgaWQ9InNob3J0Y3V0Ij4KCQkJCTxzcGFuPjxhIGhyZWY9ImphdmFzY3JpcHQ6OyIgaWQ9InNob3J0Y3V0Y2xvc2VpZCIgdGl0bGU9IntsYW5nIGNsb3NlfSI+e2xhbmcgY2xvc2V9PC9hPjwvc3Bhbj4KCQkJCXtsYW5nIHNob3J0Y3V0X25vdGljZX0KCQkJCTxhIGhyZWY9ImphdmFzY3JpcHQ6OyIgaWQ9InNob3J0Y3V0dGlwIj57bGFuZyBzaG9ydGN1dF9hZGR9PC9hPgoKCQkJPC9kaXY+CgkJCTxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij5zZXRUaW1lb3V0KHNldFNob3J0Y3V0LCAyMDAwKTs8L3NjcmlwdD4KCQk8IS0tey9pZn0tLT4KCQk8ZGl2IGlkPSJ0b3B0YiIgY2xhc3M9ImNsIj4KCQkJPCEtLXtob29rL2dsb2JhbF9jcG5hdl90b3B9LS0+CgkJCTxkaXYgY2xhc3M9IndwIj4KCQkJCTxkaXYgY2xhc3M9InoiPgoJCQkJCTwhLS17bG9vcCAkX0dbJ3NldHRpbmcnXVsndG9wbmF2cyddWzBdICRuYXZ9LS0+CgkJCQkJCTwhLS17aWYgaXNfYXJyYXkoJG5hdikgJiYgJG5hdlsnYXZhaWxhYmxlJ10gJiYgKCEkbmF2WydsZXZlbCddIHx8ICgkbmF2WydsZXZlbCddID09IDEgJiYgJF9HWyd1aWQnXSkgfHwgKCRuYXZbJ2xldmVsJ10gPT0gMiAmJiAkX0dbJ2FkbWluaWQnXSA+IDApIHx8ICgkbmF2WydsZXZlbCddID09IDMgJiYgJF9HWydhZG1pbmlkJ10gPT0gMSkpfS0tPiRuYXZbY29kZV08IS0tey9pZn0tLT4KCQkJCQk8IS0tey9sb29wfS0tPgoJCQkJCTwhLS17aG9vay9nbG9iYWxfY3BuYXZfZXh0cmExfS0tPgoJCQkJPC9kaXY+CgkJCQk8ZGl2IGNsYXNzPSJ5Ij4KCQkJCQk8YSBpZD0ic3dpdGNoYmxpbmQiIGhyZWY9ImphdmFzY3JpcHQ6OyIgb25jbGljaz0idG9nZ2xlQmxpbmQodGhpcykiIHRpdGxlPSJ7bGFuZyBzd2l0Y2hfYmxpbmR9IiBjbGFzcz0ic3dpdGNoYmxpbmQiPjwvYT4KCQkJCQk8IS0te2hvb2svZ2xvYmFsX2NwbmF2X2V4dHJhMn0tLT4KCQkJCQk8IS0te2xvb3AgJF9HWydzZXR0aW5nJ11bJ3RvcG5hdnMnXVsxXSAkbmF2fS0tPgoJCQkJCQk8IS0te2lmIGlzX2FycmF5KCRuYXYpICYmICRuYXZbJ2F2YWlsYWJsZSddICYmICghJG5hdlsnbGV2ZWwnXSB8fCAoJG5hdlsnbGV2ZWwnXSA9PSAxICYmICRfR1sndWlkJ10pIHx8ICgkbmF2WydsZXZlbCddID09IDIgJiYgJF9HWydhZG1pbmlkJ10gPiAwKSB8fCAoJG5hdlsnbGV2ZWwnXSA9PSAzICYmICRfR1snYWRtaW5pZCddID09IDEpKX0tLT4kbmF2W2NvZGVdPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXsvbG9vcH0tLT4KCQkJCQk8IS0te2lmIGVtcHR5KCRfR1snZGlzYWJsZWR3aWR0aGF1dG8nXSkgJiYgJF9HWydzZXR0aW5nJ11bJ3N3aXRjaHdpZHRoYXV0byddfS0tPgoJCQkJCQk8YSBocmVmPSJqYXZhc2NyaXB0OjsiIGlkPSJzd2l0Y2h3aWR0aCIgb25jbGljaz0id2lkdGhhdXRvKHRoaXMpIiB0aXRsZT0ie2lmIHdpZHRoYXV0bygpfXtsYW5nIHN3aXRjaF9uYXJyb3d9e2Vsc2V9e2xhbmcgc3dpdGNoX3dpZGV9ey9pZn0iIGNsYXNzPSJzd2l0Y2h3aWR0aCI+PCEtLXtpZiB3aWR0aGF1dG8oKX0tLT57bGFuZyBzd2l0Y2hfbmFycm93fTwhLS17ZWxzZX0tLT57bGFuZyBzd2l0Y2hfd2lkZX08IS0tey9pZn0tLT48L2E+CgkJCQkJPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXtpZiAkX0dbJ3VpZCddICYmICFlbXB0eSgkX0dbJ3N0eWxlJ11bJ2V4dHN0eWxlJ10pfS0tPjxhIGlkPSJzc2xjdCIgaHJlZj0iamF2YXNjcmlwdDo7IiBvbm1vdXNlb3Zlcj0iZGVsYXlTaG93KHRoaXMsIGZ1bmN0aW9uKCkge3Nob3dNZW51KHsnY3RybGlkJzonc3NsY3QnLCdwb3MnOiczNCEnfSl9KTsiPntsYW5nIGNoYW5nZXN0eWxlfTwvYT48IS0tey9pZn0tLT4KCQkJCQk8IS0te2lmIGNoZWNrX2RpeV9wZXJtKCR0b3BpYyl9LS0+CgkJCQkJCSRkaXluYXYKCQkJCQk8IS0tey9pZn0tLT4KCQkJCTwvZGl2PgoJCQk8L2Rpdj4KCQk8L2Rpdj4KCgkJPCEtLXtpZiAhSVNfUk9CT1R9LS0+CgkJCTwhLS17aWYgJF9HWyd1aWQnXSAmJiAhJF9HWydzZXR0aW5nJ11bJ2JiY2xvc2VkJ10gJiYgZW1wdHkoJF9HWydtZW1iZXInXVsnZnJlZXplJ10pICYmICRfR1snbWVtYmVyJ11bJ2dyb3VwaWQnXSAhPSA1fS0tPgoJCQk8dWwgaWQ9Im15cHJvbXB0X21lbnUiIGNsYXNzPSJwX3BvcCIgc3R5bGU9ImRpc3BsYXk6IG5vbmU7Ij4KCQkJCTxsaT48YSBocmVmPSJob21lLnBocD9tb2Q9c3BhY2UmZG89cG0iIGlkPSJwbV9udGMiIHN0eWxlPSJiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0OyBiYWNrZ3JvdW5kLXBvc2l0aW9uOiAwIDUwJTsiPjxlbSBjbGFzcz0icHJvbXB0X25ld3N7aWYgZW1wdHkoJF9HW21lbWJlcl1bbmV3cG1dKX1fMHsvaWZ9Ij48L2VtPntsYW5nIHBtX2NlbnRlcn08L2E+PC9saT4KCQkJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ2ZvbGxvd3N0YXR1cyddfS0tPgoJCQkJCTxsaT48YSBocmVmPSJob21lLnBocD9tb2Q9Zm9sbG93JmRvPWZvbGxvd2VyIj48ZW0gY2xhc3M9InByb21wdF9mb2xsb3dlcntpZiBlbXB0eSgkX0dbbWVtYmVyXVtuZXdwcm9tcHRfbnVtXVtmb2xsb3dlcl0pfV8wey9pZn0iPjwvZW0+PCEtLXtsYW5nIG5vdGljZV9pbnRlcmFjdGl2ZV9mb2xsb3dlcn0tLT57aWYgJF9HW21lbWJlcl1bbmV3cHJvbXB0X251bV1bZm9sbG93ZXJdfSgkX0dbbWVtYmVyXVtuZXdwcm9tcHRfbnVtXVtmb2xsb3dlcl0pey9pZn08L2E+PC9saT4KCQkJCQk8IS0te2lmICRfR1ttZW1iZXJdW25ld3Byb21wdF0gJiYgJF9HW21lbWJlcl1bbmV3cHJvbXB0X251bV1bZm9sbG93XX0tLT4KCQkJCQkJPGxpPjxhIGhyZWY9ImhvbWUucGhwP21vZD1mb2xsb3ciPjxlbSBjbGFzcz0icHJvbXB0X2NvbmNlcm4iPjwvZW0+PCEtLXtsYW5nIG5vdGljZV9pbnRlcmFjdGl2ZV9mb2xsb3d9LS0+KCRfR1ttZW1iZXJdW25ld3Byb21wdF9udW1dW2ZvbGxvd10pPC9hPjwvbGk+CgkJCQkJPCEtLXsvaWZ9LS0+CgkJCQk8IS0tey9pZn0tLT4KCQkJCTwhLS17aWYgJF9HW21lbWJlcl1bbmV3cHJvbXB0XX0tLT4KCQkJCQk8IS0te2xvb3AgJF9HWydtZW1iZXInXVsnY2F0ZWdvcnlfbnVtJ10gJGtleSAkdmFsfS0tPgoJCQkJCQk8bGk+PGEgaHJlZj0iaG9tZS5waHA/bW9kPXNwYWNlJmRvPW5vdGljZSZ2aWV3PSRrZXkiPjxlbSBjbGFzcz0ibm90aWNlXyRrZXkiPjwvZW0+PCEtLXtlY2hvIGxhbmcoJ3RlbXBsYXRlJywgJ25vdGljZV8nLiRrZXkpfS0tPig8c3BhbiBjbGFzcz0icnEiPiR2YWw8L3NwYW4+KTwvYT48L2xpPgoJCQkJCTwhLS17L2xvb3B9LS0+CgkJCQk8IS0tey9pZn0tLT4KCQkJCTwhLS17aWYgZW1wdHkoJF9HWydjb29raWUnXVsnaWdub3JlX25vdGljZSddKX0tLT4KCQkJCQk8bGkgY2xhc3M9Imlnbm9yZV9ub3RpY2VsaSI+PGEgaHJlZj0iamF2YXNjcmlwdDo7IiBvbmNsaWNrPSJzZXRjb29raWUoJ2lnbm9yZV9ub3RpY2UnLCAxKTtoaWRlTWVudSgnbXlwcm9tcHRfbWVudScpIiB0aXRsZT0ie2xhbmcgdGVtcG9yYXJpbHlfdG9fcmVtaW5kfSI+PGVtIGNsYXNzPSJpZ25vcmVfbm90aWNlIj48L2VtPjwvYT48L2xpPgoJCQkJPCEtLXsvaWZ9LS0+CgkJCTwvdWw+CgkJCTwhLS17L2lmfS0tPgoJCQk8IS0te2lmICRfR1sndWlkJ10gJiYgIWVtcHR5KCRfR1snc3R5bGUnXVsnZXh0c3R5bGUnXSl9LS0+CgkJCQk8ZGl2IGlkPSJzc2xjdF9tZW51IiBjbGFzcz0iY2wgcF9wb3AiIHN0eWxlPSJkaXNwbGF5OiBub25lOyI+CgkJCQkJPCEtLXtpZiAhJF9HW3N0eWxlXVtkZWZhdWx0ZXh0c3R5bGVdfS0tPjxzcGFuIGNsYXNzPSJzc2xjdF9idG4iIG9uY2xpY2s9ImV4dHN0eWxlKCcnKSIgdGl0bGU9IntsYW5nIGRlZmF1bHR9Ij48aT48L2k+PC9zcGFuPjwhLS17L2lmfS0tPgoJCQkJCTwhLS17bG9vcCAkX0dbJ3N0eWxlJ11bJ2V4dHN0eWxlJ10gJGV4dHN0eWxlfS0tPgoJCQkJCQk8c3BhbiBjbGFzcz0ic3NsY3RfYnRuIiBvbmNsaWNrPSJleHRzdHlsZSgnJGV4dHN0eWxlWzBdJykiIHRpdGxlPSIkZXh0c3R5bGVbMV0iPjxpIHN0eWxlPSdiYWNrZ3JvdW5kOiRleHRzdHlsZVsyXSc+PC9pPjwvc3Bhbj4KCQkJCQk8IS0tey9sb29wfS0tPgoJCQkJPC9kaXY+CgkJCTwhLS17L2lmfS0tPgoJCQk8IS0te2lmICRfR1sndWlkJ119LS0+CgkJCQk8dWwgaWQ9Im15aXRlbV9tZW51IiBjbGFzcz0icF9wb3AiIHN0eWxlPSJkaXNwbGF5OiBub25lOyI+CgkJCQkJPCEtLXtpZiAkX0dbJ3NldHRpbmcnXVsnZm9ydW1zdGF0dXMnXX0tLT48bGk+PGEgaHJlZj0iaG9tZS5waHA/bW9kPXNwYWNlJmRvPXRocmVhZCZ2aWV3PW1lIj57bGFuZyBteXBvc3R9PC9hPjwvbGk+PCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXtpZiAkX0dbJ3NldHRpbmcnXVsnZmF2b3JpdGVzdGF0dXMnXX0tLT48bGk+PGEgaHJlZj0iaG9tZS5waHA/bW9kPXNwYWNlJmRvPWZhdm9yaXRlJnZpZXc9bWUiPntsYW5nIGZhdm9yaXRlfTwvYT48L2xpPjwhLS17L2lmfS0tPgoJCQkJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ2ZyaWVuZHN0YXR1cyddfS0tPjxsaT48YSBocmVmPSJob21lLnBocD9tb2Q9c3BhY2UmZG89ZnJpZW5kIj57bGFuZyBmcmllbmRzfTwvYT48L2xpPjwhLS17L2lmfS0tPgoJCQkJCTwhLS17aG9vay9nbG9iYWxfbXlpdGVtX2V4dHJhfS0tPgoJCQkJPC91bD4KCQkJPCEtLXsvaWZ9LS0+CgkJCTwhLS17c3VidGVtcGxhdGUgY29tbW9uL2hlYWRlcl9xbWVudX0tLT4KCQk8IS0tey9pZn0tLT4KCgkJPCEtLXthZC9oZWFkZXJiYW5uZXIvd3AgYV9ofS0tPgoJCTxkaXYgaWQ9ImhkIj4KCQkJPGRpdiBjbGFzcz0id3AiPgoJCQkJPGRpdiBjbGFzcz0iaGRjIGNsIj4KCQkJCQk8IS0te2V2YWwgJG1uaWQgPSBnZXRjdXJyZW50bmF2KCk7fS0tPgoJCQkJCTxoMj48IS0te2lmICFpc3NldCgkX0dbJ3NldHRpbmcnXVsnbmF2bG9nb3MnXVskbW5pZF0pfS0tPjxhIGhyZWY9IntpZiAkX0dbJ3NldHRpbmcnXVsnZG9tYWluJ11bJ2FwcCddWydkZWZhdWx0J119eyRfR1snc2NoZW1lJ119Oi8veyRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2RlZmF1bHQnXX0ve2Vsc2V9Li97L2lmfSIgdGl0bGU9IiRfR1snc2V0dGluZyddWydiYm5hbWUnXSI+eyRfR1snc3R5bGUnXVsnYm9hcmRsb2dvJ119PC9hPjwhLS17ZWxzZX0tLT4kX0dbJ3NldHRpbmcnXVsnbmF2bG9nb3MnXVskbW5pZF08IS0tey9pZn0tLT48L2gyPgoJCQkJCTwhLS17dGVtcGxhdGUgY29tbW9uL2hlYWRlcl91c2Vyc3RhdHVzfS0tPgoJCQkJPC9kaXY+CgoJCQkJPGRpdiBpZD0ibnYiPgoJCQkJCTxhIGhyZWY9ImphdmFzY3JpcHQ6OyIgaWQ9InFtZW51IiBvbm1vdXNlb3Zlcj0iZGVsYXlTaG93KHRoaXMsIGZ1bmN0aW9uICgpIHtzaG93TWVudSh7J2N0cmxpZCc6J3FtZW51JywncG9zJzonMzQhJywnY3RybGNsYXNzJzonYScsJ2R1cmF0aW9uJzoyfSk7c2hvd0ZvcnVtbWVudSgkX0dbZmlkXSk7fSkiPntsYW5nIG15X25hdn08L2E+CgkJCQkJPHVsPgoJCQkJCQk8IS0te2xvb3AgJF9HWydzZXR0aW5nJ11bJ25hdnMnXSAkbmF2fS0tPgoJCQkJCQkJPCEtLXtpZiBpc19hcnJheSgkbmF2KSAmJiAkbmF2WydhdmFpbGFibGUnXSAmJiAoISRuYXZbJ2xldmVsJ10gfHwgKCRuYXZbJ2xldmVsJ10gPT0gMSAmJiAkX0dbJ3VpZCddKSB8fCAoJG5hdlsnbGV2ZWwnXSA9PSAyICYmICRfR1snYWRtaW5pZCddID4gMCkgfHwgKCRuYXZbJ2xldmVsJ10gPT0gMyAmJiAkX0dbJ2FkbWluaWQnXSA9PSAxKSl9LS0+PGxpIHtpZiAkbW5pZCA9PSAkbmF2W25hdmlkXSB8fCBzdWJzdHIoJF9TRVJWRVJbJ1JFUVVFU1RfVVJJJ10sIDEpID09IHN0cl9yZXBsYWNlKCcuLycsICcnLCAkbmF2W2ZpbGVuYW1lXSl9Y2xhc3M9ImEiIHsvaWZ9JG5hdltuYXZdPjwvbGk+PCEtLXsvaWZ9LS0+CgkJCQkJCTwhLS17L2xvb3B9LS0+CgkJCQkJPC91bD4KCQkJCQk8IS0te2hvb2svZ2xvYmFsX25hdl9leHRyYX0tLT4KCQkJCTwvZGl2PgoJCQkJPCEtLXtpZiAhZW1wdHkoJF9HWydzZXR0aW5nJ11bJ3BsdWdpbnMnXVsnanNtZW51J10pfS0tPgoJCQkJCTx1bCBjbGFzcz0icF9wb3AgaF9wb3AiIGlkPSJwbHVnaW5fbWVudSIgc3R5bGU9ImRpc3BsYXk6IG5vbmUiPgoJCQkJCTwhLS17bG9vcCAkX0dbJ3NldHRpbmcnXVsncGx1Z2lucyddWydqc21lbnUnXSAkbW9kdWxlfS0tPgoJCQkJCQkgPCEtLXtpZiBpbl9hcnJheSgkbW9kdWxlWydhZG1pbmlkJ10sIGFycmF5KDAsIC0xKSkgfHwgKCRtb2R1bGVbJ2FkbWluaWQnXSAmJiAkX0dbJ2FkbWluaWQnXSA+IDAgJiYgJG1vZHVsZVsnYWRtaW5pZCddID49ICRfR1snYWRtaW5pZCddKX0tLT4KCQkJCQkJIDxsaT4kbW9kdWxlW3VybF08L2xpPgoJCQkJCQkgPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXsvbG9vcH0tLT4KCQkJCQk8L3VsPgoJCQkJPCEtLXsvaWZ9LS0+CgkJCQkkX0dbc2V0dGluZ11bbWVudW5hdnNdCgkJCQk8ZGl2IGlkPSJtdSIgY2xhc3M9ImNsIj4KCQkJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ3N1Ym5hdnMnXX0tLT4KCQkJCQk8IS0te2xvb3AgJF9HW3NldHRpbmddW3N1Ym5hdnNdICRuYXZpZCAkc3VibmF2fS0tPgoJCQkJCQk8IS0te2lmICRfR1snc2V0dGluZyddWyduYXZzdWJob3ZlciddIHx8ICRtbmlkID09ICRuYXZpZH0tLT4KCQkJCQkJPHVsIGNsYXNzPSJjbCB7aWYgJG1uaWQgPT0gJG5hdmlkfWN1cnJlbnR7L2lmfSIgaWQ9InNuYXZfJG5hdmlkIntpZiAkbW5pZCAhPSAkbmF2aWR9IHN0eWxlPSJkaXNwbGF5Om5vbmUiey9pZn0+CgkJCQkJCSRzdWJuYXYKCQkJCQkJPC91bD4KCQkJCQkJPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXsvbG9vcH0tLT4KCQkJCTwhLS17L2lmfS0tPgoJCQkJPC9kaXY+CgkJCQk8IS0te2FkL3N1Ym5hdmJhbm5lci9hX211fS0tPgoJCQkJPCEtLXtzdWJ0ZW1wbGF0ZSBjb21tb24vcHVic2VhcmNoZm9ybX0tLT4KCQkJPC9kaXY+CgkJPC9kaXY+CgoJCTwhLS17aG9vay9nbG9iYWxfaGVhZGVyfS0tPgoJPCEtLXsvaWZ9LS0+CgoJPGRpdiBpZD0id3AiIGNsYXNzPSJ3cCI+Cg==
qidao123.com ToB IT社区-企服评测·应用市场»论坛 › 主机系统 › linux › C++网络编程之利用Boost库搭建简单的异步服务器 ...
返回列表 发新帖

C++网络编程之利用Boost库搭建简单的异步服务器

[复制链接]
发表于 2024-8-11 11:15:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
C++网络编程之利用Boost库搭建简单的异步服务器

技能概述



  • 做什么的:C++ Boost库是一个开源的C++软件库集合,旨在提升C++的标准库的性能和功能。在C++编程中,Boost库通常用于解决一些通用但复杂的题目。
  • 应用:网络通信、异步IO操作、跨平台方案、进步完成任务效率。
  • 学习动机:扩展标准库的功能、进步开发效率。
  • 技能难点:API复杂,学习成本高;相对于主流技能,学习资源少。
技能详述

配置

由于主要在Windows下编程训练,所以只先容Windows下如和配置Boost库。
下载库



  • Boost库官网下载

  • 解压后文件夹下有个一个bootstrap.bat文件,双击运行会生成b2.exe

  • 然后在boost文件夹下启动cmd,执行 “.\b2.exe toolset=msvc”。执行编译事后,会在stage文件夹下生成lib文件夹,内里就是我们要用到的lib库。
项目中配置Boost库



  • 右键点击项目,选择“属性”打开项目的属性页。在VC++的包含目次中配置boost库的目次位置(就是下载后解压的位置),库目次配置boost库中的stage\lib即可。

  • 或者可以打开属性管理器,自建属性页配置(自建属性页配置好后会有一个文件,可以复用到其他项目,自建属性页所在目次要和)

注意,自建属性页目次要和当前项目的版本同等,如当前是Debug的x64程序,自建属性页就要在Debug | x64目次下。

末了的配置过程和配置项目的属性页一样。
利用Boost库

俗话说得好,饭要一口一口吃,所以先从Boost库中网络编程的比较主要和重要的开始利用。演示程序就是一个回显客户端消息的服务器。架构也很简单。下面我将自顶向下的先容怎样完成其编写及其细节。在此之前,必须要注意到的是,异步操作的执行时机总是不确定,所以代码中会大量地用到shared_ptr等智能指针来主动托管一个对象的生命周期。
基本架构


main

  1. #include "Server.h"
  2. int main(void) {
  3.         try {
  4.         //创建一个boost库的IO服务,这是boost使用库进行各种IO操作的核心,io_context的旧版本是io_service
  5.                 boost::asio::io_context ioc;
  6.         //实例化化Server类
  7.                 Server server(ioc, 10086);
  8.         //启动IO服务
  9.                 ioc.run();
  10.         }
  11.         catch (std::exception& e) {
  12.                 std::cerr << "Exception: " << e.what() << std::endl;
  13.         }
  14.         return 0;
  15. }
复制代码
Server

头文件

  1. #pragma once
  2. #include <boost/asio.hpp>
  3. #include <iostream>
  4. #include "Session.h"
  5. #include <memory>
  6. #include <map>
  7. #include "Session.h"
  8. class Session;
  9. class Server
  10. {
  11. public:
  12.     //构造函数,引用一个IO服务,同时指定监听的端口
  13.         Server(boost::asio::io_context& ioc, unsigned short port);
  14.     //按照Session的uuid删除uuid
  15.         void ClearSession(const std::string& uuid) {
  16.                 _sessions.erase(uuid);
  17.         }
  18. private:
  19.     //开始一个异步的监听socket连接
  20.         void start_accept();
  21.     //处理收到的socket连接
  22.         void handle_accept(std::shared_ptr<Session> new_session, const boost::system::error_code& ec);
  23.        
  24.     //io_context不支持拷贝构造,使用引用方式
  25.         boost::asio::io_context& _ioc;
  26.     //用于监听连接
  27.         boost::asio::ip::tcp::acceptor _acceptor;
  28.         //通过智能指针延长Session对象的生命周期
  29.         std::map<std::string, std::shared_ptr<Session>> _sessions;
  30. };
复制代码
定义代码

  1. #include "Server.h"
  2. //注意这里的_acceptor的构造方式,接受一个io_context,以及一个端点。这个端点通过一个默认可用的ip和指定端口进行构造。
  3. Server::Server(boost::asio::io_context& ioc, unsigned short port): _ioc(ioc),
  4.         _acceptor(ioc, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) {
  5.         std::cout << "Server starts on success, using port: " << port << std::endl;
  6.     //启动监听
  7.     //这么说不是一个严谨的说法,稍后将看到为什么
  8.         start_accept();
  9. }
  10. void Server::start_accept() {
  11.         //创建一个Session,注意到这里的构造又用到了io_context
  12.         auto new_session = std::make_shared<Session>(_ioc, this);
  13.     //这里_acceptor执行异步的accept函数
  14.     //这里做的事情不是accept,而是往io_context中注册一个accept事件,以及这个事件的处理函数
  15.         _acceptor.async_accept(
  16.                 new_session->Socket(), //传入Session的socket,事件发生时,_acceptor会初始化成连接进来的socket
  17.                 std::bind( //修饰处理收到连接的方法
  18.                         &Server::handle_accept,
  19.                         this,
  20.                         new_session,
  21.                         std::placeholders::_1
  22.                 )
  23.         );
  24. }
  25. void Server::handle_accept(std::shared_ptr<Session> new_session, const boost::system::error_code& ec) {
  26.         if (ec) {
  27.                 std::cout << "Error occured in accepting. Errmessage: " << ec.message() << std::endl;
  28.                 //delete new_session;
  29.         }
  30.         else { //没有错误
  31.         //启动Session
  32.                 new_session->Start();
  33.                 _sessions.insert(std::make_pair(new_session->GetUuid(), new_session));
  34.         }
  35.        
  36.     //这里无论成功还是失败,都要重新注册一个监听事件给io_context
  37.     //以便能够继续监听连接
  38.         start_accept();
  39. }
复制代码
源程序

Session

在处置惩罚读写数据时,我们不知道客户端会发多少数据来,同时还要处置惩罚数据粘包的题目。所以服务器和服务端之间的通信采用tlv(type length value)的协议举行通信。由于是回显服务器,type可用省略,在处置惩罚读到的数据时,要先解析头部,再读取头部看客户端发来多少数据。
头文件

  1. #pragma once
  2. #include <iostream>
  3. #include <boost/asio.hpp>
  4. #include <boost/uuid/uuid_generators.hpp>
  5. #include <boost/uuid/uuid_io.hpp>
  6. #include "Server.h"
  7. #include <mutex>
  8. #include <queue>
  9. #include "MsgNode.h"
  10. #include <iomanip>
  11. class Server;
  12. class MsgNode;
  13. //注意到这里继承了std::enable_shared_from_this<Session>
  14. //这样可以使得类内部能够使用shared_from_this()统一获取this指针的shared_ptr,shared_ptr的唯一性,防止不同的shared_ptr托管同一个this指针的资源
  15. class Session: public std::enable_shared_from_this<Session>
  16. {
  17. public:
  18.     //构造函数
  19.         Session(boost::asio::io_context& ioc, Server* server);
  20.         boost::asio::ip::tcp::socket& Socket();
  21.     //启动读写
  22.         void Start();
  23.     //发送数据
  24.         void Send(char* buf, std::size_t len);
  25.     //返回uuid
  26.         const std::string& GetUuid() {
  27.                 return _uuid;
  28.         }
  29.         ~Session() {
  30.                 std::cout << "delete session: uuid=" << _uuid << std::endl;
  31.         }
  32. private:
  33.     //处理读事件
  34.         void HandleRead(const boost::system::error_code& ec, std::size_t bytes_transferred, std::shared_ptr<Session> _self_ptr);
  35.     //处理写事件
  36.         void HandleWrite(const boost::system::error_code& ec, std::shared_ptr<Session> _self_ptr);
  37.         boost::asio::ip::tcp::socket _socket;
  38.         enum {max_length = 1024};
  39.     //接受数据的缓冲区
  40.         char _data[max_length] = {0};
  41.     //发送队列
  42.         std::queue<std::shared_ptr<MsgNode>> _send_que;
  43.     //队列的锁
  44.         std::mutex _send_lock;
  45.     //用于标识连接
  46.         std::string _uuid;
  47.     //引用server,以便能够在读写出错时,清除自身的连接
  48.         Server* _server;
  49.     //接受消息的节点
  50.         std::shared_ptr<MsgNode> _recv_msg_node;
  51.     //标识消息头部是否解析完成
  52.         bool _b_head_parse;
  53.     //头部消息节点
  54.         std::shared_ptr<MsgNode> _recv_head_node;
  55. };
复制代码
定义代码

  1. #include "Session.h"
  2. Session::Session(boost::asio::io_context& ioc, Server* server) : _socket(ioc),
  3.         _server(server), _b_head_parse(false) {
  4.     //为Session生成uuid   
  5.         boost::uuids::uuid a_uuid = boost::uuids::random_generator()();
  6.         _uuid = boost::uuids::to_string(a_uuid);
  7.     //初始化消息头,通常头的长度是一个short类型的长度
  8.         _recv_head_node = std::make_shared<MsgNode>(HEAD_LENGTH);
  9. }
  10. //返回使用的socket
  11. boost::asio::ip::tcp::socket& Session::Socket() {
  12.         return _socket;
  13. }
  14. void Session::Start() {
  15.         //对象创建时已初始化_data
  16.     //往io_context注册读事件,区别于另一个async_read函数,这里是能读就返回
  17.         _socket.async_read_some(
  18.                 boost::asio::buffer( //构造一个buffer
  19.                         _data,
  20.                         max_length
  21.                 ),
  22.                 std::bind( //修饰处理
  23.                         &Session::HandleRead,
  24.                         this,
  25.                         std::placeholders::_1,
  26.                         std::placeholders::_2,
  27.                         //std::make_shared<Session>(this)导致引用计数不同步
  28.                         shared_from_this()
  29.                 )
  30.         );
  31. }
  32. void Session::Send(char* buf, std::size_t len) {
  33.     bool pending = false;
  34.         std::lock_guard<std::mutex> guard(_send_lock);
  35.     //如果在为加入消息节点之前,队列不为空,说明这个队列的消息正在发送(写完一个消息后,写事件的处理函数还会继续注册写事件,直到队列为空,所以,如果此时有消息,就说明后续还会继续回调写事件,不再需要在这里注册写事件)
  36.         if (_send_que.size() > 0) {
  37.                 pending = true;
  38.         }
  39.     //往队列投递消息
  40.         _send_que.emplace(std::make_shared<MsgNode>(buf, len));
  41.         if (pending) {
  42.                 return;
  43.         }
  44.     //注册写事件
  45.         boost::asio::async_write(
  46.                 _socket,
  47.                 boost::asio::buffer(
  48.                         buf,
  49.                         len
  50.                 ),
  51.                 std::bind(
  52.                         &Session::HandleWrite,
  53.                         this,
  54.                         std::placeholders::_1,
  55.                         shared_from_this()
  56.                 )
  57.         );
  58. }
  59. //由于这里使用async_read_some,一读到数据就返回,所以处理的过程比较繁琐。还可以可以使用async_read,读取指定字节在返回,其内部实现依然是异步的,和async_read_some一样高效。
  60. void Session::HandleRead(const boost::system::error_code& ec, std::size_t bytes_transferred, std::shared_ptr<Session> _self_ptr) {
  61.         if (ec) {
  62.                 std::cout << "Error occured in reading. ErrMessage: " << ec.message() << std::endl;
  63.        
  64.                 _server->ClearSession(_uuid);
  65.         }
  66.         else {
  67.                 int copy_len = 0;
  68.                 while (bytes_transferred > 0) {
  69.                         if (!_b_head_parse) {
  70.                                 //收到的数据不足头部大小
  71.                                 if (bytes_transferred + _recv_head_node->_cur_len < HEAD_LENGTH) {
  72.                                         memcpy(_recv_head_node->_msg + _recv_head_node->_cur_len, _data + copy_len, bytes_transferred);
  73.                                         _recv_head_node->_cur_len += bytes_transferred;
  74.                                         ::memset(_data, 0, max_length);
  75.                                         _socket.async_read_some(boost::asio::buffer(_data, max_length
  76.                                         ),
  77.                                                 std::bind(&Session::HandleRead, this, std::placeholders::_1, std::placeholders::_2, _self_ptr));
  78.                                         return;
  79.                                 }
  80.                                 //收到的数据比头部多
  81.                                 //头部剩余未复制的长度
  82.                                 int head_remain = HEAD_LENGTH - _recv_head_node->_cur_len;
  83.                                 memcpy(_recv_head_node->_msg + _recv_head_node->_cur_len, _data + copy_len, head_remain);
  84.                                 //更新已处理的data长度和剩余未处理的长度
  85.                                 copy_len += head_remain;
  86.                                 bytes_transferred -= head_remain;
  87.                                 //获取头部数据
  88.                                 int data_len = 0;
  89.                                 memcpy(&data_len, _recv_head_node->_msg, HEAD_LENGTH);
  90.                                 std::cout << "data_len is " << data_len << std::endl;
  91.                                 //头部长度非法
  92.                                 if (data_len > max_length) {
  93.                                         std::cout << "invalid data length is " << data_len << std::endl;
  94.                                         _server->ClearSession(_uuid);
  95.                                         return;
  96.                                 }
  97.                                 _recv_msg_node = std::make_shared<MsgNode>(data_len);
  98.                                 //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里
  99.                                 if (bytes_transferred < data_len) {
  100.                                         memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred);
  101.                                         _recv_msg_node->_cur_len += bytes_transferred;
  102.                                         ::memset(_data, 0, max_length);
  103.                                         _socket.async_read_some(boost::asio::buffer(_data, max_length),
  104.                                                 std::bind(&Session::HandleRead, this, std::placeholders::_1, std::placeholders::_2, _self_ptr));
  105.                                         //头部处理完成
  106.                                         _b_head_parse = true;
  107.                                         return;
  108.                                 }
  109.                                 memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, data_len);
  110.                                 _recv_msg_node->_cur_len += data_len;
  111.                                 copy_len += data_len;
  112.                                 bytes_transferred -= data_len;
  113.                                 _recv_msg_node->_msg[_recv_msg_node->_tot_len] = '\0';
  114.                                 std::cout << "receive data is " << _recv_msg_node->_msg << std::endl;
  115.                                 //此处可以调用Send发送测试
  116.                                 Send(_recv_msg_node->_msg, _recv_msg_node->_tot_len);
  117.                                 //继续轮询剩余未处理数据
  118.                                 _b_head_parse = false;
  119.                                 _recv_head_node->Clear();
  120.                                 if (bytes_transferred <= 0) {
  121.                                         ::memset(_data, 0, max_length);
  122.                                         _socket.async_read_some(boost::asio::buffer(_data, max_length),
  123.                                                 std::bind(&Session::HandleRead, this, std::placeholders::_1, std::placeholders::_2, _self_ptr));
  124.                                         return;
  125.                                 }
  126.                                 continue;
  127.                         }
  128.                         //已经处理完头部,处理上次未接受完的消息数据
  129.                         //接收的数据仍不足剩余未处理的
  130.                         int remain_msg = _recv_msg_node->_tot_len - _recv_msg_node->_cur_len;
  131.                         if (bytes_transferred < remain_msg) {
  132.                                 memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred);
  133.                                 _recv_msg_node->_cur_len += bytes_transferred;
  134.                                 ::memset(_data, 0, max_length);
  135.                                 _socket.async_read_some(boost::asio::buffer(_data, max_length),
  136.                                         std::bind(&Session::HandleRead, this, std::placeholders::_1, std::placeholders::_2, _self_ptr));
  137.                                 return;
  138.                         }
  139.                         memcpy(_recv_msg_node->_msg + _recv_msg_node->_cur_len, _data + copy_len, remain_msg);
  140.                         _recv_msg_node->_cur_len += remain_msg;
  141.                         bytes_transferred -= remain_msg;
  142.                         copy_len += remain_msg;
  143.                         _recv_msg_node->_msg[_recv_msg_node->_tot_len] = '\0';
  144.                         std::cout << "receive data is " << _recv_msg_node->_msg << std::endl;
  145.                         //此处可以调用Send发送测试
  146.                         Send(_recv_msg_node->_msg, _recv_msg_node->_tot_len);
  147.                         //继续轮询剩余未处理数据
  148.                         _b_head_parse = false;
  149.                         _recv_head_node->Clear();
  150.                         if (bytes_transferred <= 0) {
  151.                                 ::memset(_data, 0, max_length);
  152.                                 _socket.async_read_some(boost::asio::buffer(_data, max_length),
  153.                                         std::bind(&Session::HandleRead, this, std::placeholders::_1, std::placeholders::_2, _self_ptr));
  154.                                 return;
  155.                         }
  156.                         continue;
  157.                 }
  158.         }
  159. }
  160. //处理写事件
  161. void Session::HandleWrite(const boost::system::error_code& ec, std::shared_ptr<Session> _self_ptr) {
  162.         if (ec) {
  163.                 std::cout << "Error occured in writting. ErrMessage: " << ec.message() << std::endl;
  164.                 //delete this;
  165.                 _server->ClearSession(_uuid);
  166.         }
  167.         else {
  168.                 std::lock_guard<std::mutex> guard(_send_lock);
  169.                 _send_que.pop(); //使用async_write,保证发完指定的字节数
  170.                 if (!_send_que.empty()) {
  171.                         auto& msg = _send_que.front();
  172.                         boost::asio::async_write(
  173.                                 _socket,
  174.                                 boost::asio::buffer(
  175.                                         msg->_msg,
  176.                                         msg->_tot_len
  177.                                 ), //使用async_write,可以不做偏移
  178.                                 std::bind(
  179.                                         &Session::HandleWrite,
  180.                                         this,
  181.                                         std::placeholders::_1,
  182.                                         _self_ptr
  183.                                 )
  184.                         );
  185.                 }
  186.         }
  187. }
复制代码
MsgNode

  1. #pragma once
  2. #include <memory>
  3. #include "Session.h"
  4. const int HEAD_LENGTH = sizeof(int);
  5. class MsgNode
  6. {
  7. public:
  8.         friend class Session;
  9.         MsgNode(char* msg, int tot_len) :_cur_len(0), _tot_len(tot_len) {
  10.                 _msg = new char[_tot_len + 1];
  11.                 memcpy(_msg, &tot_len, HEAD_LENGTH);
  12.                 memcpy(_msg + HEAD_LENGTH, msg, _tot_len);
  13.                 _msg[_tot_len] = '\0';
  14.         }
  15.         MsgNode(int tot_len) : _cur_len(0), _tot_len(tot_len) {
  16.                 _msg = new char[_tot_len + 1];
  17.                 memset(_msg, 0, _tot_len + 1);
  18.         }
  19.         ~MsgNode() {
  20.                 delete[] _msg;
  21.                 _msg = nullptr;
  22.         }
  23.         void Clear() {
  24.                 memset(_msg, 0, _tot_len);
  25.                 _cur_len = 0;
  26.         }
  27. private:
  28.         char* _msg;
  29.         int _cur_len;
  30.         int _tot_len;
  31. };
复制代码
技能利用中碰到的题目息争决过程。

题目

实际上,在项目开始时尽管知道了异步函数执行的时机不能确定,但是怎样延长Session的生命周期使其恰到利益的和注册的读写事件同步,也就是读写事件发生时,Session必须有用,它的读写事件不会再注册后,必须回收它的资源。
解决过程



  • 利用shared_ptr管理Session,同时Session要注册到Server的_sessions。在Session堕落或者对端关闭socket的写端之后,再从Server中利用ClearSession()清除自己。
  • 仅仅这样还是不够的,为什么?因为这个毗连在仅仅在这个事件清除了自己,但是后续大概另有这个毗连的其他事件,这样会使后续事件碰到无效的对象。所以对于每个事件回调,还要拷贝这个Session的shared_ptr,以保证这个Session在这个事件的回调中是有用的。
举行总结。

根据自己的学习路线,这属于项目实战的内容。尽管只先容了一个简单的回显服务器,但是这个例子已经能够很好的体现异步服务器的层次和框架。
回复

使用道具 举报

返回列表 发新帖
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录

QQ|手机版|qidao123.com IT社区;IT企服评测▪应用市场 ( 浙ICP备20004199|浙ICP备20004199号 )|网站地图

GMT+8, 2026-10-18 06:27 , Processed in 0.022991 second(s), 8 queries , Redis On.

Powered by Discuz! X3.5 Licensed

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表