---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
一、简介
Java的网络编程提供了两种通讯协议,UDP(数据报协议)与TCP(传输控制协议),网络通讯有三个要素:1、IP地址,2、端口号,3、网络协议
UDP与TCP的区别:
UDP将数据及源和目的封装成数据包,不需要建立链接,每个数据报的大小在现状在64k内,因无连接,所以是不可靠的协议,不需要建立连接,速度快。
TCP建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接,效率会稍低。
二、UDP协议简单例子与知识点
在UDP协议中,一个通讯路径中有两个端点,一个是接收端,一个是发送端。
发送端:实例化UDP连接的类DatagramSocket(端口号(可选)),封装数据报包DatagramPacket,数据报包相当于邮件包,里面需要封装数据,数据的大小,发送的目的地(ip与端口),最后通过send方法发送数据
接收端:实例化UDP连接DatagramSocket(必填) 此时端口号应该明确说明,因为这样才能让发送端更方便的知道接收方的地址,在接受数据之前,需要准备接收数据的容器,也就是DatagramPacket数据报包,接收数据调用receive()方法接收数据,封装到DatagramPacket包中,调用数据报包的方法,获取发送端的信息与数据,有一个小的知识点在这里提下,就是发送端数据报包的IP地址最后一位为255的话,就能够实现广播的效果,例如:192.168.0.255在这个网段的用户如果存在端口号一致的服务,都能接受到数据的。
例子:从键盘上输入一段文字,通过UDP发送给接收端,接收端,接收到数据显示在控制台上
package com.itheima.net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 网络编程之UDP程序设计
* 模拟网络通讯,发送端发送数据,接收端接收到数据并打印在控制台上
*/
public class UDPDemo {
public static void main(String[] args) throws Exception{
//启动程序
new Thread(new UDPReceive()).start();
new Thread(new UDPSend()).start();
}
}
/**
* UDP发送端,把从控制台上接收到的数据发送到接收端
* 发送端实现Runnable接口
*
*/
class UDPSend implements Runnable{
public void run(){
send();
}
//发送数据的方法
private void send(){
try{
//定义UDP发送端
DatagramSocket socket = new DatagramSocket();
//接收控制台输入的数据,因为我的控制台上UTF-8编码会出现乱码,这里我采用GBK编码
BufferedReader mReader = new BufferedReader(new InputStreamReader(System.in,"GBK"));
String line = null;
while((line = mReader.readLine())!=null){
byte[] buf = line.getBytes();
//定义要发送的数据包
DatagramPacket p = new DatagramPacket
/*发送的数据 数据长度 发送到的地址 发送到那个端口*/
(buf, 0, buf.length, InetAddress.getByName("127.0.0.1"), 10000);
socket.send(p);
if(line.equals("bye")){//键盘接收数据不能够停止,所以需要定义一个终止符
break;
}
}
System.out.println("发送端结束...");
}catch(Exception e){e.printStackTrace();}
}
}
class UDPReceive implements Runnable{
public void run(){
try{
//定义UDP接收端,接收端采用多线程,接收端口号为10000的所有数据
DatagramSocket socket = new DatagramSocket(10000);
while(true){
String result = receive(socket);
System.out.println(result);
//加上一下代码是为了终止接收端代码...终止接收端是不合理的。
if(result!=null && result.equals("bye")){
System.out.println("接收端结束...");
break;
}
}
}catch(Exception e){e.printStackTrace();}
}
private String receive(DatagramSocket socket){
try{
//因为UDP接收数据的最大长度为64k,所以定义一个64K为长度的缓冲区
byte[] buf = new byte[64*1024];
//定义数据包,接收数据
DatagramPacket p = new DatagramPacket(buf, buf.length);
socket.receive(p);
//获取数据包中的数据
byte[] result = p.getData();
//得到发送端发送的数据
String data = new String(result,0,result.length);
System.out.println(p.getAddress().getHostName()+p.getPort()+"说:"+data);
if(data.trim().equals("bye")){
return "bye";
}
return null;
}catch(Exception e){e.printStackTrace();return null;}
}
}
三、TCP协议与知识点
Java中使用Socket(套接字)完成TCP程序的开发,使用此类可以方便的建立可靠的、双向的、持续的、点对点的通讯连接
TCP里面有两个重要的类:Socket类与ServerSocket类,ServerSocket类主要用在服务端程序的开发上(服务端一般都是多线程的),用于接收客户端的连接请求,常用的方法:
构造方法:ServerSocket(int port),创建ServerSocket实例,用于监听指定端口
方法:
accept();等待客户端连接,是一个阻塞式方法
getInetAddress();得到IP
close();关闭连接
在服务端每次运行时都会使用accept()方法等待客户端的请求,当有客户端请求过来时,说返回一个Socket对象,该对象就是一个客户端对象,Socket对象常用的方法有:
构造方法:Socket(String host,int port),构造Socket对象,同时指定要连接的服务器主机与端口号
方法:
getInputStream()返回Socket对象的输入流
getOutputStream()返回Socket对象的输出流
close()关闭连接
分析学习下面最简单的TCP程序(这里先使用单线程)
服务端代码
package com.itheima.net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 最简单的一个TCP程序,服务端
* 客户端输入一个小写字符,服务端把大写返回
*/
public class TCPServerDemo1 {
public static void main(String[] args) throws Exception{
//建立服务端
ServerSocket ss = new ServerSocket(10001);
//等待客户端请求
Socket s = ss.accept();
//客户端请求到来,输出客户端信息 主机名
System.out.println(s.getInetAddress().getHostName()+"连接到我的服务....");
BufferedReader mReader = new BufferedReader(new InputStreamReader(s.getInputStream()));
OutputStreamWriter writer = new OutputStreamWriter(s.getOutputStream());
String line = null;
while((line = mReader.readLine())!=null){
//转换为大写发送出去
System.out.println("服务端接收到了数据....");
writer.write(line.toUpperCase());
//写出换行符,表示改行已经结束
writer.write(System.getProperty("line.separator"));
writer.flush();
System.out.println("服务端数据返回。");
}
//释放资源.....
s.close();
ss.close();
}
}
客户端代码
package com.itheima.net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* TCP程序的客户端
* @author Administrator
*
*/
public class TCPClientDemo1 {
public static void main(String[] args) throws Exception{
//向指定主机,这里是本机的指定端口10001发送请求
Socket s = new Socket("127.0.0.1", 10001);
//获取键盘输入
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
//获取网络输出流对象,向服务端发送消息
OutputStreamWriter out = new OutputStreamWriter(s.getOutputStream());
//获取网络输入流对象,获取服务器返回值
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line = reader.readLine())!=null){
System.out.println(line+"---");
out.write(line);
//写出换行符,表示改行已经结束
//当然这里也可以使用BufferedWriter,里面有newLine()方法很好用
//还有就有更简单的方法PrintWriter 构造方法里面有autoFlush这个选项。
out.write(System.getProperty("line.separator"));
out.flush();
System.out.println("客户端发送出了数据");
System.out.println("服务端返回的数据是:"+in.readLine());
}
}
}
下面使用多线程的方式完成服务端代码的编写,支持多线程并发访问
需求:完成图片上传,服务器端支持多个客户端并发上传图片
服务端代码:
package com.itheima.net;
import java.io.BufferedInputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* TCP程序服务端
* 目标:支持多个客户端并发访问
* 技术:Socket、多线程、IO
*/
public class TCPServerDemo2 {
public static void main(String[] args) throws Exception{
//定义服务端
ServerSocket ss = new ServerSocket(10002);
//每个客户端进入,都会开辟一个新的线程,处理
while(true){
//等待客户端连接
Socket s = ss.accept();
//开启线程
new Thread(new Server(s)).start();
}
}
}
final class Server implements Runnable{
private Socket s;//每个服务端线程,对应一个客户端
public Server(Socket s){
this.s = s;
}
@Override
public void run() {
try{
System.out.println(s.getInetAddress().getHostName()+"连接到服务端....");
//得到网络输入流,接收客户端数据
BufferedInputStream in = new BufferedInputStream(s.getInputStream());
//得到文件输出流
PrintStream writer = new PrintStream(System.currentTimeMillis()+".jpg");
byte[] buf = new byte[1024];
int len = -1;
while((len = in.read(buf))!=-1){
writer.write(buf, 0, len);
}
writer.close();
//得到网络输出流,通知客户端,完成了上传
PrintWriter out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true);
out.println("上传成功!");
//下面是释放资源
s.close();
writer.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
客户端代码:
package com.itheima.net;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
//模拟客户端--第一个
public class TCPClientDemo2 {
public static void main(String[] args) throws Exception{
Socket s = new Socket("127.0.0.1", 10002);
//获取网络输入流,接收服务端反馈信息
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//获取网络输出流
PrintStream out = new PrintStream(new BufferedOutputStream(s.getOutputStream()));
//获取读取文件流
BufferedInputStream reader = new BufferedInputStream(new FileInputStream("a.jpg"));
byte[] buf = new byte[1024];
int len = -1;
while((len = reader.read(buf))!=-1){
//模拟大文件
TimeUnit.SECONDS.sleep(1);
out.write(buf,0,len);
out.flush();//晕..我以为资源关闭会自动flush的..测试结果还需要手动flush
}
//结束标志
s.shutdownOutput();
reader.close();
//服务端返回数据
String line = in.readLine();
System.out.println("服务端说:"+line);
s.close();
}
}
我在做测试的时候,复制一份客户端启动,模拟多个客户端并发请求服务端...
最后,还有一个知识点需要点出,就是HttpURLConnection,这个类的使用方法在JavaIO里面有个例子,就是模拟多线程下载文件。
总结:
在网络编程开发思路都是一样的,获取服务端连接,发送请求,得到服务端反馈信息。
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net