wxWidget教程(9)——网络编程HTTP与SOCKET

来源:互联网 发布:初中英语词汇软件 编辑:程序博客网 时间:2024/06/08 05:20

一、wxHTTP网络请求

1、先来看一个简单的请求示例:

wxHTTP http;http.SetHeader(wxT("User-Agent"),wxT("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"));http.Connect(wxT("www.biqudu.com"));//使用管道流来读取内容wxInputStream * httpStream = http.GetInputStream(wxT("/43_43821/"));if (http.GetError() == wxPROTO_NOERR) {wxString res;wxStringOutputStream out_stream(&res);httpStream->Read(out_stream);// 显示返回码wxMessageBox(wxString::FromDouble(http.GetResponse()));wxMessageBox(res);}else {wxMessageBox(wxT("http error"));}wxDELETE(httpStream);http.Close();

2、wxURI路由解析类

wxURI uri(wxT("https://pan.baidu.com/disk/home?a=1&b=2"));// 获取路由的相关信息wxString scheme = uri.GetScheme();// "https"wxString host= uri.GetServer();// "pan.baidu.com"wxString port= uri.GetPort();// "",80端口这里都为空字符串wxString path= uri.GetPath();// "/disk/home"wxString query= uri.GetQuery();// "a=1&b=2"
如果路由是经过url加密的字符串,可以通过下面的方法解密

wxString escapeUrl = wxT("https%3a%2f%2fpan.baidu.com%2fdisk%2fhome%3fa%3d1%26b%3d2");wxString unescapeUrl = wxURI::Unescape(escapeUrl);
如果已经构造完了,要解析url,可以如下:
wxString escapeUrl = wxT("https%3a%2f%2fpan.baidu.com%2fdisk%2fhome%3fa%3d1%26b%3d2");wxURI uri(escapeUrl);wxString unescapeUrl = uri.BuildUnescapedURI();
3、wxURL与代理设置

需要用到wxURL,它继承自wxURI,多了一个SetProxy函数

url.SetProxy(wxT("127.0.0.1:1080"));
wxURL可以直接请求网络,获取内容

wxURL url;url.SetURL(wxT("http://www.biqudu.com/43_43821/"));wxString res;wxStringOutputStream out_stream(&res);url.GetInputStream()->Read(out_stream);wxMessageBox(res);


二、socket编程

1、先来看一个简单的服务端与客户端的例子,我把服务端和客户端写在一起了,不影响说明问题。

声明代码:

protected:wxSocketServer * m_server;wxSocketClient * m_client;void OnStartClient(wxCommandEvent& event);void OnStartServer(wxCommandEvent& event);void OnSocketServerAcceptEvent(wxSocketEvent& event);void OnSocketServerInputEvent(wxSocketEvent& event);void OnSocketClientEvent(wxSocketEvent& event);


实现:

#define EVT_SOCKET_SERVER_ACCEPT1000#define EVT_SOCKET_SERVER_IO1001#define EVT_SOCKET_CLIENT1002wxBEGIN_EVENT_TABLE(MyFrame, MainFrame)EVT_SOCKET(EVT_SOCKET_SERVER_ACCEPT, MyFrame::OnSocketServerAcceptEvent)EVT_SOCKET(EVT_SOCKET_SERVER_IO, MyFrame::OnSocketServerInputEvent)EVT_SOCKET(EVT_SOCKET_CLIENT, MyFrame::OnSocketClientEvent)wxEND_EVENT_TABLE()/************************************************************************//* 服务端                                                                *//************************************************************************/void MyFrame::OnStartServer(wxCommandEvent& event){wxIPV4address addr;addr.Service(6000);m_server = new wxSocketServer(addr);if (!m_server->Ok()) return;// 绑定事件m_server->SetEventHandler(*this, EVT_SOCKET_SERVER_ACCEPT);m_server->SetNotify(wxSOCKET_CONNECTION_FLAG);m_server->Notify(true);wxLogDebug(wxT("SERVER=>开始监听客户端请求"));}/************************************************************************//* 服务端--接收请求的事件处理                                             *//************************************************************************/void MyFrame::OnSocketServerAcceptEvent(wxSocketEvent& event) {// 开始接收客户端请求wxSocketBase * sock = m_server->Accept(false);wxLogDebug(wxT("SERVER=>收到一个请求"));// 绑定收发数据的事件sock->SetEventHandler(*this, EVT_SOCKET_SERVER_IO);sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);sock->Notify(true);}/************************************************************************//* 服务端--收发数据的事件处理                                             *//************************************************************************/void MyFrame::OnSocketServerInputEvent(wxSocketEvent& event) {wxSocketBase * sock = event.GetSocket();char buf[11];memset(buf, 0, sizeof(buf));switch (event.GetSocketEvent()){case wxSOCKET_INPUT:{sock->Read(buf,11);wxLogDebug(wxT("SERVER=>接收到数据:%s"), wxString(buf));char response[] = "connected.";sock->Write(response, sizeof(response));wxLogDebug(wxT("SERVER=>响应客户端一串数据:%s"), wxString(response));sock->Destroy();wxLogDebug(wxT("SERVER=>我方销毁了socket,并通知对方"));break;}case wxSOCKET_LOST:{sock->Destroy();wxLogDebug(wxT("SERVER=>对方销毁了socket,通知我方"));break;}}}/************************************************************************//* 客户端                                                                *//************************************************************************/void MyFrame::OnStartClient(wxCommandEvent& event){wxIPV4address addr;addr.Hostname(wxT("127.0.0.1"));addr.Service(6000);// socket客户端m_client = new wxSocketClient();// 绑定事件m_client->SetEventHandler(*this, EVT_SOCKET_CLIENT);m_client->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);m_client->Notify(true);// 等待接收事件m_client->Connect(addr, false);wxLogDebug(wxT("CLIENT=>向服务端发送一个请求"));}/************************************************************************//* 客户端事件处理函数                                                     *//************************************************************************/void MyFrame::OnSocketClientEvent(wxSocketEvent& event) {wxSocketBase * sock = event.GetSocket();char buf[11];memset(buf, 0, sizeof(buf));switch (event.GetSocketEvent()){case wxSOCKET_CONNECTION:{char mychar = '0';for (int i = 0; i < 10; ++i) buf[i] = mychar++;sock->Write(buf, sizeof(buf));wxLogDebug(wxT("CLIENT=>发送数据:%s"),wxString(buf));break;}case wxSOCKET_INPUT:{sock->Read(buf, sizeof(buf));wxLogDebug(wxT("CLIENT=>接收到数据:%s"), wxString(buf));break;}case wxSOCKET_LOST:{sock->Destroy();wxLogDebug(wxT("CLIENT=>对方销毁了socket,并通知我方"));break;}}}



代码运行结果如下:

SERVER=>开始监听客户端请求CLIENT=>向服务端发送一个请求CLIENT=>发送数据:0123456789SERVER=>收到一个请求SERVER=>接收到数据:0123456789SERVER=>响应客户端一串数据:connected.SERVER=>我方销毁了socket,并通知对方CLIENT=>接收到数据:connected.CLIENT=>对方销毁了socket,并通知我方


过程还是比较直观明了的。

2、上面的代码中,有几点要说明。

1)为什么Accept需要传入false?

如果传入的是true,表明一直等待接收,那么事件处理函数将会阻塞GUI界面的线程;

而传入false,会立即返回一个接收到的请求,为什么它那么肯定能接收到一个请求呢?

因为想要传入false,那么当前的事件必须在wxSOCKET_CONNECTION事件之后。

2)wxSOCKET_CONNECTION_FLAG与wxSOCKET_CONNECTION区别

服务端发送了一个事件叫wxSOCKET_CONNECTION_FLAG,这个事件会判断是否有请求进来。

一旦有请求进来,那么wxSOCKET_CONNECTION事件就会触发。

这些都是在不阻塞GUI进程的情况下发生的。

如果需要同步等待客户端请求,可以使用Accept(true),或者WaitForAccept等待多少秒。

3)客户端调用Connect方法开始连接服务端,怎么发生的?

connect传入的是false,表示不等待,如果连接不上,就返回false。

客户端尝试请求服务端,请求有信号,则触发wxSOCKET_CONNECTION事件。

在这个事件中,向服务端发送了一串字符串。

4)Close与Destroy的区别

Destroy是安全的关闭一个socket,而close是立即关闭,不太安全,需要手动的处理关闭的一些操作。

5 ) 在程序结束的时候,如果服务端的socket还在监听中,要Destroy掉,不然会内存泄漏。

3、使用socket流来操作读写,方便传输大文件

首先什么是流?从内存写入到文件或屏幕设备的,都是输出流;从文件或屏幕写入到内存中的,都是输入流。

关于wxSocketOutputStream,这是一个输出流,数据从内存中转移到socket中。

而wxSocketInputStream,这是一个输入流,数据从socket转移到内存中。

-----------------------------------

关于socket的标记有4种,wxSOCKET_NONE | wxSOCKET_NOWAIT | wxSOCKET_WAITALL | wxSOCKET_BLOCK。

wxSOCKET_NONE:总是试图读取或者写入一些数据,但是不关心具体是多少数据。

wxSOCKET_NOWAIT:只关心尽快返回,即使没有读取或写任何数据。

wxSOCKET_WAITALL:将在所有的数据都被写入或者是读取的数据达到要求的数目的时候返回。

wxSOCKET_BLOCK:和前面的标记没有关系,只控制是否在底层操作的间隙执行Yield动作。

常用的话,有五种组合,如下:

wxSOCKET_NONE | wxSOCKET_BLOCK:类似标准的socket阻塞的recv和send行为。

wxSOCKET_NOWAIT:类似标准的socket非组赛的recv和send行为。

wxSOCKET_WAITALL|wxSOCKET_BLOCK:一般传输大文件时需要设置,保证传输数据的完整性,阻塞GUI或其他代码的执行。

wxSOCKET_NONE:类似标准的socket阻塞的recv和send行为,但是会调用wxYiedl,看起来不阻塞。

wxSOCKET_WAITALL:一般传输大文件时需要设置,保证传输数据的完整性,不阻塞。

最后2组比较危险,虽然好用,但是容易产生麻烦。

尽量使用wxSOCKET_NOWAIT,可以立即返回,不阻塞。

其他情况,要设置超时时间SetTimeout,这样可以在n秒内,如果没有写入或输出,就超时返回,不再阻塞。

代码如下:

// 从socket取数据sock->SetTimeout(5);sock->SetFlags(wxSOCKET_WAITALL);wxSocketInputStream sockis(*sock);wxTextInputStream tis(sockis);wxString data;tis >> data;wxLogDebug(wxT("SERVER=>接收到数据:%s"), data);
sock->SetTimeout(5);sock->SetFlags(wxSOCKET_WAITALL);wxSocketOutputStream sos(*sock);wxTextOutputStream tos(sos);tos << "good" << "morning";tos.Flush();






























原创粉丝点击