Java之网络编程

来源:互联网 发布:英雄联盟mac版 编辑:程序博客网 时间:2024/05/24 07:30



一、概述


1、网络参考模型


2、网络通讯要素
◆IP地址:InetAddress
      网络中设备的标识
      不易记忆,可用主机名
      本地回环地址:127.0.0.1主机名:localhost
◆端口号
      用于标识进程的逻辑地址,不同进程的标识
      有效端口:0~65535,其中0~ 1024系统使用或保留端口。
◆传输协议
      通讯的规则

      常见协议:TCP,  UDP

通讯例子模型:


3、TCP和UDP
UDP
      将数据及源和目的封装成数据包中,不需要建立连接
      每个数据报的大小在限制在64k内
        因无连接,是不可靠协议
      不需要建立连接,速度快
TCP
      建立连接,形成传输数据的通道。
      在连接中进行大数据量传输
      通过三次握手完成连接,是可靠协议(你收到了吗?我收到了。哦,我知道你收到了
        必须建立连接,效率会稍低

.4、Socke

Socket就是为网络服务提供的一种机制。
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过io传输。


二、UDP传输


◆DatagramSocket与DatagramPacket
◆建立发送端,接收端。
◆建立数据包。
◆调用Socket的发送接收方法。
◆关闭Socket 。
◆发送端与接收端是两个独立的运行程序。

1、发送端
在发送端要在数据包对象中明确目的地 IP及端口。
DatagramSocket ds=new DatagramSocket();
byte[]by="hello,udp".getBytes();
DatagramPacket dp=new DatagramPacket(by,0,by.length)
InetAddress.getByName(“127.0.0.1”),10000);
ds·send(dp);
ds.close();

2、接收端
在接收端,要指定监听的端口。
    DatagramSocket ds=new DatagramSocket(10000);
    byte[]by=n ew byte[1024];
    Datag ram Packet dp=new DatagramPacket(by,by.length);
    ds.receive(dp);
    String str=new String(dp.getData(),0,dp.getLength());
    System .out.println(str+”——”+dp.g etAdd ress());
    ds.close();

下面用一个通讯小程序演示

发送端:

package internet;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class UdpSend {public static void main(String[] args) throws IOException {//建立数据借口规则DatagramSocket sendsocket = new DatagramSocket();//创建键录入的读取流BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));String line = null;while ((line = buf.readLine()) != null) {//如果输入over的话就结束if ("over".equals(line))break;//把得到的字符转换成字节,并且放入字节数组byte by[] = line.getBytes();//把数据打包入数据包对象,出入字节数据,和字节数据长度,和确定主机的 IP 地址。,和接收端的端口号DatagramPacket sentpacket = new DatagramPacket(by, by.length,InetAddress.getByName("192.168.0.255"), 7778);//开始发送sendsocket.send(sentpacket);}//关闭流buf.close();//关闭资源sendsocket.close();}}
接收端:

package internet;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class UdpSend {public static void main(String[] args) throws IOException {//建立数据借口规则DatagramSocket sendsocket = new DatagramSocket();//创建键录入的读取流BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));String line = null;while ((line = buf.readLine()) != null) {//如果输入over的话就结束if ("over".equals(line))break;//把得到的字符转换成字节,并且放入字节数组byte by[] = line.getBytes();//把数据打包入数据包对象,出入字节数据,和字节数据长度,和确定主机的 IP 地址。,和接收端的端口号DatagramPacket sentpacket = new DatagramPacket(by, by.length,InetAddress.getByName("192.168.0.255"), 7778);//开始发送sendsocket.send(sentpacket);}//关闭流buf.close();

操作结果为:




三、TCP传输


1TCP分客户端和服务端。客户端对应的对象是Socket,服务端对应的对象是ServerSocket

2、方法:

        ◆创建客户端对象:

              Socket():创建空参数的客户端对象,一般用于服务端接收数据

             Socket(String host,int port),指定要接收的IP地址和端口号

        创建服务端对象:ServerSocket(int port):指定接收的客户端的端口

       Socket accept():监听并接收到此套接字的连接

        void shutdownInput():此套接字的输入流至于“流的末尾”

        void shutdownOutput():禁用此套接字的输出流

        InputStream getInputStream():返回此套接字的输入流,Socket对象调用

        OutputStream getOutputStream():返回套接字的输出流,Socket对象调用

3、基本思路

客户端:

       客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。

        连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。

        与服务端通讯结束后,关闭Socket

服务端:

        服务端需要明确它要处理的数据是从哪个端口进入的。

        当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。

        当该客户端访问结束,关闭该客户端。

4、步骤

客户端:

        通过查阅Socket对象的API文档,发现在该对象在建立时,就可去连接指定主机,因为TCP是面向连接的,所以在建立Socket服务时,就要有服务端存在,并连接成功,形成通路后,再通过该通道进行数据的传输。

         创建Socket服务,并指定要连接的主机端口。通路一建立,就会产生Socket流(包括输入流和输出流),通过方法获取

        为了发送数据,应获取Socket中的输出流,如果要接收服务端的反馈信息,还需要获取Socket的输入流

        通过输出流的write()方法将要发送的数据写入到流中

        关闭Socket流资源

服务端:

        服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。需监听一个端口。

         建立服务端的Socket服务,并监听一个端口。通过ServerSocet带端口参数的构造函数

        获取连接过来的客户对象,通过ServerSocketaccept()方法,此方法是阻塞式的,如果服务端没有连接到就会一直等待

        客户端如果发过来数据,则服务端要使用对应的客户端对象,并获取到该客户端对象的读取流读取发过来的数据,并输出到指定目的地。

        关闭服务端(可选)。一般服务端是常开的,因为在实际应用中,随时有客户端在请求连接和服务。但这里需要定时关闭客户端对象流,避免某一个客户端长时间占用服务器端。

下面通过一个客服端与服务端连接与反馈来说明:

示例1:需求是客服端给服务端一堆小写字母,服务端返回其大写字母

先建立服务端:

package internet;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class TcpTransSever {public static void main(String[] args) throws IOException {//建立服务端接口规则对象,和设定监视端口ServerSocket ssocket = new ServerSocket(18989);//获取到客服端的Socket对象Socket socket=ssocket.accept();//通过Socket对象获得客服端的输入流,并且放入缓冲区BufferedReader bur = new BufferedReader(new InputStreamReader(socket.getInputStream()));//建立打印流,开始自动刷流PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);String line = null;//还是读取while((line=bur.readLine())!=null){//先生对方的数据System.out.println(line);//用打印流把对应的大写字母反馈给客服端pw.println(line.toUpperCase());}//关流bur.close();//关闭资源ssocket.close();}}

再建立客服端:

package internet;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;public class TcpTrans {public static void main(String[] args) throws IOException {//建立规则借口对象,制定ip与端口号Socket socke = new Socket("192.168.0.101",18989);//建立键盘录入BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));//建立输入流接受服务端的反馈BufferedReader buf = new BufferedReader(new InputStreamReader(socke.getInputStream()));//建议打印流把输入的数据给服务端PrintWriter  pw=new PrintWriter(socke.getOutputStream(),true);String line = null;while((line=bf.readLine())!=null){//当输入over表示请求的服务结束if(line.equals("over"))break;pw.println(line);//读取反馈信息System.out.println(buf.readLine());}socke.close();bf.close();}}

结果为:

示例2:

需求是上传图片到服务端,当上传成功后服务端给与客服端反馈信息

建立服务端:

package internet;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class ClientUploadHandle {public static void main(String[] args) throws IOException {//建立服务端口ServerSocket ssocket = new ServerSocket(22346);//获取客服端对象Socket socket = ssocket.accept();//通过客服端对象获取客服端输入流InputStream input = socket.getInputStream();//通过客服端对象获取输出流OutputStream output =socket.getOutputStream();//建立输出流FileOutputStream fos = new FileOutputStream("c://3.jpg");//建立缓存容器用来接受客服端的数据byte by[] =new byte[1024*10];int num=0;while((num=input.read(by))!=-1){//数据加载到输出流fos.write(by, 0,num);}//数据加载完毕,提示用户output.write("恭喜你上传成功".getBytes());//关闭流,关闭资源socket.close();ssocket.close();fos.close();}}
◆建立客服端:

package internet;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.net.UnknownHostException;public class ClientUpload {public static void main(String[] args) throws UnknownHostException, IOException {//建立客服端对象Socket socket = new Socket("192.168.0.101",22346);//把路径文件封装成对象File file = new File("c://1.jpg");//创建输入流FileInputStream fis = new FileInputStream(file);//利用对象获取输出流OutputStream sOut = socket.getOutputStream();//定义缓存容器byte by[]=new  byte[1024*10];int num=0;while((num=fis.read(by))!=-1){sOut.write(by,0,num);}//结束输出,不然客服端循环里的read将会一直等待socket.shutdownOutput();//这个容器是接受服务端的反馈byte buf[]=new byte[1024];//建立输入流InputStream input = socket.getInputStream();int i =input.read(buf);//打印反馈信息System.out.println(new String(buf,0,i));//关闭流socket.close();fis.close();}}
上传成功,客服端收到了反馈信息:



示例三:

这是一个验证客户端输入的用户名是否注册过,并且给出相应处理的小程序

建立服务端:

package internet;import java.io.BufferedReader;import java.io.FileReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;/* 服务端,判断客服端输入的用户名存不存在,如果有就返回恭喜你登录成功 没有的话,提示从新输入,如果输入次数大于五次就断开服务 这里需要考虑到多个客服端请求的情况  */public class TCPLoginServer {public static void main(String[] args) throws Exception {//建立黑名单的对象引用String Blacklist=null;//建立服务端对象,指定窗口ServerSocket server = new ServerSocket(8848);//获得客户端对象Socket socket = server.accept();//判断客户端的ip时候是黑名单中的数据String ip=socket.getInetAddress().getCanonicalHostName();//判断客户端的ip时候是黑名单中的数据,这里先不考虑并发和多个用户等情况,实际情况可用集合或者文本储存if(Blacklist==ip){socket.close();}//获得客户端输入流BufferedReader bfr = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获得客户端输出流PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);//建立读取存档的输入流指向BufferedReader bfr_2 =null;String line=null;String line_2=null;//建立标记boolean flag=false;//建立计数器int mark=0;//开始获得客服端传来的数据while((line=bfr.readLine())!=null){//记录运行次数mark++;//判断运行次数if(mark>=5){//反馈客服端pw.println("您尝试登录的次数已经到了");//把此客户端ip加入黑名单Blacklist=socket.getInetAddress().getCanonicalHostName();//关闭连接socket.close();//跳出循环break;}//把本地数据库加载到输入流bfr_2 = new BufferedReader(new FileReader("c:\\SQL.txt"));//读取数据while((line_2=bfr_2.readLine())!=null){//查找数据库里是否有用户输入的用户名if(line.equals(line_2)){//有的话就反馈客户端登陆成功pw.println("用户:"+line+"您好!"+"恭喜您已经成功登录了");//改掉标记flag=true;//并且跳出循环break;}}if(!flag){pw.println("没有这个用户,请重新输入");}}//关闭流bfr_2.close();}}

◆建立客户端

package internet;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;/* 客户端:输入自己的用户名登录,输入正确与错误都会有提示。超过五次错误本客户端ip将会被加入黑名单 */public class TCPLogin {public static void main(String[] args) throws Exception, Exception {//建立客户端对象Socket socket = new Socket("192.168.0.101",8848);//通过对象获得客户端的输入流 BufferedReader bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));// 获得客户端对象的输出流PrintWriter pw=new PrintWriter(socket.getOutputStream(),true);//键盘录入BufferedReader burd =new BufferedReader( new InputStreamReader(System.in));String line = null;//读取键盘录入的数据while((line=burd.readLine())!=null){//数据加载到打印流pw.println(line);//获得服务端反馈String str=bfr.readLine();//打印服务端反馈System.out.println(str);//反馈如果保护”成功“那么登陆成功,跳出循环if(str.contains("成功"))break;}//关闭资源socket.close();}}
结果演示

当输入错误时:


当输入正确时;


当数次次数到达时,会被加入黑名单,并且断开连接


5、TCP客户端高并发的解决方案

上述程序演示的全是一对一的传输,但在实际开发中最常见的是客户端并发的访问服务端,下面我将对上面程序的代码做出修改

以满足并发请求的需

这是示例三的服务端多并发处理方案:

package internet;import java.io.BufferedReader;import java.io.FileReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;/* 服务端,判断客服端输入的用户名存不存在,如果有就返回恭喜你登录成功 没有的话,提示从新输入,如果输入次数大于五次就断开服务 这里需要考虑到多个客服端请求的情况  */public class TCPLoginServer {public static void main(String[] args) throws Exception {//建立黑名单的对象引用String Blacklist=null;//建立服务端对象,指定窗口ServerSocket server = new ServerSocket(8848);//获得客户端对象Socket socket = server.accept();//判断客户端的ip时候是黑名单中的数据String ip=socket.getInetAddress().getCanonicalHostName();//判断客户端的ip时候是黑名单中的数据,这里先不考虑并发和多个用户等情况,实际情况可用集合或者文本储存if(Blacklist==ip){socket.close();}//获得客户端输入流BufferedReader bfr = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获得客户端输出流PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);//建立读取存档的输入流指向BufferedReader bfr_2 =null;String line=null;String line_2=null;//建立标记boolean flag=false;//建立计数器int mark=0;//开始获得客服端传来的数据while((line=bfr.readLine())!=null){//记录运行次数mark++;//判断运行次数if(mark>=5){//反馈客服端pw.println("您尝试登录的次数已经到了");//把此客户端ip加入黑名单Blacklist=socket.getInetAddress().getCanonicalHostName();//关闭连接socket.close();//跳出循环break;}//把本地数据库加载到输入流bfr_2 = new BufferedReader(new FileReader("c:\\SQL.txt"));//读取数据while((line_2=bfr_2.readLine())!=null){//查找数据库里是否有用户输入的用户名if(line.equals(line_2)){//有的话就反馈客户端登陆成功pw.println("用户:"+line+"您好!"+"恭喜您已经成功登录了");//改掉标记flag=true;//并且跳出循环break;}}if(!flag){pw.println("没有这个用户,请重新输入");}}//关闭流bfr_2.close();}}
◆下面是并发上传图片是服务端代码示例:

package internet;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class ConcurrentUploadServer {public static void main(String[] args) throws IOException {//建立服务端对象ServerSocket ssocket = new ServerSocket(22346);//一有客服端请求就获取其客服端对象,并且为其单独开启一个线程while(true){Socket socket = ssocket.accept();new Thread(new ConcurrentHandle(socket)).start();;}}}class ConcurrentHandle implements Runnable{private Socket socket;ConcurrentHandle(Socket socket){this.socket =socket;}public void run(){ InputStream input=null; OutputStream output=null; FileOutputStream fos=null;// 建立一个计数器 int temp =1; String ip=socket.getInetAddress().getHostAddress();try{//得到客服端的输入输出流 input = socket.getInputStream(); output =socket.getOutputStream();// 把ip当作名字并且封装成对象 File file = new File("c:/",ip+".jpg");// 如果有这个文件的话,名字就为计计数器的值加上ip while(file.exists()) { file = new File("c:/",ip+"("+(temp++)+")"+".jpg"); }// 把数据加载到输出流 fos = new FileOutputStream(file);//建立缓冲区byte by[] =new byte[1024*10];int num=0;while((num=input.read(by))!=-1){//加载到输入流fos.write(by, 0,num);}//反馈客服端上传成功output.write("恭喜你上传成功".getBytes());fos.close();socket.close();}catch(Exception e){throw new RuntimeException("上传失败");}}}

四、URLURLConnection

1URL

        URI:范围更大,条形码也包含于此范围

        URL:范围较小,即域名

方法:

        1)构造函数:URL(String protocol,String host,int port,String file);//根据指定 protocolhostport号和 file 创建 URL对象。

        2String getProtocol();//获取协议名称

        3String getHost();//获取主机名

        4int getPort();//获取端口号

        5String getFile();//获取URL文件名

        6String getPath();//获取此URL的路径部分

        7String getQuery();//获取此URL的查询部,客户端传输的特定信息

注:一般输入网址,是不带端口号的,此时可进行获取,通过获取网址返回的port,若port-1,则分配一个默认的80端口,如

        int port = getPort();

        if(port == -1)

              port = 80;

2URLConnection

方法:

        1URLConnection openConnection();//URL调用此方法,返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。

        2InputStream getInputStream();//获取输入流

        3OutputStream getOutputStream();//获取输出流



感谢浏览,不足之处希望谅解




1 0
原创粉丝点击