GA原有的实现中对并发连接并不支持,表现出的是先启动服务端,服务端通过指定的游戏配置文件来启动特定的游戏,然后等待客户端进行连接,当客户端连接进来时将捕捉到的游戏画面通过H.264压缩后,通过RTP、RTSP等协议将其发送到客户端。当服务端启动一个游戏有多个客户端连接进来时,多个客户端看到的画面是一样的,因为它们都通过相同的端口来接收数据。这种情况并不满足实际使用的需要,为了真正实现并发改造,需要实现一下功能:
1> GA服务端启动后应该一直监听,每当有客户端的请求到来时就建立一个新的链接,根据客户端传递的游戏参数启动对应的游戏。
2> GA accept客户端的连接请求后应该能将该socket保存下来,并在通信时使用,使每个连接过来的客户端能够看到属于自己的画面。
3> 既然能并发连接,那么每个连接过来的客户端就能够单独断开连接而不妨碍其它现有的连接着的客户端。
第一部分的实现:
第一部分的实现需要解决的问题有:1》服务端要能够一直监听客户端的请求。2》服务器端要能够解析客户端传来的参数,从而解析出该客户端需要启动哪个游戏。
服务器端一直监听的实现想必大家都有思路,就是利用socket一直listen就可以了,等到accept之后,启动receiveclient线程,由该线程负责解析参数,以及后期的其它操作,至于socket的使用就不在里详细讲解了,但需要注意的一点就是:Linux和Windows下socket的使用有一些差异,Windows下Socket使用前必须使用WSAStartup()完成对Winsock环境的初始化,相关实现代码如下(注意标红部分的使用):
#ifdef WIN32
WSADATA wd;
if(WSAStartup(MAKEWORD(2,2), &wd) != 0)
return -1;
#endif
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
fprintf(stderr, "socket creating failed: %s\n", strerror(errno));
return -1;
}
//
do {
#ifdef WIN32
BOOL val = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*) &val, sizeof(val));
#else
int val = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
#endif
} while(0);
//
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(8554);
//
if(bind(s, (struct sockaddr*) &sin, sizeof(sin)) < 0) {
fprintf(stderr, "socket binding failed: %s\n", strerror(errno));
return -1;
}
if(listen(s, 256) < 0) {
fprintf(stderr, "socket listening failed: %s\n", strerror(errno));
return -1;
}
//
do {
int csinlen;
pthread_t thread;
SOCKET cs;
struct sockaddr_in csin;
//
csinlen = sizeof(csin);
bzero(&csin, sizeof(csin));
if((cs = accept(s, (struct sockaddr*) &csin, &csinlen)) < 0) {
fprintf(stderr, "socket acceptting error: %s\n", strerror(errno));
return -1;
}
fprintf(stderr, "client connected from %s:%d\n",
inet_ntoa(csin.sin_addr), htons(csin.sin_port));
// tunning sending window
do {
int sndwnd = 8388608; // 8MB
if(setsockopt(cs, SOL_SOCKET, SO_SNDBUF, (const char *)&sndwnd, sizeof(sndwnd)) == 0) {
fprintf(stderr, "*** set TCP sendi