在介绍这三种编程技术之前,先说一下网络编程的三要素:
- IP地址:网络中计算机的唯一标识
- 端口:正在运行程序的标识(有效的端口区间在0~65535之间,其中0~1024系统使用或保留端口)
- 协议:通信的规则
举一个简单的例子:
我要和某人说话,这个步骤流程是什么?
首先,我要找到说话的对象(通过IP地址找到的);
然后,开始对她说话,怎么说,要对着耳朵说(端口);
说了什么,I love you,她好像听不懂英文,这时候我没必要讲英文了,说汉语就好,(协议)
*
1、UDP编程*
特点如下:
- 把数据打包
- 数据有限制
- 不建立连接
- 速度快
- 不可靠
然后我们通过代码来了解UDP编程:
这段代码是UDP服务端的代码;
public class UdpServer {
public static final int PORT = 30000;
// 定义每个数据报的最大大小为4K
private static final int DATA_LEN = 4096;
// 定义接收网络数据的字节数组
byte[] inBuff = new byte[DATA_LEN];
// 以指定字节数组创建准备接受数据的DatagramPacket对象
/**
* 可存储4K数据的数据报文
*/
private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
// 定义一个用于发送的DatagramPacket对象
private DatagramPacket outPacket;
// 定义一个字符串数组,服务器发送该数组的的元素
String[] books = new String[] { "疯狂Java讲义", "轻量级Java EE企业应用实战",
"疯狂Android讲义", "疯狂Ajax讲义" };
public void init() throws IOException {
try (
// 创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket(PORT)) {
// 采用循环接受数据
for (int i = 0; i < 1000; i++) {
// 读取Socket中的数据,读到的数据放入inPacket封装的数组里。
socket.receive(inPacket);
// 判断inPacket.getData()和inBuff是否是同一个数组
System.out.println(inBuff == inPacket.getData());// -----
// 将接收到的内容转成字符串后输出
System.out.println(new String(inBuff, 0, inPacket.getLength()));
// 从字符串数组中取出一个元素作为发送的数据
byte[] sendData = books[i % 4].getBytes();
// 以指定字节数组作为发送数据、以刚接受到的DatagramPacket的
// 源SocketAddress作为目标SocketAddress创建DatagramPacket。
outPacket = new DatagramPacket(sendData, sendData.length,
inPacket.getSocketAddress());
// 发送数据
socket.send(outPacket);
}
}
}
public static void main(String[] args) throws IOException {
new UdpServer().init();
}
}
这是客户端的代码:
public class UdpClient {
// 定义发送数据报的目的地
public static final int DEST_PORT = 30000;
public static final String DEST_IP = "127.0.0.1";//测试本机
// 定义每个数据报的最大大小为4K
private static final int DATA_LEN = 4096;
// 定义接收网络数据的字节数组
byte[] inBuff = new byte[DATA_LEN];
// 以指定字节数组创建准备接受数据的DatagramPacket对象
private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
// 定义一个用于发送的DatagramPacket对象
private DatagramPacket outPacket = null;
public void init() throws IOException {
try (DatagramSocket socket = new DatagramSocket()) {
// 初始化发送用的DatagramSocket,它包含一个长度为0的字节数组
outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName(DEST_IP), DEST_PORT);
// 创建键盘输入流
Scanner scan = new Scanner(System.in);
// 不断读取键盘输入
while (scan.hasNextLine()) {
// 将键盘输入的一行字符串转换字节数组
byte[] buff = scan.nextLine().getBytes();
// 设置发送用的DatagramPacket里的字节数据
outPacket.setData(buff);
// 发送数据报
socket.send(outPacket);
// 读取Socket中的数据,读到的数据放在inPacket所封装的字节数组里。
socket.receive(inPacket);
System.out.println(new String(inBuff, 0, inPacket.getLength()));
}
}
}
public static void main(String[] args) throws IOException {
new UdpClient().init();
}
}
2、TCP协议
特点如下:
- 需要建立连接通道
- 数据无限制
- 速度慢
可靠
这是服务端代码:
public class Server {
public static void main(String[] args) throws IOException {
// 8888监听的端口号
ServerSocket ss = new ServerSocket(8888);
boolean isFlag = true;
while(isFlag) {
// 与客户端对应的socket, 阻塞
Socket socket = ss.accept();
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.write("服务端");
pw.close();
}
if (!ss.isClosed()) {
ss.close();
}
}
}
这是客户端代码
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
// 10.50.8.40
Socket socket = new Socket("127.0.0.1", 8890);
// System.out.println(br.readLine());
// br.close();
new Thread(new ClientThread(socket)).start();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
// PrintWriter pw = new PrintWriter(socket.getOutputStream());
PrintStream ps = new PrintStream(socket.getOutputStream());
while ((line = br.readLine()) != null) {
// 将用户的键盘输入内容写入Socket对应的输出流
// pw.println(line);
// pw.flush();
System.out.println(line + "----------");
ps.print(line);
ps.flush();
}
// PrintWriter pw = new PrintWriter(socket.getOutputStream());
// pw.println("zhangtao");
// pw.flush();
// pw.close();
}
}
其中类ClientThread代码如下:
package com.gb.android.tcp.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ClientThread implements Runnable {
Socket socket;
BufferedReader br;
public ClientThread(Socket socket) throws IOException {
this.socket = socket;
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
@Override
public void run() {
String line = null;
try {
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、HTTP编程
http网络编程有两种,分别是HttpURLConnection和HttpClient:
首先是HttpURLConnection的用法:
一般只需要new出一个URL对象,并出入目标的网络地址,然后调用一下openConnection()方法即可,如下所示:
URL url=new URL("http://www.baidu.com");
HttpURLConnection connection=(HttpURLConnection)url.openConnection();
得到HttpURLConnection的实例之后,我们就可以设置一下HTTP请求所使用的方法。常用的方法主要有两个,GET和POST。GET表示希望从服务区哪里了获取数据,而POST则表示希望提交数据给服务器。写法如下:
connection.setRequestMethod("GET");
接下来就可以进行一些自由的定制了,比如设置连接超时、读取超时的毫秒数,以及服务器希望得到的一些消息头等。这部分内容根据自己的实际情况进行编写,实例写法如下:
connection.setConnectTimeOut(8000);
connection.setReadTimeOut(8000);
之后调用getInputStream()方法就可以获取到服务器返回的输入流了,剩下的任务就是对流的读取,如下:
InputStream in =connection.getInputStream();
最后就可以调用disconnect()方法将这个HTTP连接关闭掉,如下:
connection.disconnect();
根据上面的步骤,很容易写出一个获取网络数据的代码:
开辟了一个线程处理耗时的网络操作
public class HttpClient {
public void getInfo() {
new Thread(new Runnable() {
public void run() {
URL url;
HttpURLConnection connection = null;
try {
url = new URL("http://www.baidu.com");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setConnectTimeout(8000);
InputStream in = (InputStream) connection.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
HttpClient是Apache提供的HTTP网络访问接口,从一开始的时候就被引入到了androidAPI中,他可以完成和HttpURLConnection巨虎一模一样的效果,但两者也有很大的差别:
HttpClient是一个接口,接口无法创建实例,他会创建DefaultHttpClient的实例,如下:
HttpClient HttpClient=new DefaultHttpClient();
`接着可以创建一个GET请求,就可以创建一个HttpGet对象,并传入网络地址然后调用HttpClient的execute()方法即可:
HttpGet httpGet=new HttpGet(“http://www.baidu.com”);
httpClient.execute(httpClient);
然而POST’请求会比GET请求复杂一些,需要创建一个HttpPost对象,并传入目标网路的地址,如下:
HttpPost httpPost=new HttpPost(“http://www.baidu.com”);
然后通过一个NameValuePair集合来存放带待提交的参数,并将这个参数传入到一个UrlEncodedFormEntity中,然后调用HttpPost的setEntity()方法将构建好的UrlEncodedFormEntity传入,如下:
List<NameValuePair> params= new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username","admin"));
params.add(new BasicNameValuePair("password","123456"));
UrlEncodedFormEntity entity=new UrlEncodedFormEntity(params,"utf-8");
httpPost.setEntity(entity);
接下来和HttpGet是一样的,调用HttpClient的execute()方法,并传入HttpPost对象即可:
httpClient.execute();
执行execute()方法之后就会返回一个HttpResponse对象,服务器返回的所有信息都在这里,通常会先取出服务器的返回状态码,如果是200说明请求和响应是成功的 。如下所示:
if(httpResponse.getStatusLine().getStatusCode()==200){
//请求和响应成功!
}
接下来取出内容可一个调用个getEntity()方法获取到一个HttpEntity实例,然后再用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串;如下:
HttpEntity entity=httpResponse.getEntity();
String response=EntityUtils.toString(entity);
注意乱码问题,如果服务器端返回的数据是带有中文的,直接调用EntityUtils.toString()方法转换会出现乱码,这时候转换的时候将字符集指定成utf-8就可以了,如下:
String response =EntityUtils.toString(entity,“utf-8”);
根据上面的步骤很容易写出一个网络请求数据的代码:
package http;
import javax.xml.ws.Response;
public class HttpClient {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
HttpClient httpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet("http://www.baidu.com");
HttpResponse httpResponse= httpClient.execute();
if(httpResponse.getStatusLine().getStatusCode()==200){
//请求和响应成功!
HttpEntity entity=httpResponse.getEntity();
String response =EntityUtils.toString(entity,“utf-8”);
String info=response.toString();
}
}
}).start();
}
}