Core Java 8 读书笔记-Networking编程
作者:老九—技术大黍
社交:知乎
公众号:老九学堂(新人有惊喜)
特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权
前言
网络应用
我们一张图来描述网络应用的概念,参见下图所示:
- client-网络客户端
- network packet--网络数据包
- Internet--互联网
- ports on server--服务器的端口号
- server-网络服务器
telnet是大多数操作系统都预装的网络工具命令。你可以使用请求HTTP服务器如下:
- telnet java.sun.com 80
- 然后输入GET / HTTP/1.0
- 最后点击回车键多次。
如果一切正常,那么你会在命令行窗体中看到html的所有标签。它得到一个web网站的完整网页内容。 下面我们使用Java的Socket技术进行编程,也可以达到相同的效果。
ShowSocket演示代码
package com.jb.arklis.net;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
/**
功能:书写一个类,用来演示怎样使用Socket技术达到与telnet命令相同的效果
作者:技术大黍
*/
public class ShowSocket{
private Socket socket;
public ShowSocket(){
// firstNetworking(); //模拟telnet命令
showInetAddress();
}
private void showInetAddress(){
try{
String host = JOptionPane.showInputDialog("输入主机名称:");
//得到主机的所有信息
InetAddress[] addresses = InetAddress.getAllByName(host);
//打印出来
for(InetAddress x : addresses){
out.println("远程主机是:" + x);
}
//显示本地主机
InetAddress localhostAddress = InetAddress.getLocalHost();
out.println("本地主机是:" + localhostAddress);
}catch(Exception e){
e.printStackTrace();
}
}
private void firstNetworking(){
try{
socket = new Socket("time-A.timefreq.bldrdoc.gov",13);
InputStream input = socket.getInputStream();
//读取内存中去
Scanner scanner = new Scanner(input);
StringBuilder message = new StringBuilder();
//演示出来
while(scanner.hasNextLine()){
String line = scanner.nextLine();
message.append(line);
}
javax.swing.JOptionPane.showMessageDialog(null,message.toString());
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
new ShowSocket();
}
}
运行效果
在VS Code的控制台中显示内容信息如下:
上面这个例子的第一行代码是打开一个socket对象,该对象是对网络的一种抽象。我们把远程地址和端口号传送给socket构造方法,然后由该对象实现具体的细节。一旦一个socket对象打开了,那么getInputStream访求 会得到一个InputStream对象,如果我们得到这个流对象,那么就可以在控制台输出内容,直到服务器关闭连接为止。Socket类为Java程序员提供了非常方便和简单的方式进行网络编程。
读取服务器数据时如果找不主机,那么你的应用会等很长时间。为解决这种问题,我们可以使用setSoTimeout方法来间隔读取网络数据。
网络应用—服务器实现
我们上面的例子实现了从互联网获取数据,下面我们解决怎样在互联网上提供数据—书写一个简单的服务器提供数据。它的原理是通过ServerSocket类创建一个socket对象,然后对象socket对象得到输入和输出流对象。
EchoServer类演示代码
package com.jb.arklis.net;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
/**
功能:书写一个服务器类,用来演示怎样在互联网上提供一种服务
作者:技术大黍
*/
public class EchoServer{
private ServerSocket server;
private Socket incoming;
public EchoServer(){
try{
//创建服务器端socket对象
server = new ServerSocket(8888);
//得到客户端的请求
incoming = server.accept();
try{
//从客户端得到输入流对象
InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();
//读到内存,然后在客户端显示出来
Scanner scanner = new Scanner(inStream);
PrintWriter output = new PrintWriter(outStream,true/*自动刷新*/);
output.println("你好!远程主机的连接已创建。。。");//给客户端显示已经建立会话
//在服务端响应客户端的请求
boolean done = false;
while(!done && scanner.hasNextLine()){
String line = scanner.nextLine();
output.println("响应:" + line);
//如果客户端输入"BYE"
if(line.trim().equals("BYE")){
//那么在服务端标识客户端请求结束(或者会话结束),于是退出循环。
done = true;
}
}
}finally{
//关闭与客户端的会话
incoming.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
System.out.println("服务器已启动!!!");
new EchoServer();
}
});
}
}
运行效果
运行服务端程序后,打印一个命令窗体,然后输入telnet localhost 8888 之后的结果如下:
如果要实现一对多的服务,即服务多个客户端,那么我们需要使用多线程来完成处理动作。
ThreadedEchoServer类演示多线程服务器代码
package com.jb.arklis.net;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
/**
功能:书写一个多线程服务器类,用来演示一对多服务器的模式
作者:技术大黍
*/
public class ThreadedEchoServer{
private ServerSocket server;
private Socket incoming;
public static int i = 1;//计算客户端连接个数
public ThreadedEchoServer(){
try{
//创建服务器端socket对象
server = new ServerSocket(8888);
out.println("服务器已启动。。。");
//处理多个客户端请求
while(true){
//得到客户端的请求
incoming = server.accept();
//声明一个多线程对象
Runnable run = new ThreadedEchoHandler(incoming);
Thread thread = new Thread(run);
//启动多线程对象
thread.start();
out.println("当前客户端请求数:" + i);
i++;
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
new ThreadedEchoServer();
}
}
/**
功能:处理客户端的输入
*/
class ThreadedEchoHandler implements Runnable{
private Socket incoming;
public ThreadedEchoHandler(Socket incoming){
this.incoming = incoming;
}
public void run(){
try{
try{
//从客户端得到输入流对象
InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();
//读到内存,然后在客户端显示出来
Scanner scanner = new Scanner(inStream);
PrintWriter output = new PrintWriter(outStream,true/*自动刷新*/);
output.println("你好!远程主机的连接已创建。。。");//给客户端显示已经建立会话
//在服务端响应客户端的请求
boolean done = false;
while(!done && scanner.hasNextLine()){
String line = scanner.nextLine();
output.println("响应:" + line);
//如果客户端输入"BYE"
if(line.trim().equals("BYE")){
//那么在服务端标识客户端请求结束(或者会话结束),于是退出循环。
done = true;
}
}
}finally{
//关闭客户端会话
incoming.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
运行效果
可中断Sockets
在实际的交互应用中,如果我们希望给用户选择是否取消socket连接时,那么我们需要使用可中断socket技术—使用SocketChannel来实现。因为,一个channel中没有关联的流,相反,它只是读或者写一个缓存的对象,所以可以实现中断的操作动作。
ShowInterruptibleSocket类演示代码
package com.jb.arklis.net;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.nio.channels.*;
import java.awt.event.*;
/**
功能:书写一个应用类,用来演示中断Socket对象
作者:技术大黍
备注:
在实际中,这样写可以让客户端用户中断socket连接对象
*/
public class ShowInterruptibleSocket{
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
new InterruptibleSocketFrame();
}
});
}
}
/**
功能:书写一个演示中断Socket请求的窗体
*/
class InterruptibleSocketFrame extends JFrame{
private Scanner scanner;
private JButton interruptibleButton;
private JButton blockingButton;
private JButton cancelButton;
private JTextArea messages;
private TestServer server;
private Thread connectThread;
private static final int WIDTH = 550;
private static final int HEIGHT = 450;
/**
功能:定义一个内部类用来演示中断客户端的请求
*/
class TestServer implements Runnable{
public void run(){
try{
ServerSocket server = new ServerSocket(8888);
//处理多个客户端的请求
while(true){
Socket incomfing = server.accept();
Runnable run = new TestServerHandler(incomfing);
Thread thread = new Thread(run);
thread.start();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
功能:书写一个处理请求Socket对象
*/
class TestServerHandler implements Runnable{
private Socket incoming;
private int counter;
public TestServerHandler(Socket incoming){
this.incoming = incoming;
}
public void run(){
try{
OutputStream outStream = incoming.getOutputStream();
PrintWriter output = new PrintWriter(outStream,true/*自动刷新*/);
try{
while(counter < 100){
counter++;
if(counter <= 10){
output.println(counter);
}
Thread.sleep(100);
}
}finally{
incoming.close();
messages.append("关闭服务!\n");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
public InterruptibleSocketFrame(){
setTitle("演示中断客户端的请求动作");
setSize(WIDTH,HEIGHT);
init();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
private void init(){
Container container = getContentPane();
//使用面板来布局
JPanel northPanel = new JPanel();
container.add(northPanel,BorderLayout.NORTH);
//初始化成员变量
messages = new JTextArea();
container.add(new JScrollPane(messages),BorderLayout.CENTER);
interruptibleButton = new JButton(" 中断 ");
blockingButton = new JButton(" 锁定 ");
northPanel.add(interruptibleButton);
northPanel.add(blockingButton);
interruptibleButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
interruptibleButton.setEnabled(false);
blockingButton.setEnabled(false);
cancelButton.setEnabled(true);
//启动多线程
connectThread = new Thread(new Runnable(){
public void run(){
try{
connectInterruptibly();
}catch(Exception e){
e.printStackTrace();
messages.append("connectInterruptibly()呼叫失败:" + e);
}
}
});
//启动多线程
connectThread.start();
}
});
//给blockingButton按钮添加事件处理方法
blockingButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
interruptibleButton.setEnabled(false);
blockingButton.setEnabled(false);
cancelButton.setEnabled(true);
connectThread = new Thread(new Runnable(){
public void run(){
try{
connectBlocking();
}catch(Exception e){
messages.append("connectBlocking方法呼叫失败:" + e);
}
}
});
connectThread.start();
}
});
cancelButton = new JButton("取消");
cancelButton.setEnabled(false);
northPanel.add(cancelButton);
cancelButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
//关闭线程
connectThread.interrupt();
cancelButton.setEnabled(false);
}
});
//开始一个服务端线程
server = new TestServer();
new Thread(server).start();
}
/**
使用中断的方式连接测试服务器
*/
public void connectInterruptibly()throws IOException{
messages.append("可中断连接服务器:\n");
SocketChannel channel = SocketChannel.open(new InetSocketAddress("localhost",8888));
try{
//从管道中读取到内存
scanner = new Scanner(channel);
//如果当前线程没有被中止
while(!Thread.currentThread().isInterrupted()){
//读取网络流对象
messages.append("正在读取中。。。\n");
if(scanner.hasNextLine()){
String line = scanner.nextLine();
messages.append(line);
messages.append("\n");
}
}
}finally{
channel.close();
//使用多线程来异步说明读取完毕
EventQueue.invokeLater(new Runnable(){
public void run(){
messages.append("管道已关闭\n");
interruptibleButton.setEnabled(true);
blockingButton.setEnabled(true);
}
});
}
}
/**
完成客户端的锁定动作
*/
public void connectBlocking()throws IOException{
messages.append("锁定中:\n");
Socket socket = new Socket("localhost",8888);
try{
scanner = new Scanner(socket.getInputStream());
while(!Thread.currentThread().isInterrupted()){
messages.append("客户端读取中\n");
if(scanner.hasNextLine()){
String line = scanner.nextLine();
messages.append(line);
messages.append("\n");
}
}
}finally{
socket.close();
EventQueue.invokeLater(new Runnable(){
public void run(){
messages.append("Socket已关闭\n");
interruptibleButton.setEnabled(true);
blockingButton.setEnabled(true);
}
});
}
}
}
发送邮件
下面我们介绍使用socket编程来向远程网站发送电子邮件。如果要发送MAIL,那么需要让socket连接端口号25也就同SMTP端口号。SMTP是Simple Mail Transport Protocol的缩写,它表示E-MAIL消息的格式。我们可使用socket连接任何一个支持SMTP的服务器。但是,由于现在垃圾邮件的泛滥,所以大多数邮件服务器都内置了检查和可接收用户指定范围的邮件。
如果要使用socket发送邮件,它的步骤如下:
- 打开一个连接主机的socket
- Socket s = new Socket(“mail.yourserver.com”,25);
- PrintWriter out = new PrintWriter(s.getOutputStream());
- 发送如下的流信息
ShowSendMail类演示代码
package com.jb.arklis.net;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.nio.channels.*;
import java.awt.event.*;
/**
功能:书写一个应用类,用来演示Socket对象发送MAIL
作者:技术大黍
备注:
不使用JavaMail API来发送EMail
*/
public class ShowSendMail{
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
new MailSendFrame();
}
});
}
}
/**
书写一个发送Email的窗体
*/
class MailSendFrame extends JFrame{
private Scanner scanner;
private PrintWriter output;
private JTextField from;
private JTextField to;
private JTextField smtpServer;
private JTextArea message;
private JTextArea comments;
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 300;
public MailSendFrame(){
setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
setTitle("自定义的Outlook");
init();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
private void init(){
Container container = getContentPane();
container.setLayout(new GridBagLayout());
container.add(new JLabel("从(From):"), new GBC(0,0).setFill(GBC.HORIZONTAL));
from = new JTextField(20);
container.add(from, new GBC(1,0).setFill(GBC.HORIZONTAL).setWeight(100,0));
container.add(new JLabel("到(To)"),new GBC(0,1).setFill(GBC.HORIZONTAL).setWeight(100,0));
to = new JTextField(20);
container.add(to, new GBC(1,1).setFill(GBC.HORIZONTAL).setWeight(100,0));
container.add(new JLabel("SMTP服务器:"),new GBC(0,2).setFill(GBC.HORIZONTAL).setWeight(100,0));
smtpServer = new JTextField(20);
container.add(smtpServer,new GBC(1,2).setFill(GBC.HORIZONTAL).setWeight(100,0));
message = new JTextArea();
container.add(new JScrollPane(message),new GBC(0,3,2,1).setFill(GBC.BOTH).setWeight(100,100));
comments = new JTextArea();
container.add(new JScrollPane(comments),new GBC(0,4,2,1).setFill(GBC.BOTH).setWeight(100,100));
JPanel buttonPanel = new JPanel();
container.add(buttonPanel, new GBC(0,5,2,1));
JButton sendButton = new JButton(" 发送 ");
buttonPanel.add(sendButton);
sendButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
new SwingWorker<Void, Void>(){
protected Void doInBackground()throws Exception{
comments.setText("");
sendMail();
return null;
}
}.execute();
}
});
}
/**
通过GUI输入的信息进行发送邮件
*/
public void sendMail(){
try{
//创建一个连接smtp服务器的socket对象
Socket socket = new Socket(smtpServer.getText(),25);
//读取socket客户端内容
InputStream inStream = socket.getInputStream();
//向socket客户端写出内容
OutputStream outStream = socket.getOutputStream();
scanner = new Scanner(inStream);
output = new PrintWriter(outStream,true/*自动刷新*/);
String hostName = InetAddress.getLocalHost().getHostName();
//先接收
receive();
//再发送
send("HELO " + hostName);
//再接收
receive();
send("MAIL FROM: <" + from.getText() + ">");
//以请求响应的方式循环
receive();
send("RCPT TO: <" + to.getText() + ">");
receive();
send("DATA");
receive();
send(message.getText());
send(".");
receive();
socket.close();//关闭与服务器的请求
}catch(Exception e){
e.printStackTrace();
}
}
/**
发送一个字符串到socket对象,然后响应到注释文本域
*/
public void send(String content)throws IOException{
comments.append(content);
comments.append("\n");
//把结束符替换成Mail服务器要求的格式
output.print(content.replaceAll("\r","\r\n"));
output.print("\r\n");
output.flush();
}
/**
接收MAIL服务器的内容
*/
public void receive()throws IOException{
String line = scanner.nextLine();
comments.append(line);
comments.append("\n");
}
}
使用URL连接
URL和URLConnection类封装了从远程站点复杂的接收信息的方式。如果我们只是想要获取资源的内容,那么我们可以使用URL类的openStream方法来获取:
URL url = new URL(urlString);
InputStream inStream = url.openStream();
Scanner scanner = new Scanner(inStream );
在java.net包中还有URI类,它没有访问资源的方法,它用来完成解决资源时使用。相对于URL来说,URL类可以打开被请求资源的流。所以,它可以与http:, https:, ftp:,和file:, jar:等联合使用。比如:
maps.yahoo.com/py/maps.py?… ftp://username:password@ftp.yourserver.com/pub/file.txt
如果我们想得到web网站的额外信息,那么需要使用类URLConnection来达到这种目的。该类提供更多的方法来操作Web网站的资源。使用该类有五大步骤:
- 呼叫URL的openConnection初始化该对象
- URLConnection con = url.openConnection();
- 设置请求属性
- 连接远程web网站
- 连接之后使用getter方法得到相应的内容
- 使用getInputStream方法读取远程web服务器响应的的内容
ShowURLConnection类演示代码
package com.jb.arklis.net;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.EventQueue;
import java.awt.event.*;
/**
功能:书写一个演示类用来演示URLConnection的使用
作者:技术大黍
*/
public class ShowURLConnection{
public ShowURLConnection(){
showURLConnetion();
}
private void showURLConnetion(){
try{
String usrlName = "http://horstmann.com/corejava/AllowUserInteractionTest.html";
//String usrlName = "http://java.sun.com";
URL url = new URL(usrlName);
URLConnection connection = url.openConnection(); //初始化URLConnection对象
//设置用户名和密码
String user = "";
String password = "";
String temp = user + ":" + password;
//使用自定义的编码算法来实现
String encoding = base64Encode(temp);
//使用未说明的API进行编码
//String encoding = new sun.misc.BASE64Encoder().encode(temp.getBytes());
connection.setRequestProperty("Authorization","Basic " + encoding);
//连接远程Web服务器
connection.connect();
//读头信息
Map<String,List<String>> headers = connection.getHeaderFields();
for(Map.Entry<String,List<String>> entry : headers.entrySet()){
String key = entry.getKey();
//根据键读对应的值
for(String value : entry.getValue()){
out.println(key + ": " + value);
}
}
out.println("-----------------------------------------------");
out.println("getContentType: " + connection.getContentType());
out.println("getContentLength: " + connection.getContentLength());
out.println("getContentEncoding: " + connection.getContentEncoding());
out.println("getDate: " + connection.getDate());
out.println("getExpiration: " + connection.getExpiration());
out.println("getLastModified: " + connection.getLastModified());
out.println("-----------------------------------------------");
//读到内存中
Scanner scanner = new Scanner(connection.getInputStream());
//把内容显示屏幕上
for(int n = 1; scanner.hasNextLine() && n <= 10; n++){
out.println(scanner.nextLine());
}
if(scanner.hasNextLine()){
out.println(". . .");
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
对字符串进行加密
*/
public String base64Encode(String content){
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
Base64OutputStream output = new Base64OutputStream(byteOut);
try{
output.write(content.getBytes());
output.flush();
}catch(Exception e){
e.printStackTrace();
}
//返回加密的字符串
return byteOut.toString();
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
new ShowURLConnection();
}
});
}
}
/**
功能:实现Base64的编码
备注:
Base64编码就是把3字个字节编码成4个字符。比如|11111122|22223333|33444444|第组都是基于6位
的影射算法。如果输入的字符不是一个3的倍数,那么4个字符的最后一组使用一个或者两个“=”来填充,
第一输出行最多76个字符。
*/
class Base64OutputStream extends FilterOutputStream{
private static char[] toBase64 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'+', '/' };
private int colomn = 0;
private int i = 0;
private int[] inbuffers = new int[3];
public Base64OutputStream(OutputStream output){
super(output);
}
//进行写字符运算
public void write(int ch)throws IOException{
inbuffers[i] = ch;
i++;
//如果i == 3
if(i == 3){//进行位与以及移位运算
super.write(toBase64[(inbuffers[0] & 0xFC) >> 2]);
super.write(toBase64[((inbuffers[0] & 0x03) << 4) | ((inbuffers[1] & 0xF0) >> 4)]);
super.write(toBase64[((inbuffers[1] & 0x0F) << 2) | ((inbuffers[2] & 0xC0) >> 6)]);
super.write(toBase64[inbuffers[2] & 0x3F]);
//移四个字符
colomn += 4;
//计数器归位
i = 0;
//判断最大字符
if(colomn >= 76){
super.write('\n');
colomn = 0;
}
}
}
public void flush()throws IOException{
if(i == 1){
super.write(toBase64[(inbuffers[0] & 0xFC) >> 2]);
super.write(toBase64[(inbuffers[0] & 0x03) << 4]);
super.write('=');
super.write('=');
}else if(i == 2){
super.write(toBase64[(inbuffers[0] & 0xFC) >> 2]);
super.write(toBase64[((inbuffers[0] & 0x03) << 4) | ((inbuffers[1] & 0xF0) >> 4)]);
super.write(toBase64[(inbuffers[1] & 0x0F) << 2]);
super.write('=');
}
if(colomn > 0){
super.write('\n');
colomn = 0;
}
}
}
发送表单数据
上面我们讲解了怎样从一个web服务器读取数据,下面我们来讨论怎样向一个web服务器发送数据,以便web服务器调用。向一个服务器发送数据时,需要用户填写form表单。
当表单数据被发送到一个web服务器之后,使用什么脚本技术来解析是不重要的。因为,客户端使用标准的格式发送数据给一个web服务器,然后web服务器会根据这些标准的格式来进行解析这些数据,然后产生相应的响应动作。其中GET和POST是常用的向web服务器发送信息的命令。 GET命令如下:
每个参数都有一个键/值对来表示。多个参数由“&”分隔,参数值的编码编码是使用URL编码格式。遵守的规则如下:
- 保留A-Z, a-z,0-9和, - * _不变
- 把所有的空白使用”+”字符替换
- 把所有其它字符转换成UTF-8格式,每个字符使用a %跟一个两位十六进制的数值
比如,把街道S.Main编码成S%2e+Main格式,2e是一个十六进制(或者小数46)是ASCII码’.’字符。
使用POST命令埋,我们不是向URL发送参数,而是使用从URLConnection得到一个输出流对象,然后向输出流写键/值对。但是,如果是多参数时,我们仍然使用”&”字符来进行分隔。
ShowPostSendData类演示代码
package com.jb.arklis.net;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.nio.channels.*;
import java.awt.event.*;
/**
功能:书写一个应用类,用来演示向web服务器发送POST方式的数据
作者:技术大黍
*/
public class ShowPostSendData{
public static void main(String[] args){
try{
EventQueue.invokeLater(new Runnable(){
public void run(){
new PostSendFrame();
}
});
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
功能:用来演示向web服务器发送POST方式的数据的窗体
*/
class PostSendFrame extends JFrame{
private JPanel northPanel;
public PostSendFrame(){
setTitle("演示POST方式向web服务器发送数据");
setSize(450,350);
init();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
private void init(){
Container container = getContentPane();
northPanel = new JPanel();
container.add(northPanel,BorderLayout.NORTH);
northPanel.setLayout(new GridLayout(0,2));
northPanel.add(new JLabel("主机(Host):",SwingConstants.CENTER));
final JTextField hostField = new JTextField();
northPanel.add(hostField);
northPanel.add(new JLabel("行为(Action):",SwingConstants.CENTER));
final JTextField actionField = new JTextField();
northPanel.add(actionField);
for(int i = 1; i <= 8; i++){
northPanel.add(new JTextField());
}
final JTextArea result = new JTextArea(20,40);
container.add(new JScrollPane(result),BorderLayout.CENTER);
JPanel southPanel = new JPanel();
container.add(southPanel,BorderLayout.SOUTH);
JButton addButton = new JButton("更多表单元素(More)");
southPanel.add(addButton);
addButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
northPanel.add(new JTextField());
northPanel.add(new JTextField());
pack();
}
});
JButton getButton = new JButton("获取(Get)");
southPanel.add(getButton);
getButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
//在文本域中显示出来
result.setText("");
final Map<String, String> post = new HashMap<String, String>();
//下面是关键代码,指POST向web服务器以键/值对的形式发送表单数据
for(int i = 4; i < northPanel.getComponentCount(); i += 2){
String name = ((JTextField)northPanel.getComponent(i)).getText();
if(name.length() > 0){
String value = ((JTextField)northPanel.getComponent(i + 1)).getText();
post.put(name,value);
out.println("当前有的表单键/值:" + name + " : " + value);
}
}
new SwingWorker<Void,Void>(){
protected Void doInBackground()throws Exception{
try{
String urlString = hostField.getText() + "/" + actionField.getText();
result.setText(doPost(urlString,post));
}catch(IOException e){
result.setText("" + e);
}
return null;
}
}.execute();
}
});
pack();
}
/**
实现具体分析的doPost方法
*/
public String doPost(String urlString, Map<String, String>nameValuePaires)throws IOException{
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
connection.setDoOutput(true);//呼叫该方法让连接对象可以输出
//接下来准备向web服务器发送数据
PrintWriter output = new PrintWriter(connection.getOutputStream());
boolean first = true;
for(Map.Entry<String,String> pair : nameValuePaires.entrySet()){
if(first)
first = false;
else
output.print('&');
String name = pair.getKey();
String value = pair.getValue();
output.print(name);
output.print('=');
//编码为UTF-8格式
output.print(URLEncoder.encode(value,"UTF-8"));
}
output.close();
//得到web服务器的响应信息
Scanner scanner = null;
StringBuilder response = new StringBuilder();
try{
scanner = new Scanner(connection.getInputStream());
}catch(IOException e){
//如果不Http服务器
if(!(connection instanceof HttpURLConnection))
throw e; //抛出异常
InputStream error = ((HttpURLConnection)connection).getErrorStream();
if (error == null)
throw e;
scanner = new Scanner(error);
}
//演示到响应的信息在屏幕中
while(scanner.hasNextLine()){
response.append(scanner.nextLine());
response.append("\n");
}
scanner.close();
//响应到客户端去
return response.toString();
}
}
运行效果
输入项是模拟浏览器中的表单数据;当我们点击“获取”按钮后,得到web服务器响应数据。
总结
很多人天天写Web应用,但是其实真的理解是什么Web应用吗?真的理解表单的概念吗?
嘿嘿,希望我们文章可以真的帮助大家。
最后
记得给大黍❤️关注+点赞+收藏+评论+转发❤️
作者:老九学堂—技术大黍
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。