原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。
http://52android.blog.51cto.com/2554429/482954
Java对于网络通讯有着非常强大的支持。不仅可以获取网络资源,传递参数到远程服务器,还可以通过Socket对象实现TCP协议,通过DatagramSocket对象实现UDP协议。同时,对于多点广播以及代理服务器也有着非常强大的支持。以下是本人在学习过程中的总结和归纳。
1. Java的基本网络支持
1.1 InetAddress
Java中的InetAddress是一个代表IP地址的对象。IP地址可以由字节数组和字符串来分别表示,InetAddress将IP地址以对象的形式进行封装,可以更方便的操作和获取其属性。InetAddress没有构造方法,可以通过两个静态方法获得它的对象。代码如下:
|
1.2 URLDecoder和URLEncoder
这两个类可以别用于将application/x-www-form-urlencoded MIME类型的字符串转换为普通字符串,将普通字符串转换为这类特殊型的字符串。使用URLDecoder类的静态方法decode()用于解码,URLEncoder类的静态方法encode()用于编码。具体使用方法如下。
|
1.3 URL和URLConnection
URL可以被认为是指向互联网资源的“指针”,通过URL可以获得互联网资源相关信息,包括获得URL的InputStream对象获取资源的信息,以及一个到URL所引用远程对象的连接URLConnection。
URLConnection对象可以向所代表的URL发送请求和读取URL的资源。通常,创建一个和URL的连接,需要如下几个步骤:
a. 创建URL对象,并通过调用openConnection方法获得URLConnection对象;
b. 设置URLConnection参数和普通请求属性;
c. 向远程资源发送请求;
d. 远程资源变为可用,程序可以访问远程资源的头字段和通过输入流来读取远程资源返回的信息。
这里需要重点讨论一下第三步:如果只是发送GET方式请求,使用connect方法建立和远程资源的连接即可;如果是需要发送POST方式的请求,则需要获取URLConnection对象所对应的输出流来发送请求。这里需要注意的是,由于GET方法的参数传递方式是将参数显式追加在地址后面,那么在构造URL对象时的参数就应当是包含了参数的完整URL地址,而在获得了URLConnection对象之后,就直接调用connect方法即可发送请求。
而POST方法传递参数时仅仅需要页面URL,而参数通过需要通过输出流来传递。另外还需要设置头字段。以下是两种方式的代码。
|
另外需要注意的是,如果既需要读取又需要发送,一定要先使用输出流,再使用输入流。因为远程资源不会主动向本地发送请求,必须要先请求资源。
2. 基于TCP协议的网络编程
TCP协议是一种可靠的通络协议,通信两端的Socket使得它们之间形成网络虚拟链路,两端的程序可以通过虚拟链路进行通讯。Java使用socket对象代表两端的通信端口,并通过socket产生的IO流来进行网络通信。
2.1 ServerSocket
在两个通信端没有建立虚拟链路之前,必须有一个通信实体首先主动监听来自另一端的请求。ServerSocket对象使用accept()方法用于监听来自客户端的Socket连接,如果收到一个客户端Socket的连接请求,该方法将返回一个与客户端Socket对应的Socket对象。如果没有连接,它将一直处于等待状态。通常情况下,服务器不应只接受一个客户端请求,而应该通过循环调用accept()不断接受来自客户端的所有请求。
这里需要注意的是,对于多次接收客户端数据的情况来说,一方面可以每次都在客户端建立一个新的Socket对象然后通过输入输出通讯,这样对于服务器端来说,每次循环所接收的内容也不一样,被认为是不同的客户端。另外,也可以只建立一次,然后在这个虚拟链路上通信,这样在服务器端一次循环的内容就是通信的全过程。
服务器端的示例代码:
|
2.2 Socket
使用Socket可以主动连接到服务器端,使用服务器的IP地址和端口号初始化之后,服务器端的accept便可以解除阻塞继续向下执行,这样就建立了一对互相连接的Socket。
客户端示例代码:
|
2.3 使用多线程
在复杂的通讯中,使用多线程非常必要。对于服务器来说,它需要接收来自多个客户端的连接请求,处理多个客户端通讯需要并发执行,那么就需要对每一个传过来的Socket在不同的线程中进行处理,每条线程需要负责与一个客户端进行通信。以防止其中一个客户端的处理阻塞会影响到其他的线程。对于客户端来说,一方面要读取来自服务器端的数据,另一方面又要向服务器端输出数据,它们同样也需要在不同的线程中分别处理。
具体代码如下,服务器端:
|
客户端:
|
2.4 使用协议字符
协议字符用于标识一些字段的特定功能,用于说明传输内容的特性。它可以由用户自定义。一般情况下,可以定义一个存放这些协议字符的接口。如下:
|
在字段时可以加上这些字符,如下代码:
|
收到发送来的字段时候,也再次拆分成所需要的部分,如下代码:
|
3. UDP协议的网络编程
UDP协议是一种不可靠的网络协议,它在通讯实例的两端个建立一个Socket,但这两个Socket之间并没有虚拟链路,这两个Socket只是发送和接受数据报的对象,Java提供了DatagramSocket对象作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送和接收的数据报。
3.1 使用DatagramSocket发送、接收数据
DatagramSocket本身并不负责维护状态和产生IO流。它仅仅负责接收和发送数据报。使用receive(DatagramPacket p)方法接收,使用send(DatagramPacket p)方法发送。
这里需要首先明确的是,DatagramPacket对象的构造。DatagramPacket的内部实际上采用了一个字节型数组来保存数据,它的初始化方法如下:
|
作为这两个方法的参数,作用和构造不同的。作为接收方法中的参数,DatagramPacket中的数组一个空的数组,用来存放接收到的DatagramPacket对象中的数组;而作为发送方法参数,DatagramPacket本身含有了目的端的IP和端口,以及存储了要发送内容的指定了长度的字节型数组。
另外,DatagramPacket对象还提供了setData(Byte[] b)和Byte[] b= getData()方法,用于设置DatagramPacket中包含的数组内容和获得其中包含数组的内容。
使用TCP和UDP通讯的编码区别:
a. 在TCP中,目标IP和端口由Socket指定包含;UDP中,目标IP由DatagramPacket包含指定,DatagramSocket只负责发送和接受。
b. 在TCP中,通讯是通过Socket获得的IO流来实现;在UDP中,则通过DatagramSocket的send和receive方法。
3.2 使用MulticastSocket实现多点广播
MulticastSocket是DatagramSocket的子类,可以将数据报以广播形式发送到数量不等的多个客户端。实现策略就是定义一个广播地址,使得每个MulticastSocket都加入到这个地址中。从而每次使用MulticastSocket发送数据报(包含的广播地址)时,所有加入了这个广播地址的MulticastSocket对象都可以收到信息。
MulticastSocket的初始化需要传递端口号作为参数,特别对于需要接受信息的端来说,它的端口号需要与发送端数据报中包含的端口号一致。具体代码如下:
|
4. 使用代理服务器
Java中可以使用Proxy直接创建连接代理服务器,具体使用方法如下:
|
5. 编码中的问题总结
a. 双方初始化套接字以后,就等于建立了链接,表示双方互相可以知晓对方的状态。服务器端可以调用接收到的客户端套接字进行输入输出流操作,客户端可以调用自身内部的套接字对象进行输入输出操作。这样可以保持输入输出的流畅性。例如,客户端向服务器端发送消息时,可以隔一段的时间输入一段信息,然后服务器端使用循环不断的读取传过来的输入流。
b. 对于可能出现阻塞的方法,例如客户端进行循环不断读取来自服务器端的响应信息时,如果此时服务器端并没有向客户端进行输出,那么读取的方法将处于阻塞状态,直到收到信息为止才向下执行代码。那么对于这样容易产生阻塞的代码,就需要将它放在一个单独的线程中处理。
c. 有一些流是顺承的。例如,服务器端在收到客户端的消息以后,就将消息再通过输出流向其他所有服务器发送。那么,这个来自客户端的输入流和发向客户端的输出流就是顺接的关系,不必对它们分在两个不同的线程。
d. println()方法对应readLine()。
e. 在JFrame类中,一般不要将自己的代码写进main方法中,可以将代码写到自定义的方法中,然后在main方法中调用。
Java中的DatagramPacket与DatagramSocket的初步
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。
http://sunspot.blog.51cto.com/372554/130313
构造方法:
DatagramSocket():
表示创建一个默认的套接字,并绑定到本地地址和一个随机的端口号
DatagramSocket( int port):
与上面不同的是,绑定到特定端口号,其它不变
DatagramSocket( int port, InetAddress iad):
表示创建一个套接字,绑定到特定的端口号及指定地址
DatagramSocket(SocketAddress sad);
表示创建一个套接字,绑定到特定的套接字地址
小提示:所谓的本地地址我们可以这样来理解,我们通过“控制面板-本地连接-支持”中可以看到一个IP地址,这就是本地的IP地址,也暂且理解为本地地址。
close():
关闭套接字
recevie(DatagramPacket dp):
接受数据报
send(DatagramPacket dp):
发送数据报
这里触及了数据报,下面介绍数据报:
DatagramPacket类:
构造方法:(只列出主要的)
接受类型:
DatagramPacket( byte [] buf, int length):
用来接受长度为length的buf数据(即数据存于字节数组buf中)
发送类型:
DatagramPacket( byte [] buf, int length, InetAddress address, int port);
将length长的buf数据发送到指定的地址的端口号处
DatagramPacket( byte [] buf, int length, SocketAddress address);
将length长的buf数据发送到指定的套接字地址处
有上面可知,我们发送数据时构造的数据报应该是发送类型的,而接受数据报时应该是接受类型的
接收方:Getter.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
public class Getter {
public static void main(String[] args) {
try {
//确定接受方的IP和端口号,IP地址为本地机器地址
InetAddress ip = InetAddress.getLocalHost();
int port = 8888;
//创建接收方的套接字,并制定端口号和IP地址
DatagramSocket getSocket = new DatagramSocket(port,ip);
//确定数据报接受的数据的数组大小
byte[] buf = new byte[1024];
//创建接受类型的数据报,数据将存储在buf中
DatagramPacket getPacket = new DatagramPacket(buf,buf.length);
//通过套接字接收数据
getSocket.receive(getPacket);
//解析发送方传递的消息,并打印
String getMes = new String(buf,0,getPacket.getLength());
System.out.println("对方发送的消息:"+getMes);
//通过数据报得到发送方的IP和端口号,并打印
InetAddress sendIP = getPacket.getAddress();
int sendPort = getPacket.getPort();
System.out.println("对方的IP地址是:"+sendIP.getHostAddress());
System.out.println("对方的端口号是:"+sendPort);
//通过数据报得到发送方的套接字地址
SocketAddress sendAddress = getPacket.getSocketAddress();
//确定要反馈发送方的消息内容,并转换为字节数组
String feedback = "接收方说:我收到了!";
byte[] backBuf = feedback.getBytes();
//创建发送类型的数据报
DatagramPacket sendPacket = new DatagramPacket(backBuf,backBuf.length,sendAddress);
//通过套接字发送数据
getSocket.send(sendPacket);
//关闭套接字
getSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
发送方: Sender.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Sender {
public static void main(String[] args) {
try {
//创建发送方的套接字,IP默认为本地,端口号随机
DatagramSocket sendSocket = new DatagramSocket();
//确定要发送的消息:
String mes = "你好!接收方!";
//由于数据报的数据是以字符数组传的形式存储的,所以传转数据
byte[] buf = mes.getBytes();
//确定发送方的IP地址及端口号,地址为本地机器地址
int port = 8888;
InetAddress ip = InetAddress.getLocalHost();
//创建发送类型的数据报:
DatagramPacket sendPacket = new DatagramPacket(buf,buf.length,ip,port);
//通过套接字发送数据:
sendSocket.send(sendPacket);
//确定接受反馈数据的缓冲存储器,即存储数据的字节数组
byte[] getBuf = new byte[1024];
//创建接受类型的数据报
DatagramPacket getPacket = new DatagramPacket(getBuf,getBuf.length);
//通过套接字接受数据
sendSocket.receive(getPacket);
//解析反馈的消息,并打印
String backMes = new String(getBuf,0,getPacket.getLength());
System.out.println("接受方返回的消息:"+backMes);
//关闭套接字
sendSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
本文出自 “博远至静” 博客,请务必保留此出处http://sunspot.blog.51cto.com/372554/130313
Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
InetAddress ip = null;
while (allNetInterfaces.hasMoreElements())
{
NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
System.out.println(netInterface.getName());
Enumeration addresses = netInterface.getInetAddresses();
while (addresses.hasMoreElements())
{
ip = (InetAddress) addresses.nextElement();
if (ip != null && ip instanceof Inet4Address)
{
System.out.println("本机的IP = " + ip.getHostAddress());
}
}
}
java udp广播代码示例
应用场景: 一个学校,每当下课时间到了提供提示下课功能。
分析:用UDP实现广播向同网段电脑发送数据,客户端接受接受到数据后,弹出提示对话框。服务端需要一个循环来判断时间是否到下课时间,这里如果用死循环,很占系统资源,我测试CUP在100%,用java的Timer类可以很好的解决这个问题,既能保证程序始终运行,又不怎么占系统资源,具体什么原理,没有仔细研究过。下面来看代码具体实现
服务段代码:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
public class UdpSend {
public void sendData()throws SocketException,
UnknownHostException{
DatagramSocket ds = new DatagramSocket();// 创建用来发送数据报包的套接字
String str = "1";
DatagramPacket dp = new DatagramPacket(str.getBytes(),
str.getBytes().length,
InetAddress.getByName("255.255.255.255"), 3001);
// 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号
try {
ds.send(dp);
} catch (IOException e) {
e.printStackTrace();
}
ds.close();
}
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new MyTask(), 1000, 1000);
}
static class MyTask extends java.util.TimerTask{
@Override
public void run() {
UdpSend tt = new UdpSend();
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String strdate = sdf.format(d);
String[] classTime = {"17:18:00","17:19:00","17:20:00"};
for(int i = 0;i<classTime.length;i++){
if(classTime[i].equals(strdate)){
try {
tt.sendData();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
客户单简单代码示例,没有做循环
package udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UdpRecv {
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket(3001);// 创建接收数据报套接字并将其绑定到本地主机上的指定端口
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, 1024);
ds.receive(dp);
String strRecv = new String(dp.getData(), 0, dp.getLength()) + " from "
+ dp.getAddress().getHostAddress() + ":" + dp.getPort();
System.out.println(strRecv);
ds.close();
}
}
java利用udp实现广播[java]
在局域网的两台机器上,一台跑server向广播地址的某端口发,一台做客户机监听某端口。
和多播一样向广播地址发实际就是给路由或者交换机发,通过这些设备在局域网里广播。
代码如下:
package cn.kk.socket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class BroadCastTest {
public static void main(String args[])throws Exception{
//sendBroadcast();
receiveBroadcast();
}
public static void sendBroadcast()throws Exception{
DatagramSocket socket;
DatagramPacket packet;
byte[] data={1,2,3,4};
socket = new DatagramSocket();
socket.setBroadcast(true); //有没有没啥不同
//send端指定接受端的端口,自己的端口是随机的
packet = new DatagramPacket(data,data.length,InetAddress.getByName("255.255.255.255"),8300);
for(int i = 0 ; i < 50 ; i++){
Thread.sleep(1000);
socket.send(packet);
}
}
public static void receiveBroadcast()throws Exception{
byte[] buffer = new byte[65507];
DatagramSocket server = new DatagramSocket(8300);
DatagramPacket packet = new DatagramPacket(buffer , buffer.length);
for(;;){
server.receive(packet);
String s = new String(packet.getData( ), 0, packet.getLength( ));
System.out.println(packet.getAddress( ) + " at port "
+ packet.getPort( ) + " says " + s);
}
}
}
Java发送广播
package socket;
import java.net.*; //导入java.net包
import java.util.Scanner;
public class Weather extends Thread { // 创建类。该类为多线程执行程序
int port = 9898; // 定义端口
InetAddress iaddress = null; // 创建InetAddress对象
MulticastSocket socket = null; // 声明多点广播套接字
Weather() { // 构造方法
try {
iaddress = InetAddress.getByName("225.225.225.255"); // 实例化InetAddress,指定地址
socket = new MulticastSocket(port); // 实例化多点广播套接字
socket.setTimeToLive(1); // 指定发送范围是本地网络
socket.setBroadcast(true);
socket.joinGroup(iaddress); // 加入广播组
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
public void run() { // run()方法
Scanner canScanner=new Scanner(System.in);
while (true) {
DatagramPacket packet = null; // 声明DatagramPacket对象
byte data[] = canScanner.next().getBytes(); // 声明字节数组
packet = new DatagramPacket(data, data.length, iaddress, port); // 将数据打包
try {
socket.send(packet); // 发送数据
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
}
public static void main(String[] args) { // 主方法
Weather w = new Weather(); // 创建本类对象
w.start(); // 启动线程
}
}
JAVA实现广播消息DEMO
http://www.oschina.net/code/snippet_147955_4878
package test;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulticastSender {
private int port;
private String host;
private String data;
public MulticastSender(String data, String host, int port) {
this.data = data;
this.host = host;
this.port = port;
}
public void send() {
try {
InetAddress ip = InetAddress.getByName(this.host);
DatagramPacket packet = new DatagramPacket(this.data.getBytes(),
this.data.length(), ip, this.port);
MulticastSocket ms = new MulticastSocket();
ms.send(packet);
ms.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
int port = 1234;
String host = "224.0.0.1";
String data = "hello world.";
MulticastSender ms = new MulticastSender(data, host, port);
ms.send();
}
}
package test;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulticastListener {
private int port;
private String host;
public MulticastListener(String host, int port) {
this.host = host;
this.port = port;
}
public void listen() {
byte[] data = new byte[256];
try {
InetAddress ip = InetAddress.getByName(this.host);
MulticastSocket ms = new MulticastSocket(this.port);
ms.joinGroup(ip);
DatagramPacket packet = new DatagramPacket(data, data.length);
ms.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println(message);
ms.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
int port = 1234;
String host = "224.0.0.1";
MulticastListener ml = new MulticastListener(host, port);
while(true) {
ml.listen();
}
}
}
MulticastSocket类的使用
import java.net.*;
import java.util.*;
{
}
import java.net.*;
import java.util.*;
public class AMulticastClient
{
}