------- android培训、java培训、java学习型技术博客、期待与您交流! ----------
网络编程
IP:当我们一台机器将信息发送到另一台机器,要先找到对方的IP,IP是计算机的地址
端口:,然后将数据发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这个数字,就叫做端口,逻辑端口
协议:它是一种通信规则,国际组织定义了通用协议TCP/IP.
IP编程
IPInternet Protocol(网络之间互连的协议)的缩写,IP地址在java中也有自己的类,这个类在java.net包下是一个InetAddress类,这个类是一个无构造函数的一个类。
这个类常用的方法:
a) static InetAddress getLocalHost():返回本地主机。获取引用对象
b) String getHostName() : 获取此IP 地址的主机名。
c) String getHostAddress() : 回IP 地址字符串(以文本表现形式)。//常用
d) static InetAddress getByName(String host): 在给定主机名的情况下确定主机的IP 地址。获取引用对象
e) String toString():将此 IP 地址转换为String
UDP和TCP特点
UDP特点
a) 面向无连接,即将数据及源和目的封装成数据包,不需要建立连接
b) 数据包限制,即每个数据包的大小限制在64k。
c) 不可靠协议,因为无连接,是不可靠协议
d) 速度快,因为不需要建立连接,速度很快,但容易丢包
TCP特点:
a) 面向连接,建立连接,形成传输数据的同道
b) 大数据传输,在连接中进行大数据传输,不用封装包
c) 可靠协议,通过三次握手完成连接
d) 效率较低,必须建立连接,效率会稍低
UDP网络编程
UDP发送端
Socket是一种现实中的插槽或者插座的意思,为网络服务提供的一种机制
当我们发送一段信息时,UDP的发送过程:
1. 建立UDPSocket服务。
使用的是DatagramSocket对象
2. 提供数据,并将数据封装到数据包中
a) 数据:是一个byte[]数组存放的内容
b) 数据包,使用的是DatagramPacket对象,
构造方法是DatagramPacket(byte[] byf,intlength,InetArreess address,int port)
即(数据,数据的长度,要发送的主机(主机或IP),端口号)
3. 通过Socket服务的发送功能,将数据包发出去
DatagramSocket的send(数据包对象)方法
4. 关闭资源
我们再用代码来演示下具体过程:
importjava.net.*;
class UPDSend
{
public static void main(String[] args)throws Exception
{
//过程1:创建一个UDP服务,通过DatagramSocket对象
DatagramSocket ds=newDatagramSocket();
//过程2-1:创建一组数据
byte[] buf="Hello worldwelcome".getBytes();
//过程2-2:创建数据包,将数据封装到数据包里,通过DatagramPacket对象
DatagramPacket dp=
newDatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.100"),8080);
//过程3:发送数据,使用DatagramSocket的send()方法。
ds.send(dp);
}
}
UDP接受端
当我们接受udp协议传输数据并处理数据时的过程:
1. 定义UDPSocket服务,通常会监听一个端口,其实就是给这个接受网络应用程序定义数字标识,方便与明确哪些数据过来,该应用程序可以处理
使用DatagramSocket的带参数的对象,此参数传入的一定要是端口号。
DatagramSocket(int port):创建数据报套接字并将其绑定到本地主机上的指定端口
2. 定义一个数据包,因为要存储接受到的字节数据,因为数据包对象中有更多功能可以提取字节数据中的不同数据信息
a) 数据:byte[] buf=new byte[1024];用来存储数据的空间
b) 数据包:DatagramPacket(byte[] buf, int length): 构造DatagramPacket,用来接收长度为 length 的数据包,即(数据空间,所接受的数据长度)
3. 通过Socket服务的received方法将收到的数据存入以定义好的数据包中。
DatagramSocket的receive方法.
4. 通过数据包对象的特有功能,将这些不同的数据取出,打印在目的地。
常用的方法:
a) byte[] getData() :返回数据缓冲区,即接受的数据
b) InetAddress getAddress():返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的
c) int getLength():返回将要发送或接收到的数据的长度。
d) int getPort() :返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
5. 关闭资源
ds.close();
我们用代码基本演示如下:
import java.net.*;
class UDPRec
{
public staticvoid main(String[] args) throws Exception
{
//过程1:创建一个UDP服务,建立一个绑定主机的端口
DatagramSocketds=new DatagramSocket(8080);
//过程2-1:定义一个要存储数据的空间
byte[]buf=new byte[1024];
//过程2-2:定义一个数据包,存储接受数据,并将之封装成对象
DatagramPacketdp=new DatagramPacket(buf,buf.length);
//过程3:通过Socket服务将已接受的数据存入到已定义好的数据包中
ds.receive(dp);//阻塞式方法
//过程4:通过数据包DatagramPacket的特有功能,
//将这些数据包取出来打印在控制台上
Stringip=dp.getAddress().getHostAddress();//获得之际IP号
Stringdata=new String(dp.getData(),0,dp.getLength());//接受数据,和数据长度
intport=dp.getPort();//获得端口号
System.out.println("ip"+ip+".....接受到的数据:"+data+"来自"+port+"端口号");
//过程5:关闭资源,
ds.close();
}
}
UDP的应用
我们现在将发送端和接收端写一个聊天小程序。这个程序分为两部分。一个是收数据的部分,一个是发数据的部分。这两个部分要同时执行。我们需要通过多线程技术。一个线程控制收,一个线程控制发。代码演示如下:
importjava.net.*;
importjava.io.*;
/*
发送线程
*/
classSend implements Runnable
{
private DatagramSocket ds;
public Send(DatagramSocket ds)
{
this.ds=ds;
}
public void run()
{
try
{
BufferedReader br=
newBufferedReader(new InputStreamReader(System.in));
String line=null;
while((line=br.readLine())!=null)
{
if ("886".equals(line))
break;
byte[]buf=line.getBytes();
DatagramPacketdp=newDatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),8080);
ds.send(dp);
}
br.close();
}
catch (Exception e)
{
throw newRuntimeException("发送失败");
}
}
}
/*
接受线程
*/
classRec implements Runnable
{
private DatagramSocket ds;
public Rec(DatagramSocket ds)
{
this.ds=ds;
}
public void run()
{
try
{
while (true)
{
byte[]buf=new byte[1024];
DatagramPacket dp=newDatagramPacket(buf,buf.length);
ds.receive(dp);
Stringip=dp.getAddress().getHostAddress();
Stringdata=new String(dp.getData(),0,dp.getLength());
System.out.println("来自"+ip+"的会话:"+data);
}
}
catch (Exception e)
{
throw new RuntimeException("接受失败");
}
}
}
classChatDemo
{
public static void main(String[] args)throws Exception
{
DatagramSocket send=newDatagramSocket();
DatagramSocket rec=newDatagramSocket(8080);
//开启发送线程
new Thread(newSend(send)).start();
//开启接受线程
new Thread(newRec(rec)).start();
}
}
TCP网络编程
TCP分为客户端和服务端,客户端,它们分别对应的对象是Socket,服务器端对应的是SeverSocket
TCP客户端
通过查阅Socket对象,发现在该对象建立时,就可以去连接指定主机,因为TCP是面向连接的,所以在建立Socket服务时,就要有服务器端存在,并连接成功。形成通路后,在该通道进行数据的传输。
步骤:
1.建立Socket服务,并指定要连接的主机和端口。
2.获取Socket流中的输出流,将数据写到该流中。通过网络发送给服务端
3.关闭客户端资源
代码演示
import java.net.*;
import java.io.*;
class TCPClient
{
publicstatic void main(String[] args) throws Exception
{
//创建客户端,指定目的主机和端口号
Socketsoc=new Socket("192.168.1.100",10001);
//为了发送数据,应该获取Socket流中的输出流
OutputStreamout=soc.getOutputStream();
out.write("Helloworld welcome".getBytes());
soc.close();
}
}
TCP服务端
定义端点接收数据并打印在控制台上。
步骤:
1. 建立服务器端的Socket服务,即SeverSocket对象,并监听一个端口
2. 获取连接过来的客户端对象。
通过ServerSocket的accpet方法。没有连接就会等,所以这个方法是阻塞式的。
3. 客户端如果发过来数据,那么服务器端要使用对应的客户端对象,并获取到该客户端的读取流来读取发过来的数据。并打印在控制台上
4. 关闭服务端(可选的,一般不会关)。
代码演示:
import java.io.*;
import java.net.*;
class TCPServer
{
public staticvoid main(String[] args) throws Exception
{
//创建服务器
ServerSocketss=new ServerSocket(10001);
//使用SeverSocket方法来接受来自于端口的信息
Sockets=ss.accept();
//获得信息来源的主机
Stringip=s.getInetAddress().getHostAddress();
System.out.println(ip+"connect......");
//获得客户端的信息
InputStreamin=s.getInputStream();
byte[]buf=new byte[1024];
intlen=in.read(buf);
Stringdata=new String(buf,0,len);
System.out.println(data);
ss.close();
}
}
客户端和服务器器交互
客户端和服务器的原理是:两者都互相在发送信息,即跟人与人打电话一样,有听筒和话筒。
我们用代码来演示下:
import java.io.*;
importjava.net.*;
/*
客户端
*/
classClient
{
public static void main(String[] args)throws Exception
{
Socket s=newSocket("192.168.1.100",10009);
//发送给服务器消息
OutputStreamout=s.getOutputStream();
out.write("来自客户端消息:我来了".getBytes());
//接受服务器消息
InputStream in=s.getInputStream();
byte[] buf=new byte[1024];
int len=in.read(buf);
String data=newString(buf,0,len);
System.out.println(data);
s.close();
}
}
/*
服务器端
*/
classServer
{
public static void main(String[] args)throws Exception
{
ServerSocket ss=newServerSocket(10009);
System.out.println("等待客户端......");
Socket s=ss.accept();
Stringip=s.getInetAddress().getHostAddress();
System.out.println(ip+"......connect");
//接受客户端消息
InputStreamin=s.getInputStream();
byte[] buf=new byte[1024];
int len=in.read(buf);
String data=newString(buf,0,len);
System.out.println(data);
//发送给客户端消息
OutputStreamout=s.getOutputStream()
out.write("来自服务器端的消息:收到".getBytes());
ss.close();
}
}
网络编程与IO流交互应用:
应用1:建立一个文本转换器,客户端给服务器端发送文本,服务端会将文本转换成大写再返回给客户端。而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束。服务器也结束。
分析思路:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作的规律来思考。
源:键盘录入
目的:网络设备,网络输出流
而且操作的是文本数据,可以选择字符流。
服务端:
源:Socket读取流
目的:Socket输出流
都是文本,装饰
代码演示如下:
importjava.net.*;
importjava.io.*;
/*
客户端
*/
classTransClient
{
public static void main(String[] args)throws Exception
{
System.out.println("请输入你要转换的字母:");
Socket s=newSocket("192.168.1.100",9999);
//从键盘上读取字符
BufferedReader br=
new BufferedReader(new InputStreamReader(System.in));
//从服务器端接受的信息
BufferedReader in=
new BufferedReader(newInputStreamReader(s.getInputStream()));
//第一种方式;向服务器端发送信息
//BufferedWriter out=
//new BufferedWriter(newOutputStreamWriter(s.getOutputStream()));
//第二种种方式;向服务器端发送信息
PrintWriter pw=newPrintWriter(s.getOutputStream(),true);
String line=null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
pw.println(line);
//out.write(line);
//out.newLine();//必须换行,否则一直等待
//out.flush();//必须刷新
Stringstr=in.readLine();
System.out.println("sever:"+str);
}
br.close();
s.close();
}
}
/*
服务器端
*/
classTransServer
{
public static void main(String[] args)throws Exception
{
ServerSocket ss=newServerSocket(9999);
System.out.println("等待客户端。。。。。");
Socket s=ss.accept();
Stringip=s.getInetAddress().getHostAddress();
System.out.println(ip+"......connect");
//第一种方式:向客户端发送信息
//BufferedWriter out=
//new BufferedWriter(newOutputStreamWriter(s.getOutputStream()));
//第二种方式:向客户端发送信息
PrintWriter pw=newPrintWriter(s.getOutputStream(),true);
//从客户端接受信息
BufferedReader in=
new BufferedReader(newInputStreamReader(s.getInputStream()));
String line=null;
while((line=in.readLine())!=null)
{
System.out.println(line);
pw.println(line.toUpperCase());
//out.write(line.toUpperCase());//转换大写
//out.newLine();//必须换行
//out.flush();//必须刷新
}
ss.close();
}
}
该例子出现的问题.
现象:客户端和服务端都在莫名的等待、
原因:因为客户端和服务端都有阻塞式方法。,这些方法没有读到结束标记,那么就一直等待,而导致两端,都在等待。我们最好在向对方发送消息时,使用PrintWriter对象,因为它可以接受字符和字节,方法println()有自动刷新换行功能。
应用2:上传文件,建立一个服务器,并给给服务器上传一个文件。
importjava.io.*;
importjava.net.*;
class TestClient
{
public static void main(String[] args)throws Exception
{
Socket s=newSocket("192.168.1.100",8888);
BufferedReader br=newBufferedReader(new FileReader("IPDemo.java"));
BufferedReader in=
new BufferedReader(newInputStreamReader(s.getInputStream()));
PrintWriter out=newPrintWriter(s.getOutputStream(),true);
String line=null;
while((line=br.readLine())!=null)
{
out.println(line);
}
//out.println("over");//结束标记1,否则一直读取
s.shutdownOutput();//结束标记2,否则一直读取
String news=in.readLine();
System.out.println(news);
br.close();
s.close();
}
}
class TestServer
{
public static void main(String[] args)throws Exception
{
System.out.println("等待客户端.......");
ServerSocket ss=newServerSocket(8888);
Socket s=ss.accept();
Stringip=s.getInetAddress().getHostAddress();
System.out.println(ip+"........connect");
BufferedReader in=
new BufferedReader(newInputStreamReader(s.getInputStream()));
PrintWriter pw=newPrintWriter(new FileWriter("server.txt"),true);
PrintWriter out=newPrintWriter(s.getOutputStream(),true);
String line=null;
while((line=in.readLine())!=null)
{
//if("over".equals(line))
//break;
pw.println(line);
}
out.println("上传成功");
pw.close();
ss.close();
}
}
网络编程与多线程交互
我们发现上面例子服务端有个局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程,这时B客户端连接只有等待,因为服务端还没有处理完A客户端的请求,还没有循环回来执行下次accept方法,所以暂时获取不到B客户端对象。那么为了可以让多个客户端同时并发访问服务端,那么服务端最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求。
如何去定义线程呢?只要明确了每一个客户端要在服务端执行的代码即可,将该代码存入run方法中、
应用1:客户端上传MP3文件的代码演示:
importjava.io.*;
importjava.net.*;
/*
客户端
*/
class Mp3Clinet
{
public static void main(String[] args)throws Exception
{
//获得客户端的ip地址
StringhostIp=InetAddress.getLocalHost().getHostAddress();
Socket s=newSocket(hostIp,900);
//判断输入的长度是否为1
if (args.length!=1)
{
System.out.println("你还没有选择你要上传图片的路径");
return;
}
File file=new File(args[0]);
if(!(file.exists()||file.isFile())
{
System.out.println("你上传的文件不存在或者不是文件");
return;
}
//判断文件是否大于8m
if(file.length()>1024*1024*8)
{
System.out.println("对不起你上传的长度过大");
return;
}
if(!(file.getName().endsWith(".mp3")))
{
System.out.println("对不起,你上传的文件不是MP3格式,请重新选择");
return;
}
System.out.println("正在上传歌曲....");
BufferedInputStream bis=
new BufferedInputStream(newFileInputStream(file));
BufferedOutputStream out=
newBufferedOutputStream(s.getOutputStream());
BufferedReader in=
new BufferedReader(newInputStreamReader(s.getInputStream()));
byte[] buf=new byte[1024];
int len=0;
while((len=bis.read(buf))!=-1)
{
out.write(buf,0,len);
}
s.shutdownOutput();//结束标记
String news=in.readLine();
System.out.println(news);
bis.close();
s.close();
}
}
/*
线程
*/
class Mp3Server
{
public static void main(String[] args)throws Exception
{
ServerSocket ss=newServerSocket(900);
while (true)
{
System.out.println("等待客户端.......");
Sockets=ss.accept();
new Thread(newThreadMp3(s)).start();
}
}
}
/*
服务器端线程
*/
classThreadMp3 implements Runnable
{
private Socket s;
ThreadMp3(Socket s)
{
this.s=s;
}
public void run()
{
int count=0;
Stringip=s.getInetAddress().getHostAddress();
try
{
System.out.println(ip+".......connect");
File file=newFile(ip+".mp3");
while(file.exists())
{
file=newFile(ip+"("+(count++)+")"+".mp3");
}
BufferedOutputStreambos=
newBufferedOutputStream(new FileOutputStream(file));
BufferedInputStreamin=new BufferedInputStream(s.getInputStream());
PrintWriter out=newPrintWriter(s.getOutputStream(),true);
byte[] buf=newbyte[1024];
int len=0;
while((len=in.read(buf))!=-1)
{
bos.write(buf,0,len);
}
out.println("上传成功");
bos.close();
}
catch (Exception e)
{
throw newRuntimeException(ip+"上传失败");
}
}
}
应用2:客户端并发登录
题目要求:客户端通过键盘录入用户名,服务端对这个用户名进行校验,如果该用户存在,在服务端显示***,已登录。并在客户端显示***,欢迎光临。
如果改用不存在,在服务端显示***,尝试登录,并在客户端显示***,该用户不存在。最多就登录三次。
代码演示如下:
importjava.net.*;
importjava.io.*;
classUserClient
{
public static void main(String[] args)throws Exception
{
Socket s=newSocket("192.168.1.100",1111);
BufferedReader br=
new BufferedReader(newInputStreamReader(System.in));
BufferedReader in=
new BufferedReader(newInputStreamReader(s.getInputStream()));
PrintWriter out=newPrintWriter(s.getOutputStream(),true);
for (int i=0;i<3;i++)
{
String name=br.readLine();
//用户名为空跳出循环,用于登录成功后会出现的问题
if (name==null)
break;
out.println(name);
String news=in.readLine();
System.out.println(news);
//如果服务器返回的有欢迎字就跳出循环
if (news.contains("欢迎"))
break;
}
br.close();
s.close();
}
}
classUserThread implements Runnable
{
private Socket s;
UserThread(Socket s)
{
this.s=s;
}
public void run()
{
Stringip=s.getInetAddress().getHostAddress();
System.out.println(ip+"...........connect");
try
{
for (inti=0;i<3;i++ )
{
BufferedReaderbr=new BufferedReader(new FileReader("user.txt"));
BufferedReaderin=
newBufferedReader(new InputStreamReader(s.getInputStream()));
Stringname=in.readLine();
PrintWriterout=new PrintWriter(s.getOutputStream(),true);
Stringinfo=null;
booleanflag=false;//标记
while((info=br.readLine())!=null)
{
//如果用户名和信息一样
if(name.equals(info)){
flag=true;
break;
}
}
if (flag)
{
System.out.println(ip+"登录成功");
out.println("欢迎"+name+"进入系统");
break;
}else
{
out.println("你好,此用户名不存在,登录失败");
System.out.println(ip+"正在尝试登录。。。。");
}
}
s.close();
}
catch (Exception e)
{
throw newRuntimeException("登录异常");
}
}
}
class UserServer
{
public static void main(String[] args)throws Exception
{
ServerSocket ss=newServerSocket(1111);
System.out.println("等待客户端.......");
while(true){
Socket s=ss.accept();
new Thread(newUserThread(s)).start();
}
}
}
网络支持
网络的一个常用对象就是URL,类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。
它的常用方法是什么呢?
a) String getProtocol():获取此 URL 的协议名称。
b) String getHost():获取此 URL 的主机名(如果适用)。
c) int getPort() :获取此 URL 的端口号。
d) String getFile() :获取此 URL 的文件名。
e) String getPath():获取此 URL 的路径部分。
f) String getQuery():获取此 URL 的查询部分。
g) URLConnection openConnection():返回一个URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
URLConnection的一个方法:InputStreamgetInputStream():返回从此打开的连接读取的输入流。
我们简单代码演示下:
classURLDemo
{
public static void main(String[] args)throws Exception
{
URL u=new URL("http://www.baidu.com");
//URL u=newURL("HTTP://192.168.1.100:7777/myweb/1.html?name=haha&age=13");
System.out.println("协议:"+u.getProtocol());
System.out.println("主机地址:"+u.getHost());
System.out.println("端口号:"+u.getPort());
System.out.println("文件路径:"+u.getPath());
System.out.println("文件名:"+u.getFile());
System.out.println("额外信息:"+u.getQuery());
URLConnectionco=u.openConnection();//连接指定的URL
System.out.println(co);
InputStreamin=co.getInputStream();
byte[] buf=new byte[1024];
int len=0;
while((len=in.read(buf))!=-1)
{
System.out.println(newString(buf,0,len));//打印网页的HTML文本
}
}