上一節缺少了tcputil的講解,首先這幾個函數都不是我寫的,一部分是從SDL_net官網上下載的小例子,另一部分是找的github上的代碼。
tcputil.h
#ifndef tcputil_h
#define tcputil_h 1
#ifdef WIN32
#else
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#endif
#include <string>
#include "SDL.h"
#include "SDL_net.h"
/* receive a buffer from a TCP socket with error checking */
/* this function handles the memory, so it can't use any [] arrays */
/* returns 0 on any errors, or a valid char* on success */
extern char *getMsg(TCPsocket sock, char **buf);
/* send a string buffer over a TCP socket with error checking */
/* returns 0 on any errors, length sent on success */
extern int putMsg(TCPsocket sock, const char *buf);
/**
* 獲取當前的IP地址
* @param num_ip ip的數字
* @return 格式化的ip地址
*/
extern std::string getLocalHostIP(Uint32& num_ip);
#endif
putMsg負責發送信息,getMsg負責接收信息,新加的getLocalHostIP()則負責獲取當前主機的IP地址,這個函數在局域網遊戲中尤其有用,比如局域網對戰,該函數僅僅在ubuntu下測試成功,暫時未在window下測試。
#include "tcputil.h"
char *getMsg(TCPsocket sock, char **buf)
{
Uint32 len,result;
static char *_buf;
/* allow for a NULL buf, use a static internal one... */
if(!buf)
buf=&_buf;
/* free the old buffer */
if(*buf)
free(*buf);
*buf=NULL;
/* receive the length of the string message */
result=SDLNet_TCP_Recv(sock,&len,sizeof(len));
if(result<sizeof(len))
{
if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
return(NULL);
}
/* swap byte order to our local order */
len=SDL_SwapBE32(len);
/* check if anything is strange, like a zero length buffer */
if(!len)
return(NULL);
/* allocate the buffer memory */
*buf=(char*)malloc(len);
if(!(*buf))
return(NULL);
/* get the string buffer over the socket */
result=SDLNet_TCP_Recv(sock,*buf,len);
if(result<len)
{
if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
free(*buf);
buf=NULL;
}
/* return the new buffer */
return(*buf);
}
getMsg負責獲取數據,這一對函數必須配套使用,因爲putMsg函數在發送真正的信息之前,會先發送本次要發送的字節數,而getMsg也會先獲取字節數,並分配好適當的空間。另外,getMsg()內部採取了靜態局部變量來設置內存,因此,不必擔心內存問題。
int putMsg(TCPsocket sock,const char *buf)
{
Uint32 len,result;
if(!buf || !strlen(buf))
return(1);
/* determine the length of the string */
len=strlen(buf)+1; /* add one for the terminating NULL */
/* change endianness to network order */
len=SDL_SwapBE32(len);
/* send the length of the string */
result=SDLNet_TCP_Send(sock,&len,sizeof(len));
if(result<sizeof(len)) {
if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
return(0);
}
/* revert to our local byte order */
len=SDL_SwapBE32(len);
/* send the buffer, with the NULL as well */
result=SDLNet_TCP_Send(sock,buf,len);
if(result<len) {
if(SDLNet_GetError() && strlen(SDLNet_GetError())) /* sometimes blank! */
printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
return(0);
}
/* return the length sent */
return(result);
}
putMsg和getMsg函數相對,putMsg主要是向套接字發送數據。
std::string getLocalHostIP(Uint32& num_ip)
{
#ifdef WIN32
char text[20] = {};
IPaddress localhost_ip;
SDLNet_ResolveHost(&localhost_ip, nullptr, 0);
SDLNet_ResolveHost(&localhost_ip, SDLNet_ResolveIP(&localhost_ip), 0);
//output
num_ip = SDL_SwapBE32(localhost_ip.host);
sprintf(text, "%d.%d.%d.%d",
num_ip>>24,
(num_ip>>16) & 0xff,
(num_ip>>8) & 0xff,
(num_ip & 0xff));
return std::string(text);
#else
std::string ip;
int socket_fd = socket(PF_INET,SOCK_DGRAM,0);
struct sockaddr_in *sin;
ifconf conf;
struct ifreq* ifr;
char buf[128];
int i,n;
conf.ifc_len = 128;
conf.ifc_buf = buf;
ioctl(socket_fd,SIOCGIFCONF,&conf);
ifr = conf.ifc_req;
n = conf.ifc_len/sizeof(struct ifreq);
for(i=0;i<n;i++)
{
sin = (struct sockaddr_in*)(&ifr->ifr_addr);
ip = inet_ntoa(sin->sin_addr);
if(ip.compare("127.0.0.1"))
{
return ip;
}
ifr++;
}
ip = "127.0.0.1";
return ip;
#endif
}
最後則是getLocaHostIP()這個函數,此函數是我在sdlgui貼吧裏找到的一部分代碼,該代碼作者目前不再進行維護,不過有的代碼還是可圈可點的(我不懂socket編程,我只是大自然的搬運工)。
本節結束。
SDL_net 官方鏈接:http://www.libsdl.org/projects/SDL_net/docs/index.html
sdl_gui github鏈接:https://github.com/SDLGUI/SDLGUI