客户机与服务器的数据交互

作者:张剑  

1、C/S的通信模式
我们在前面文章中讨论客户机与服务器的数据交互时,数据流的方向总是从服务器到客户机,很少涉及到客户机向服务器发送请求并处理返回信息的问题。实际上,在电子商务中,客户机向服务器发送数据也是一个重要的环节,如用户填写的货物订单等。
在传统的实现方案中,用户端只要在订单中做出微小的变动,就要向服务器发送消息,要求更新数据。这样就增加了服务器和网络的负载,降低了工作效率。更有效的工作方式是在客户机端对变更的信息进行缓存,然后分批定量地发送给服务器,这样一些不确定的修改信息都首先存储在客户机,只有那些确定的需要更新的数据才会发送出去,从而避免了网络和服务器做许多无谓的操作。
2、基于XML的C/S
使用XML进行C/S间的通信是一种高效的工作方式。首先在客户机对XML数据打包,然后以XML数据包为单元发送给服务器,服务器在处理完数据之后返回消息,客户机接收到消息后执行其他操作,从而结束一次通信周期。
具体实现步骤如下:
● 客户机构造一个XMLDOM对象,作为发送XML数据的载体;
● 客户机创建一个XMLHTTP对象,该对象包括多种方法和属性,可以发送XML数据到服务器上的应用程序(如ASP页面),同时准备接收响应信息;
● 客户机将XML数据包转载到XMLHTTP对象上并发送给ASP页面;
● 服务器执行ASP,并创建一个服务器端XMLDOM对象来接收XML数据;
● ASP把数据包装载到服务器端的XMLDOM对象上;
● ASP对XML数据进行必要的处理,并返回确认消息;
● 客户机接收响应消息,执行下一步操作。
3、向服务器发送数据
客户机的首要任务是构造XML数据包。XMLDOM作为数据包的载体其数据来源可以是任何XML文档或是XML文档的片段(如XML数据岛),甚至可以是使用loadXML方法接收用户输入信息后动态生成的XML文档。
下面是动态生成的一个XML文档:
Set docSubmit = CreateObject(“Microsoft.XMLDOM”)
docSubmit.async = False
docSubmit.loadXML
“<?xml version=‘1.0’?>”&
“<customerorder>” &
“<customer>”&
txtCustomerID.Value &
“</customer>”&
“</customerorder>”
如果用户输入“5”作为用户ID,那么上面程序的运行结果如下所示:
<?xml version=‘1.0’?>
<customerorder>
<customer>5</customer>
</customerorder>
接下来,将XML数据添加到DOM树的<customer>元素之后。
比如有如下的一个XML数据岛:
<xml id=“dsoOrder”>
<?xml version=“1.0”?>
<order>
<orderitem title=“Sushi, Anyone?” isbn=“72-80081-025”>
<price>14.99</price>
<quantity>2</quantity>
</orderitem>
</order>
</xml>
利用前面介绍的DOM技术访问<order>元素:
Set docOrder = dsoOrder.XMLDocument
Set nodeOrder = docOrder.selectSingleNode(“//order”)
<order>元素是dsoOrder数据岛中的一部分,要把它添加到其他的DOM树中(比如前面的XML数据包),必须把<order>元素及其所有的后继节点进行备份,这是因为appendChild方法在把操作对象添加到其他DOM树中后,就从当前的DOM树中删除该对象!所以必须先调用cloneNode方法:
Set nodeOrderToSend = nodeOrder.cloneNode(True)
docSubmit.documentElement.appendChild nodeOrderToSend
经过以上操作,最后形成的数据包为:
<?xml version=“1.0”?>
<customerorder>
<customer>5</customer>
<order>
<orderitem title=“Sushi, Anyone?” isbn=“72-80081-025”>
<price>14.99</price>
<quantity>2</quantity>
</orderitem>
</order>
</customerorder>
4、XMLHTTP对象的Open方法
数据包构造完毕之后,就可以利用HTTP Request对象把数据包发送给服务器。MSXML中提供了Microsoft.XMLHTTP对象,能够完成从数据包到Request对象的转换以及发送任务。
创建XMLHTTP对象的语句如下:
Set poster = CreateObject(“Microsoft.XMLHTTP”)
对象创建后调用Open方法对Request对象进行初始化,语法格式为:
poster.open http-method, url, async, userID, password
Open方法中包含了5个参数,前三个是必要的,后两个是可选的(在服务器需要进行身份验证时提供)。参数的含义如下表所示:
参数 说明
http-method HTTP的通信方式,比如GET或是 POST
url 接收XML数据的服务器的URL地址。通常在URL中要指明 ASP或CGI程序
async 一个布尔标识,说明请求是否为异步的。如果是异步通信方式,客户机就不等待服务器的响应;如果是同步方式,客户机就要等到服务器返回消息后才去执行其他操作
userID 用户ID,用于服务器身份验证
password 用户密码,用于服务器身份验证

在下面这个例子中,客户机就是使用异步方式向“CustomerOrder.asp”页面发送了一个POST请求:
poster.open "POST", "CustomerOrder.asp", False

1、XMLHTTP对象的Send方法
用Open方法对Request对象进行初始化后,调用Send方法发送XML数据:
poster.send XML-data
Send方法的参数类型是Variant,可以是字符串、DOM树或任意数据流。发送数据的方式分为同步和异步两种。在异步方式下,数据包一旦发送完毕,就结束Send进程,客户机执行其他的操作;而在同步方式下,客户机要等到服务器返回确认消息后才结束Send进程。
XMLHTTP对象中的readyState属性能够反映出服务器在处理请求时的进展状况。客户机的程序可以根据这个状态信息设置相应的事件处理方法。属性值及其含义如下表所示:
值 说明
0 Response对象已经创建,但XML文档上载过程尚未结束
1 XML文档已经装载完毕
2 XML文档已经装载完毕,正在处理中
3 部分XML文档已经解析
4 文档已经解析完毕,客户端可以接受返回消息

2、服务器端的数据处理
服务器在接收到客户机发送过来的数据包后,会马上处理数据,并做出相应的响应。服务器先创建一个XMLDOM对象,然后将Request对象中的数据载入,并通过XMLDOM对象开始访问XML数据。
在获得XML数据的访问入口后,首先做的事情是对XML文档进行验证(这部分的具体过程我们将在后面介绍XML Schema的时候一并说明)。一旦通过验证,就可以利用DOM接口对XML数据进行分析处理(例如:将提取的信息用来更新数据库中的记录)。
一个简单的ASP脚本如下:
<%
Set docReceived = CreateObject(“Microsoft.XMLDOM”)
docReceived.async = False
docReceived.load Request
Set rootNode = docReceived.documentElement
Set nodeCustomer = docReceived.selectSingleNode(“//customer”)
customerID = nodeCustomer.firstChild.nodeValue
……
%>
3、服务器端的响应消息
服务器在处理完XML数据之后,构造响应消息,并返回给客户机。消息的形式可以是纯文本、HTML页面、XML文档或者是嵌有XML数据岛的HTML页面。
首先来看一个HTML页面的例子,这个消息页面中包含了客户订购的信息:
<H3>Confirmation of order</H3>
<% For Each node In listOrderitem
title = node.getAttribute(“title”)
Set quantityNode = node.selectSingleNode(“quantity”)
Quantity = quantityNode.firstChild.nodeValue %>
<P> <%=title%>, <%=quantity%> </P>
<% Next %>
服务器采用XML文档作为返回消息的优点是:客户机可以利用智能程序对结构化的消息进行分析,能够更准确地理解服务器所要表达的信息。
例如:
<%
Set docResponse = CreateObject(“Microsoft.XMLDOM”)
docResponse.async = False
docResponse.load “MyFixedResponse.xml”
Response.ContentType =“text/xml”
Response.save docResponse
%>
在使用XML文档时,必须在填写Response内容之前将ContentType属性值指明为“text/xml”,表示响应消息的格式为XML。save方法的用途是将XML文档内容填充到Response对象中。
除了可以装载已存在的XML文档,还可以实时构造XML数据作为响应内容:
<% Response.ContentType=“text/xml” %>
<confirmation>
<errorcode>0</errorcode>
<shipped>
<% For Each node In listOrderitem
title = node.getAttribute(“title”)
Set quantityNode = node.selectSingleNode(“quantity”)
Quantity = quantityNode.firstChild.nodeValue %>
<item title=“<%=title%>” quantity=“<%=quantity%>” />
<% Next %>
</shipped>
</confirmation>
4、客户机处理响应信息
客户机接收到返回消息后,进行简单的处理,基本上就完成了C/S之间的一个交互周期。客户机接收响应是通过XMLHTTP对象的属性实现的:
● responseTxt:将返回消息作为文本字符串;
● responseXML:将返回消息视为XML文档,在服务器响应消息中含有XML数据时使用;
● responseStream:将返回消息视为Stream对象。
HTML页面的返回消息一般是被作为文本字符串进行处理。下面的这个例子是一个完整的交互过程,客户机脚本的代码如下:
<DIV ID=“displayArea”> </DIV>
<SCRIPT LANGUAGE=“VBScript”>
Sub Submit_OnClick()
’创建XMLDOM
Set docSubmit = CreateObject(“Microsoft.XMLDOM”)
……’如前所述构造数据包
’向服务器发送数据包
Set poster = CreateObject(“Microsoft.XMLHTTP”)
poster.open “POST”, “CustomerOrder.asp”, False
poster.send docSubmit
’接收Resoponse消息
displayArea.innerHTML = poster.responseText
End Sub
Submit_OnClick方法首先创建XMLDOM对象用来装载XML数据,然后创建XMLHTTP对象以同步方式将数据包发送给服务器。在服务器处理完毕之后返回一个HTML页面作为响应消息。XMLHTTP对象按照文本字符串方式接收消息,并把它与DIV元素绑定起来,在浏览器中显示最终结果。
响应XML数据的基本过程与上面的这个例子是一样的,只是在接收时,使用responseXML属性,然后利用DOM技术对XML消息做进一步的处理。这样,一个完整的C/S交互过程就全部结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的Java Socket实现服务器与客户端之间交互、发送数据、创建新客户机、客户端与服务器连接和广播的示例代码及注释: 服务器端代码: ```java import java.io.*; import java.net.*; public class Server { private ServerSocket serverSocket; public static void main(String[] args) { new Server().startServer(); } public void startServer() { try { // 创建ServerSocket并绑定端口号 serverSocket = new ServerSocket(8888); System.out.println("服务器已启动,等待客户端连接..."); while (true) { // 接收客户端连接请求,创建新的客户端Socket Socket clientSocket = serverSocket.accept(); System.out.println("有新的客户端连接:" + clientSocket.getInetAddress().getHostAddress()); // 创建新线程处理客户端请求 ClientHandler clientHandler = new ClientHandler(clientSocket); new Thread(clientHandler).start(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (serverSocket != null) { serverSocket.close(); } } catch (IOException e) { e.printStackTrace(); } } } // 处理客户端请求的线程 private class ClientHandler implements Runnable { private Socket clientSocket; private BufferedReader reader; private PrintWriter writer; public ClientHandler(Socket clientSocket) throws IOException { this.clientSocket = clientSocket; // 获取输入流和输出流 reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); writer = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream())); } @Override public void run() { try { String message; while ((message = reader.readLine()) != null) { System.out.println("收到客户端消息:" + message); // 广播消息给其他客户端 broadcastMessage(message); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (clientSocket != null) { clientSocket.close(); } } catch (IOException e) { e.printStackTrace(); } } } // 发送消息给客户端 public void sendMessage(String message) { writer.println(message); writer.flush(); } } // 广播消息给所有客户端 public synchronized void broadcastMessage(String message) { for (Thread thread : Thread.getAllStackTraces().keySet()) { if (thread instanceof ClientHandler) { ClientHandler clientHandler = (ClientHandler) thread; clientHandler.sendMessage(message); } } } } ``` 客户端代码: ```java import java.io.*; import java.net.*; public class Client { private Socket clientSocket; private BufferedReader reader; private PrintWriter writer; public static void main(String[] args) { new Client().startClient(); } public void startClient() { try { // 创建客户端Socket并连接服务器 clientSocket = new Socket("localhost", 8888); System.out.println("连接服务器成功!"); // 获取输入流和输出流 reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); writer = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream())); // 开启新线程接收服务器消息 new Thread(() -> { try { String message; while ((message = reader.readLine()) != null) { System.out.println("收到服务器消息:" + message); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 发送消息给服务器 BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in)); String message; while ((message = consoleReader.readLine()) != null) { writer.println(message); writer.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (clientSocket != null) { clientSocket.close(); } } catch (IOException e) { e.printStackTrace(); } } } } ``` 注释在代码中,希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值