网路编程常用对象:
InetAddress DatagramPacket DatagramSocket
ServerSocket Socket URL URLConnection
InetAddress用于获取ip地址(本地回环地址:127.0.0.1 主机名:localhost)
import java.net.*;
class IPDemo
{
public static void main(String[] args) throws Exception
{
InetAddress i = InetAddress.getLocalHost();
System.out.println(i.toString());
System.out.println("address:"+i.getHostAddress());
System.out.println("name:"+i.getHostName());
}
}
网络传输协议:TCP和UDP
UDP:
将数据及源和目的封装到数据包中,不需要建立连接,面向无连接。因为无连接,所以是不可靠协议,不需要建立连接速度快,每个数据包的大小限在64k内
TCP:
建立连接,形成传输数据通道,在连接中可以实现大量数据的传输,是可靠协议,因需建立连接,效率稍慢
聊天(UDP传输)
需要用到多线程,一个线程负责发送数据,另一个线程负责接收数据,因为收和发动作是不一致的,所以要定义两个run方法。而且这两个方法要封装到不同的类中。
数据的发送和接收
发送:
1.建立socket服务
2.写入数据并将数据打包为数据包
3.将数据包发送出去
4.关闭资源
接收:
1.建立socket服务,并与指定的发送端相关联
2.创建一个数据包,用来接收数据
3.将接收到的数据进行处理
4.关闭资源
import java.io.*;
import java.net.*;
class Send implements Runnable
{
public void run()
{
DatagramSocket ds = null;
try
{
//建立服务,可以指定一个端口号
ds = new DatagramSocket();
//写入数据,并将数据打包
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
byte[] buf = line.getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("172.25.85.2"),10001);
//将数据包发送出去
ds.send(dp);
}
}
catch (Exception e)
{
throw new RuntimeException("数据发送失败");
}
//关闭资源
ds.close();
}
}
class Rece implements Runnable
{
public void run()
{
try
{
//建立socket服务,并与发送端相关联
DatagramSocket ds = new DatagramSocket(10001);
while (true)//接收端可以一直开着,不用关
{
//创建一个数据包,用来接收一定长度的数据
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//将接收的数据存储到数据包中,并进行相关的操作
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"...."+data);
}
}
catch (Exception e)
{
throw new RuntimeException("数据接收失败");
}
}
}
class ChatDemo
{
public static void main(String[] args) throws Exception
{
new Thread(new Rece()).start();
new Thread(new Send()).start();
}
}
TCP:
建立一个文本转换服务器。
客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。
而且客户度可以不断的进行文本转换。当客户端输入over时,转换结束。
客户端:
1,建立服务。
2,获取键盘录入。
3,将数据发给服务端。
4,后去服务端返回的大写数据。
5,结束,关资源。
都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
服务端:
1.建立服务端,并关联客户端的端口
2.通过accept方法,获取客户端的对象
3.获取客户端的输入流,用来读取来自客户端的数据
4.获取客户端的输出流,用来写反馈给客户端的数据
5.关闭客户端
6.关闭服务端(可选)
import java.io.*;
import java.net.*;
class TransClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("172.25.85.2",10002);
//读取键盘数据的流对象。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//定义目的,将数据写入到socket输出流。发给服务端。
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//定义一个socket读取流,读取服务端返回的大写信息。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line);//自带换行和刷新动作
//读取来自服务端的数据
String str =bufIn.readLine();
System.out.println("server:"+str);
}
bufr.close();
s.close();
}
}
class TransServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10002);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....已连接");
//读取socket输出流中的数据。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//目的:socket输出流。将大写数据写入到socket输出流,并发送给客户端。
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line=bufIn.readLine())!=null)
{
out.println(line.toUpperCase());;
}
s.close();
ss.close();
}
}
该例子出现的问题。
现象:客户端和服务端都在莫名的等待。
因为客户端和服务端都有阻塞式方法。这些方法么没有读到结束标记。那么就一直等,从而导致两端都在等待。
需求:多个客户端同时发送文件
由于服务端的accept方法是一个阻塞式方法,且只有主线程执行,所以只能在一个客户端将代码执行完后才能获取下一个客户端对象,要进行多个客户端同时上传文件,需要多线程
import java.io.*;
import java.net.*;
class PicClient
{
public static void main(String[] args) throws Exception
{
//通过在运行时输入路径的方式传入图片
if(args.length!=1)
{
System.out.println("请输入正确的路径");
return ;
}
//将输入的路径封装成文件对象
File file = new File(args[0]);
if (!(file.exists() && file.getName().endsWith(".bmp")))
{
System.out.println("文件不存在或者不是bmp的文件");
return ;
}
if (file.length()>1024*1024*10)
{
System.out.println("文件过大,请上传小于10M的图片");
return ;
}
Socket s = new Socket("172.25.85.2",10010);
/*
BufferedReader bufr =
new BufferedReader(new InputStreamReader(new FileInputStream(file)));
BufferedWriter bufw =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line = null;
while ((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
不能使用这样的方法进行读写,以一行为单位,会把没有数据的部分也写进去
*/
FileInputStream fis = new FileInputStream(file);
//将图片发送出去
OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len=fis.read(buf))!=-1)
{
out.write(buf,0,len);
}
s.shutdownOutput();//加入一个结束标记
//读取服务端发送过来的数据
InputStream in = s.getInputStream();
byte[] bufin = new byte[1024];
int len1 = in.read(bufin);
System.out.println(new String(bufin,0,len1));
bufr.close();
s.close();
}
}
//把多线程需要执行的代码封装到run方法中
class PicThread implements Runnable
{
private Socket s ;
PicThread(Socket s)
{
this.s = s ;
}
public void run()
{
int count = 1 ;
InetAddress i = s.getInetAddress();
String ip = i.getHostAddress();
System.out.println(ip+".....已连接");
FileOutputStream fos = null;
try
{
//以ip地址命名
File file = new File(ip+"["+(count)+"]"+".jpg");
//判断文件名是否存在,如果存在则将文件名自增后,再判断,直到文件名不在重复
while (file.exists())
{
file = new File (ip+"["+(count++)+"]"+".jpg");
}
InputStream in = s.getInputStream();
fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len=in.read(buf))!=-1)
{
fos.write(buf,0,len);
}
//向客户端发送信息,表示上传成功
OutputStream out = s.getOutputStream();
out.write("数据已上传成功".getBytes());
}
catch (Exception e)
{
throw new RuntimeException("数据上传失败");
}
finally
{
try
{
if(fos!=null)
fos.close();
s.close();
}
catch (Exception e1)
{
throw new RuntimeException("资源关闭失败");
}
}
}
}
class PicServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10010);
while (true)
{
Socket s = ss.accept();
new Thread(new PicThread(s)).start();
}
}
}
URL和URLConnection的应用:
可以直接把地址作为对象传入到URL的构造函数中,URL对象可以通OpenConnection方法创建URLConnection的对象,在URLConnection类中,有很多方法可以用来操作网页中的数据。
GUI和网络编程的相结合
在一个文本框中输入ip地址,通过点击按钮,将该网页中的内容打印到该窗体的文本区域中
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class WebGui
{
private Frame f ;
private TextField tf ;
private TextArea ta ;
private Button b ;
WebGui()
{
init();
}
public void init()
{
f = new Frame("窗体");
f.setBounds(300,150,500,400);
f.setLayout(new FlowLayout());
tf = new TextField(30);
ta = new TextArea(15,60);
b = new Button("转到");
f.add(tf);
f.add(b);
f.add(ta);
event();
f.setVisible(true);
}
public void event()
{
//给按钮添加事件监听
b.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
try
{
show();
}
catch (Exception e1)
{
}
}
});
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void show() throws Exception
{
ta.setText("");
//获取ip地址
String ip = tf.getText();
URL url = new URL(ip);
//获取URLConnection对象
URLConnection con = url.openConnection();
InputStream in = con.getInputStream();
byte[] buf = new byte[1024];
int len = 0 ;
while ((len=in.read(buf))!=-1)
{
//将网页中的数据添加到文本区域中
ta.append(new String(buf,0,len));
}
in.close();
}
public static void main(String[] args)
{
new WebGui();
}
}
总结:在网络编程中较为常用的两种传输协议就是UDP和TCP传输,并且一般网络编程都会用到IO流。
对于UDP传输(发送端,接收端),由于其面向无连接的特性,它传输快,但不可靠,传输数据的大小也有限制,不能超过64k,关于UDP传输的应用,主要有聊天等
对于TCP传输(客户端,服务端),由于它必须要连接,所以具有可靠,传输数据量大的特点,相应的它的效率会稍慢。TCP的服务端想要获取客户端的数据,必须通过accept方法,获取客户端对象,关于TCP传输的应用,有下载等。一般的软件都会具备UDP和TCP两种传输通道