ð TOP / ã¬ã¬ã·ãŒC++ L19ïŒãããã¯ãŒã¯
ã¬ã¬ã·ãŒç¬¬19ç« ïŒãªã¢ã«ã¿ã€ã éä¿¡ãšãã±ããã·ãªã¢ã©ã€ãº
Berkeley Sockets (UDP/TCP) ã«ããèªæ©åæã»è£éã»ãšã³ãã£ã¢ã³å€æ
ã2å°ã®PCã§2人ååãã¬ã€ã€ã³ããŒããŒãäœãããïŒããé信察æŠã§æµã®äœçœ®ãã«ã¯ã«ã¯é£ãã ãã匟ãããæããŠããŸããââã¹ã¿ã³ãã¢ãã³ã²ãŒã ãããããã¯ãŒã¯ãã«ããã¬ã€ã€ãŒã²ãŒã ãžã®é²åã¯ãããã°ã©ãã«å
šãæ°ããæ¬¡å
ã®èª²é¡ãçªãã€ããŸããC++ã«ã¯é·å¹Žæšæºãããã¯ãŒã¯ã©ã€ãã©ãªãååšãããOSãã€ãã£ãã®ãBerkeley Sockets APIããçŽæ¥å©ããŠãã±ãããéåä¿¡ããã®ãçŸå Žã®å¿
é æè¡ã§ãããæ¬ç« ã§ã¯ããªã¢ã«ã¿ã€ã ã¢ã¯ã·ã§ã³ã«äžå¯æ¬ ãªUDPéä¿¡ãç°ãªãCPUã¢ãŒããã¯ãã£éã§ããŒã¿ãå£ããã®ãé²ããšã³ãã£ã¢ã³å€æãæ§é äœã®ã¢ã©ã€ã¡ã³ããããã³ã°ããããŠãã±ããé
å»¶ãè³å
ã§æã¡æ¶ããæšæž¬èªæ³ïŒDead ReckoningïŒãšç·åœ¢è£éïŒLerpïŒããç¶²çŸ
ããŸãã
L19.1 ãªãã¢ã¯ã·ã§ã³ã²ãŒã ã¯TCPãæšãŠãŠUDPãæ¡çšããã®ãïŒ
Webããã¡ã€ã«è»¢éã®åžžèãéçšããªãã²ãŒã éä¿¡ã®äžçãTCPã®èŽåœç匱ç¹ãããããªãã©ã€ã³ããããã³ã°ããæŽããŸãã
ð§ ãã³ã®ã³å
茩
ã·ãã¯ãå
çïŒ2人ãã¬ã€ã®éä¿¡ãäœãããšæã£ãŠèª¿ã¹ãŠããããTCPãšUDPãšãã2ã€ã®ãããã³ã«ãããããšãç¥ããŸãããTCPã¯ããã±ããã絶察ã«å±ãããšãä¿èšŒããŠãããããšæžããŠãã£ãã®ã§ãTCPã䜿ãã®ãäžçªå®å
šã§ãããïŒ
ð»ââïž ã·ãã¯ãå
ç
ããã«ã¢ãŒãŒãŒã³ïŒãªã¢ã«ã¿ã€ã ã¢ã¯ã·ã§ã³ã²ãŒã ãFPSã§TCPã䜿ãã®ã¯ãæé200kmã§èµ°ãã¬ãŒã·ã³ã°ã«ãŒã«éšïŒãããïŒãã€ãããããªãã®ããïŒ
ð§ ãã³ã®ã³å
茩
ããã£ïŒïŒçµ¶å¯Ÿã«å±ãã®ã«ãã¡ãªãã§ããïŒïŒ
ð»ââïž ã·ãã¯ãå
ç
TCPã¯ãéäžã®ãã±ããã1ã€ã§ãæ¬ èœïŒãã±ãã¹ïŒããããåéãæåãããŸã§åŸç¶ã®å
šãã±ããã®åŠçãã¹ããããããïŒHead-of-Line BlockingïŒããšããèŽåœçãªæåãããã®ããïŒ1ç§åã®å€ãèªæ©åº§æšãªã©ã²ãŒã ã§ã¯ãŽãåç¶ïŒææ°ã®åº§æšããå±ãã°å€ããã±ãããªã©æšãŠãŠæ§ããã®ããïŒ
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
å³ææ§ vs ä¿¡é Œæ§ã®ãã¬ãŒããªã : ãªã¢ã«ã¿ã€ã ã¢ã¯ã·ã§ã³ã²ãŒã ã§ã¯ãå€ãããŒã¿ã確å®ã«å±ããããšããããææ°ããŒã¿ãä»ããå±ãã峿æ§ãæåªå
ããããããããªãã©ã€ã³ããããã³ã°ã®åé¿ : TCPã®åéåŸ
ã¡ã«ããå
šåæ¢ãé¿ãããããã²ãŒã åæã«ã¯UDPãããŒã¹ã«æ¡çšããã®ãåçšãšã³ãžã³ã®æšæºã
L19.2 ãšã³ãã£ã¢ã³å€æãšãã€ããªãã±ãããããã³ã°
Intel CPUãšARM CPUã§éä¿¡ãããšæ°å€ãå転ããïŒïŒ ãã€ããªãŒããŒã®çœ ãšæ§é äœã¢ã©ã€ã¡ã³ãã®ééã
ð§ ãã³ã®ã³å
茩
PCã®ãµãŒããŒãããèªæ©ã®HPïŒå€: 1000ïŒãæ§é äœããš send() ã§éã£ããã§ãããã¹ããåŽã®ã¯ã©ã€ã¢ã³ãã§åãåã£ãã HP ã -286331153 ã¿ãããªå¥æªãªå·šå€§æ°å€ã«ãªã£ãŠå³æ»ããŸããïŒãªãã§ã§ããïŒïŒ
ð»ââïž ã·ãã¯ãå
ç
ãããäœã¬ã€ã€ãããã¯ãŒã¯ã®ç»ç«éãããšã³ãã£ã¢ã³ïŒãã€ããªãŒããŒïŒåé¡ãããïŒPCïŒx86ïŒã¯äžäœãã€ããã䞊ã¶ãªãã«ãšã³ãã£ã¢ã³ããããã¯ãŒã¯æšæºïŒããã³äžéšã®CPUïŒã¯äžäœãã€ããã䞊ã¶ããã°ãšã³ãã£ã¢ã³ãªã®ããïŒ
### ãšã³ãã£ã¢ã³ïŒãã€ãé ïŒã®ææ äŸãã° 32ãããæŽæ°ã0x12345678ãïŒ10鲿°ã§ 305419896ïŒãã¡ã¢ãªã«çœ®ããšãïŒ - **ãªãã«ãšã³ãã£ã¢ã³ïŒPC/x86/çŸä»£ã®ã¹ããïŒ**: [78] [56] [34] [12] ïŒäžäœãã€ãããé ã«æ ŒçŽïŒ - **ããã°ãšã³ãã£ã¢ã³ïŒãããã¯ãŒã¯ãã€ããªãŒããŒ/æ§PowerPCçïŒ**: [12] [34] [56] [78] ïŒäžäœãã€ãããé ã«æ ŒçŽïŒ ãããã¯ãŒã¯ã«æµãããŒã¿ã¯å¿
ãããããã¯ãŒã¯ãã€ããªãŒããŒïŒããã°ãšã³ãã£ã¢ã³ïŒãã«å€æããèŠæ Œã«ãªã£ãŠããŸãã ããã倿ããã®ã以äžã®æšæºé¢æ°ã§ãïŒ - htonl() : Host to Network Long (32bit) - ntohl() : Network to Host Long (32bit) - htons() : Host to Network Short (16bit) - ntohs() : Network to Host Short (16bit) ããã«ãæ§é äœããã®ãŸãŸãã€ããªéä¿¡ããå Žåãã³ã³ãã€ã©ãåæã«æããããã£ã³ã°ïŒééïŒããã#pragma pack(push, 1)ãã§è©°ããå¿
èŠããããŸãã
ð PacketDefinition.hpp
#ifndef PACKET_DEFINITION_HPP
#define PACKET_DEFINITION_HPP
#include <iostream>
#include <cstring>
#include <cstdint>
#if defined(_WIN32)
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
// æ§é äœã®ããã£ã³ã°ïŒã¢ã©ã€ã¡ã³ãã®ééïŒã匷å¶çã«1ãã€ãåäœã«è©°ãã
#pragma pack(push, 1)
// èªæ©ã»æµã®åº§æšåæãã±ãã
struct PlayerSyncPacket {
uint8_t packetType; // 1: äœçœ®åæ
uint32_t sequenceId; // ãã±ããéãçªå·
int16_t posX; // X座æš
int16_t posY; // Y座æš
uint8_t inputBits; // ããŒå
¥åãã©ã°ïŒL16ã®ããããã©ã°ïŒïŒ
};
#pragma pack(pop)
// ãã±ããã®ã·ãªã¢ã©ã€ãºïŒéä¿¡åã®ãã€ãé 倿ïŒ
inline void serializePlayerPacket(const PlayerSyncPacket& src, char* buffer) {
PlayerSyncPacket netPacket;
netPacket.packetType = src.packetType;
netPacket.sequenceId = htonl(src.sequenceId);
netPacket.posX = (int16_t)htons((uint16_t)src.posX);
netPacket.posY = (int16_t)htons((uint16_t)src.posY);
netPacket.inputBits = src.inputBits;
std::memcpy(buffer, &netPacket, sizeof(PlayerSyncPacket));
}
// ãã±ããã®ãã·ãªã¢ã©ã€ãºïŒåä¿¡åŸã®ãã¹ããã€ãé 倿ïŒ
inline void deserializePlayerPacket(const char* buffer, PlayerSyncPacket& dst) {
PlayerSyncPacket netPacket;
std::memcpy(&netPacket, buffer, sizeof(PlayerSyncPacket));
dst.packetType = netPacket.packetType;
dst.sequenceId = ntohl(netPacket.sequenceId);
dst.posX = (int16_t)ntohs((uint16_t)netPacket.posX);
dst.posY = (int16_t)ntohs((uint16_t)netPacket.posY);
dst.inputBits = netPacket.inputBits;
}
#endif
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
htonl / ntohl ã®åŸ¹åº : ãããã¯ãŒã¯ãæµããæŽæ°ããŒã¿ã¯å¿
ããããã¯ãŒã¯ãã€ãé ïŒããã°ãšã³ãã£ã¢ã³ïŒã«å€æããŠéåããã#pragma pack(push, 1) ã«ããæ§é äœãããã³ã° : ã³ã³ãã€ã©ãOSã«ãã£ãŠç°ãªãããã£ã³ã°ãã€ãã®æ··å
¥ãé²ããããéä¿¡çšãã±ããã¯ã¢ã©ã€ã¡ã³ãã1ãã€ãã«åºå®ããã
L19.3 ãã±ãã¹ãšé
å»¶ãè³å
ã§æ¶ãå»ããæšæž¬èªæ³ïŒDead ReckoningïŒã
ãããã¯ãŒã¯é
å»¶ã50ããªç§ãã£ãŠããæµãæ»ããã«åãå¥è·¡ã®ã¢ã«ãŽãªãºã ãç·åœ¢è£éïŒLerpïŒã®å®è£
ã
ð§ ãã³ã®ã³å
茩
ãã±ãããæ¯ç§20åããå±ããªãã®ã§ãçžæã®ãã¬ã€ã€ãŒã1ç§éã«20åã«ã¯ã«ã¯ããšç¬éç§»åããŠèŠããŠããŸããŸãâŠïŒããããçãæã¡ã§ããŸããïŒ
ð»ââïž ã·ãã¯ãå
ç
ãã±ãããå±ããç¬éã«ãã®åº§æšãžã¯ãŒããããŠã¯ãªããïŒéå»ã®åº§æšããçŸåšã®ç®æšåº§æšãžåãã£ãŠãæéããããŠæ»ããã«äžé座æšãèšç®ãããç·åœ¢è£éïŒLerpïŒLinear InterpolationïŒãã䜿ãã®ããïŒ
ð»ââïž ã·ãã¯ãå
ç
ããã«ããã±ãããéåããéããçžæã¯åã®é床ãã¯ãã«ãç¶æããŠé²ãã§ããã¯ãã ããšæªæ¥ã®äœçœ®ãå
èªã¿èšç®ãããæšæž¬èªæ³ïŒDead ReckoningïŒããçµã¿åãããã°ãé
å»¶ããã¬ã€ã€ãŒã«å
šãæ°ä»ãããªãéæ³ã宿ããã®ããïŒ
ð DeadReckoning.cpp
#include <iostream>
struct Vector2D {
float x;
float y;
};
// ç·åœ¢è£éïŒLerp: AããBãž t (0.0ã1.0) ã®å²åã§è£éïŒ
inline float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
class NetworkEntity {
public:
Vector2D currentPos; // çŸåšç»é¢ã«è¡šç€ºããŠããè£é座æš
Vector2D targetPos; // æåŸã«ãããã¯ãŒã¯ããåä¿¡ããçã®ç®æšåº§æš
Vector2D velocity; // æšæž¬èªæ³çšã®é床ãã¯ãã«
NetworkEntity() {
currentPos.x = 0; currentPos.y = 0;
targetPos.x = 0; targetPos.y = 0;
velocity.x = 0; velocity.y = 0;
}
// ãã±ããåä¿¡æã«åŒã°ãã
void onReceivePacket(float newX, float newY, float vx, float vy) {
targetPos.x = newX;
targetPos.y = newY;
velocity.x = vx;
velocity.y = vy;
}
// æ¯ãã¬ãŒã ïŒ60FPS / 16.6msïŒåŒã°ããæç»æŽæ°
void update(float deltaTime) {
// ç®æšå°ç¹ãžå°ããã€æ»ããã«å¯ããŠããïŒè£éä¿æ° 0.15ïŒ
currentPos.x = lerp(currentPos.x, targetPos.x, 0.15f);
currentPos.y = lerp(currentPos.y, targetPos.y, 0.15f);
// ããã«æšæž¬èªæ³ïŒæ¬¡ã®ãã±ãããæ¥ããŸã§é床åã ãç®æšåº§æšãé²ãã
targetPos.x += velocity.x * deltaTime;
targetPos.y += velocity.y * deltaTime;
}
};
int main() {
NetworkEntity remotePlayer;
// çžæã座æš(100, 50)ãžé床(10, 0)ã§ç§»åäžãšãããã±ãããåä¿¡
remotePlayer.onReceivePacket(100.0f, 50.0f, 10.0f, 0.0f);
std::cout << "--- 60FPS è£éã·ãã¥ã¬ãŒã·ã§ã³éå§ ---" << std::endl;
for (int frame = 1; frame <= 5; ++frame) {
remotePlayer.update(0.0166f); // 16.6ms
std::cout << "Frame " << frame << ": Current Screen Pos = ("
<< remotePlayer.currentPos.x << ", "
<< remotePlayer.currentPos.y << ")" << std::endl;
}
return 0;
}
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
ç·åœ¢è£éïŒLerpïŒã«ããç¬éç§»åã®è§£æ¶ : åä¿¡ãã座æšã«å³åº§ã«ãªããžã§ã¯ãããžã£ã³ãããããçŸåšåº§æšããåŸã
ã«è¿ã¥ããããšã§æ»ãããªæç»ãå®çŸãããæšæž¬èªæ³ïŒDead ReckoningïŒ : ãã±ããã®å°çé »åºŠãäœããŠããé床ãã¯ãã«ãæããŠæªæ¥äœçœ®ãäºæž¬ã·ãã¥ã¬ãŒã·ã§ã³ããããšã§é
å»¶ãæããããªãã
L19.4 ãã³ããããã³ã°ãœã±ãããšåæã²ãŒã ã«ãŒãã®åäœ
ãã±ãããæ¥ãŠããªããŠãåŸ
ããã«å³ãªã¿ãŒã³ïŒ select() / ioctlsocket ã«ãããã³ããããã³ã°I/Oã
ð§ ãã³ã®ã³å
茩
recvfrom() ãåŒãã ããçžæããã±ãããéã£ãŠãããŸã§åŠçãæ¢ãŸã£ãŠããŸããã²ãŒã å
šäœãããªãŒãºããŠããŸããŸããâŠïŒ
ð»ââïž ã·ãã¯ãå
ç
ããã©ã«ãã®ãœã±ããã¯ãããŒã¿ãæ¥ããŸã§åŸ
æ©ããããããã³ã°ã¢ãŒããããïŒã²ãŒã ã«ãŒãã§äœ¿ããœã±ããã¯ãå¿
ãããã³ããããã³ã°ã¢ãŒãïŒNon-blocking I/OïŒãã«èšå®ããŠããã±ãããæ¥ãŠããªããã°å³åº§ã«ãšã©ãŒEWOULDBLOCKãè¿ããŠåŠçãæãããããã«ä»èŸŒãã®ããïŒ
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
ãã³ããããã³ã°I/Oã®å¿
é æ§ : ã²ãŒã ã«ãŒãã¯16.6msåšæã§åãç¶ããªããã°ãªããªããããéä¿¡åä¿¡åŸ
ã¡ã§ãããã¯ããããœã±ããã¯çµ¶å¯Ÿã«äœ¿çšããŠã¯ãªããªãã
ð ç解床ãã§ãã¯ã¯ã€ãº
Q1. ãªã¢ã«ã¿ã€ã ã¢ã¯ã·ã§ã³ã²ãŒã ããªã³ã©ã€ã³å¯ŸæŠã«ãããŠãTCPãããUDPãå§åçã«å€ãæ¡çšãããæå€§ã®çç±ã¯äœã§ããïŒ
1. UDPã®ã»ããæå·åã匷åºã ãã 2. TCPã§ã¯ãã±ããã1ã€æ¬ èœãããšåŸç¶ãã±ããã®åŠçãå
šãŠãããã¯ãããïŒHead-of-Line BlockingïŒããããªã¢ã«ã¿ã€ã æ§ã倱ããããã 3. UDPã䜿ããšã€ã³ã¿ãŒãããæéãåé¡ã«ãªããã 4. TCPã¯C++ã§äœ¿ããªããã
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒTCPã§ã¯ãã±ããã1ã€æ¬ èœãããšåŸç¶ãã±ããã®åŠçãå
šãŠãããã¯ãããïŒHead-of-Line BlockingïŒããããªã¢ã«ã¿ã€ã æ§ã倱ããããã
TCPã®ä¿¡é Œæ§ä¿èšŒæ©æ§ã¯ãã±ããæå€±æã«åéå®äºãŸã§åŸç¶ãæ¢ããŠããŸãããããªã¢ã«ã¿ã€ã ã²ãŒã ã§ã¯ææ°ãã±ããã峿åŠçã§ããUDPãå¿
é ãšãªããŸãã
Q2. ç°ãªãCPUã¢ãŒããã¯ãã£éã§æŽæ°ããŒã¿ãéä¿¡ããéããã€ããªãŒããŒïŒãšã³ãã£ã¢ã³ïŒã®éãã«ããæ°å€ç Žæãé²ãããã«äœ¿çšããæšæºé¢æ°ã¯ã©ãã§ããïŒ
1. malloc() / free() 2. htonl() / ntohl() 3. printf() / scanf() 4. std::sort()
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒhtonl() / ntohl()
htonlïŒãã¹ãâãããã¯ãŒã¯ïŒããã³ ntohlïŒãããã¯ãŒã¯âãã¹ãïŒã䜿çšããããšã§ããªãã«ãšã³ãã£ã¢ã³ãšããã°ãšã³ãã£ã¢ã³ã®å·®ç°ãèªååžåããŠæ£ããæ°å€ãéåã§ããŸãã
Q3. ãã±ããã®å°çé »åºŠãæ¯ç§20åçšåºŠã§ããã«ãããããããç»é¢äžã§çžæãã¬ã€ã€ãŒã60FPSã§æ»ããã«ç§»åããŠããããã«èŠããæè¡ã®çµã¿åããã¯äœã§ããïŒ
1. ã¡ã¢ãªãªãŒã¯æ€ç¥åšãšã¯ã©ãã·ã¥ãã³ã 2. ç·åœ¢è£éïŒLerpïŒãšæšæž¬èªæ³ïŒDead ReckoningïŒ 3. ä»®æ³é¢æ°ããŒãã«ãšè±åœ¢å€éç¶æ¿ 4. ããããã©ã°ãšstd::stringstream
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒç·åœ¢è£éïŒLerpïŒãšæšæž¬èªæ³ïŒDead ReckoningïŒ
ç·åœ¢è£éïŒLerpïŒã§éå»ããç®æšäœçœ®ãžæ»ããã«ç¹ããæšæž¬èªæ³ïŒDead ReckoningïŒã§ãã±ããééäžã®æªæ¥ç§»åãäºæž¬ããããšã§ãé
å»¶ã®ãªãæ»ãããªåæãå®çŸããŸãã
Q4. ã²ãŒã ã«ãŒãå
ã§éä¿¡ãã±ãããåä¿¡ãããœã±ãããæ±ãéãå¿
é ãšãªãèšå®ã¯äœã§ããïŒ
1. ããŒã¿ãå±ããŸã§ã¹ã¬ãããæ°žä¹
忢ãããããããã³ã°ã¢ãŒã 2. ãã±ãããå±ããŠããªãå Žåã¯åŸ
æ©ããå³åº§ã«å¶åŸ¡ãæ»ããã³ããããã³ã°ã¢ãŒãïŒNon-blocking I/OïŒ 3. ãœã±ããã®ãããã¡ãµã€ãºã0ãã€ãã«ããèšå® 4. æ¯ãã¬ãŒã ãœã±ããã close() ããŠåäœæããèšå®
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒãã±ãããå±ããŠããªãå Žåã¯åŸ
æ©ããå³åº§ã«å¶åŸ¡ãæ»ããã³ããããã³ã°ã¢ãŒãïŒNon-blocking I/OïŒ
ãã³ããããã³ã°ã¢ãŒãã«èšå®ããªããšãçžæãããã±ãããå±ããªããã¬ãŒã ã§ recv ãæ¢ãŸããã²ãŒã ã«ãŒãã®60FPSæç»ãããªãŒãºããŠããŸããŸãã
ð»ââïž ã·ãã¯ãC++ã©ã ã€ã³ã¿ã©ã¯ãã£ãåŠç¿ã·ã¹ãã
ãã©ãŠã¶ã§JavaScriptãæå¹ã«ãããšããã©ãŠã¶å
ã€ã³ããŒããŒã²ãŒã ãšãã¥ã¬ãŒã¿ãã¡ã¢ãªãããå¯èŠåãUMLã¯ã©ã¹å³ã察話å解説ãç解床ã¯ã€ãºãèµ·åããŸãã
ã€ã³ã¿ã©ã¯ãã£ãåŠç¿ãéå§ãã â