標籤:
由於python本身的線程基本上比較殘廢,所以為了利用機器的cpu,就不得不用上多進程。。。
在遊戲伺服器的設計中,最為常見的方式是:
掛一個前端伺服器,專門來維護與用戶端的串連,然後將用戶端的請求資料轉寄給後端伺服器。。。
上面的方式是現在最為正統的。。。
但是自己因為環境的限制,需要做到對用戶端透明,然後將後端的伺服器轉換成為多進程的。。。所以這裡就只有用一點比較彆扭的方法了,首先處理登入等一些常規的邏輯放在前端伺服器,當進入放進進行匹配戰鬥之後,將用戶端的socket串連直接交給後端伺服器,然後進行處理。。。。。
因此這裡就需要實現一個python能用的檔案描述符傳遞擴充庫。。。。
還好自己用C語言做過類似的東西。。。所以基本上把代碼拿過來,再用cython做一層封裝,也就能用了,這裡就直接貼代碼吧:
#include <sys/socket.h> #include <stdio.h>#include <stdlib.h>#include <stddef.h>#include <sys/un.h>#include <string.h>#include <unistd.h>int serv_listen(const char *name);int send_fd(int sock, int fd, char* data);int recv_fd(int sock, char *data); void close_fd(int fd);
上面是標頭檔的定義,接下來把c語言的代碼貼上來:
#include "sr.h"int sr_connect(const char *name){int fd, size;struct sockaddr_un un;memset(&un, 0, sizeof(un));un.sun_family = AF_UNIX;if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {printf("no1\n");return 0;}sprintf(un.sun_path, "%s%05d", "tmp", getpid());size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);if (bind(fd, (struct sockaddr *)&un, size) < 0) {printf("no2\n");return 0;}strcpy(un.sun_path, name);size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);if (connect(fd, (struct sockaddr *)&un, size) < 0) {printf("no3\n");return 0;}// if (listen(fd, 10) < 0) {// printf("no3");// }// char* hello = "hello fjs";// ssize_t out = send(fd, (void*)hello, strlen(hello), 0);// send_fd(lis, lis);return fd;}int send_fd(int sock, int fd, char* data) { printf("here1: %s\n", data); struct iovec iov[1]; iov[0].iov_base = data; iov[0].iov_len = strlen(data); printf("len: %d\n", strlen(data)); printf("here2: %s\n", iov[0].iov_base); int cmsgsize = CMSG_LEN(sizeof(int)); struct cmsghdr* cmptr = (struct cmsghdr*)malloc(cmsgsize); if(cmptr == NULL){ return -1; } cmptr->cmsg_level = SOL_SOCKET; cmptr->cmsg_type = SCM_RIGHTS; // we are sending fd. cmptr->cmsg_len = cmsgsize; struct msghdr msg; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = cmptr; msg.msg_controllen = cmsgsize; *(int *)CMSG_DATA(cmptr) = fd; int ret = sendmsg(sock, &msg, 0); free(cmptr); if (ret == -1){ return -1; } return 0; } int recv_fd(int sock, char* data) { int cmsgsize = CMSG_LEN(sizeof(int)); struct cmsghdr* cmptr = (struct cmsghdr*)malloc(cmsgsize); if (cmptr == NULL) { return -1; } char buf[33]; // the max buf in msg. memset(buf, 0, 33); struct iovec iov[1]; iov[0].iov_base = buf; iov[0].iov_len = sizeof(buf); struct msghdr msg; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = cmptr; msg.msg_controllen = cmsgsize; int ret = recvmsg(sock, &msg, 0); if (ret == -1) { return -1; } int fd = *(int *)CMSG_DATA(cmptr); strcpy(data, iov[0].iov_base); free(cmptr); return fd; }void close_fd(int fd){ close(fd);}// int main()// {// int lis = serv_listen("/tmp/fjs.sock");// printf("ok\n");// char* hello = "hello fjs";// // ssize_t out = send(lis, (void*)hello, strlen(hello), 0);// send_fd(lis, lis);// }
最後貼上cython的封裝檔案:
cdef extern from "sr.h":extern int sr_connect(const char *name)extern int send_fd(int sock, int fd, char* data)extern int recv_fd(int sock, char* data)void close_fd(int fd)cdef extern from "stdlib.h":extern void *malloc(unsigned int num_bytes)extern void free(void *ptr)def connect_and_send():cdef char* dist = "/tmp/fjs.sock"cdef int fd = sr_connect(dist)send_fd(fd, fd, "fdsaf");def fjs_recv_fd(sock):cdef int fd = sockcdef char* data = <char*>malloc(33)fd = recv_fd(fd, data)try:out_data = datareturn (fd, out_data)finally:free(data)def fjs_send_fd(fd1, fd2, data):cdef int source = fd1cdef int des = fd2send_fd(source, des, data)def fjs_close_fd(fd):cdef int now_fd = fdclose_fd(fd)
嗯。。就上面這些代碼。。。這裡需要基於unix域socket。。。。
最後吐槽一下。。。。python真他媽的慢。。。。
python進程間傳遞檔案描述符擴充庫