上一节缺少了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