题目
众所周知,网络游戏有服务端与客户端,客户端需要发 “消息” 给服务端,请使用 “命名管道” 模拟以下服务端和客户端的功能。
客户端:接收4种输入‘w’、’s’、’a’、’d’ 键,他们分别对应了上、下、左、右移动,将接收的输入作为 “消息” 发送给服务端。
服务端:每隔一秒打印角色的坐标 (x, y),初始坐标为 (0, 0),服务端在接收到客户端的 “消息” 过后,重新计算角色的坐标。其中向上 y 加 1,向下 y 减 1,向左 x 减 1,向右 x 加 1。
源代码
命名管道
Client:
#include <cstdlib>
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
const char* const pipe_name = "pcsc";
int c;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int existent = access(pipe_name, F_OK);
if (existent == -1) {
int r = mkfifo(pipe_name, 0664);
if(r == -1) {
puts("mkfifo ERROR.");
exit(EXIT_FAILURE);
}
}
int pfd = open(pipe_name, O_WRONLY);
system("/bin/stty raw");
for (;;) {
c = getchar();
if (c == 't' || c == 'T') break;
write(pfd, &c, sizeof(int));
}
system("/bin/stty cooked");
close(pfd);
return 0;
}
Server:
#include <chrono>
#include <cstdlib>
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <thread>
#include <unistd.h>
#include <utility>
using namespace std;
using namespace std::chrono;
const char* const pipe_name = "pcsc";
const size_t BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
ssize_t bytes_read;
pair<int, int> coord = {
0, 0 };
time_point<high_resolution_clock> t[2];
seconds delay = 1s;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int existent = access(pipe_name, F_OK);
if (existent == -1) {
int r = mkfifo(pipe_name, 0664);
if(r == -1) {
puts("mkfifo ERROR.");
exit(EXIT_FAILURE);
}
}
int pfd = open(pipe_name, O_RDONLY);
int flags = fcntl64(pfd, F_GETFL, 0);
fcntl64(pfd, F_SETFL, flags | O_NONBLOCK);
for (;;) {
t[0] = high_resolution_clock::now();
t[1] = t[0] + delay;
bytes_read = read(pfd, buffer, BUFFER_SIZE);
if (bytes_read == -1 && errno != EAGAIN) {
puts("Read ERROR.");
exit(EXIT_FAILURE);
}
for (ssize_t i = 0; i < bytes_read; ++i) {
switch (buffer[i]) {
case 'W': case 'w': ++coord.second; break;
case 'S': case 's': --coord.second; break;
case 'A': case 'a': --coord.first; break;
case 'D': case 'd': ++coord.first; break;
case 'T': case 't': goto TERM;
}
}
this_thread::sleep_until(t[1]);
cout << "<" << coord.first << ", " << coord.second << ">" << endl;
}
TERM:
return 0;
}
消息队列
点拨
使用消息队列收发消息时,注意缓冲区的大小必须大于消息的最大长度。
Client:
#include <iostream>
#include <sys/msg.h>
using namespace std;
using mq_t = int;
using msgtype_t = long;
const key_t mqkey = 1000;
const msgtype_t msgty = 1;
int msgsndres;
struct keymsg {
long mtype = msgty;
int mkey[2];
};
keymsg keybuf;
int& c = keybuf.mkey[0];
int main() {
mq_t mq = msgget(mqkey, 0664 | IPC_CREAT);
if (mq == -1) {
perror("msgget ERROR");
exit(EXIT_FAILURE);
}
system("/bin/stty raw");
for (;;) {
c = getchar();
msgsndres = msgsnd(mq, &keybuf, sizeof(keymsg::mkey), 0);
if (msgsndres == -1) {
perror("msgsnd ERROR");
exit(EXIT_FAILURE);
}
if (c == 't' || c == 'T') break;
}
system("/bin/stty cooked");
return 0;
}
Server:
#include <sys/msg.h>
#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
using namespace std;
using namespace std::chrono;
using mq_t = int;
using msgtype_t = long;
using coord_t = int;
const key_t mqkey = 1000;
const msgtype_t msgty = 1;
int msgrcvres;
struct keymsg {
long mtype = msgty;
int mkey[2];
};
keymsg keybuf;
int& c = keybuf.mkey[0];
pair<coord_t, coord_t> coord = {
0, 0 };
time_point<high_resolution_clock> t[2];
const seconds delay = 1s;
int main() {
mq_t mq = msgget(mqkey, 0664 | IPC_CREAT);
if (mq == -1) {
perror("msgget ERROR");
exit(EXIT_FAILURE);
}
for (;;) {
t[0] = high_resolution_clock::now();
t[1] = t[0] + delay;
for (;;) {
msgrcvres = msgrcv(mq, &keybuf, sizeof(keymsg::mkey), msgty, IPC_NOWAIT);
if (msgrcvres == -1) {
if (errno == ENOMSG) break;
else {
perror("msgrcv ERROR");
exit(EXIT_FAILURE);
}
}
switch (c) {
case 'W': case 'w': ++coord.second; break;
case 'S': case 's': --coord.second; break;
case 'A': case 'a': --coord.first; break;
case 'D': case 'd': ++coord.first; break;
case 'T': case 't': goto TERM;
}
}
this_thread::sleep_until(t[1]);
cout << "<" << coord.first << ", " << coord.second << ">" << endl;
}
TERM:
if (msgctl(mq, IPC_RMID, 0) == -1) {
perror("msgctl ERROR");
exit(EXIT_FAILURE);
}
return 0;
}