Java之网络编程

概述


   网络编程其实是为了实现不同主机上的数据通信,而要实现通讯则需要首先知道对方的IP和端口号,同时明确双方使用的通信规则,或传输协议


Ip地址端口号传输协议,即是网络通讯的三要素


IP地址:主机在网络的地址,或者在网上的唯一标识。

127.0.0.1,本地回环地址,用它可以测试网卡是否损坏;还有些IP地址保留了,不用于广域网,只用于局域网,例如:192.168.0.1~255,192.168.1.1~255等。


端口号:数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识,为了方便称呼这个数字,就这个数组叫为“端口”,或“逻辑端口”,它并没有物理实体与之对应。有效端口好从0~65535,其中0~1024是系统保留的。一些常用程序的默认端口:浏览器80端口,TomCat服务器8080端口,MySQL3306端口。


通信规则:即传输协议,国际组织定义的通用协议是TCP/IPTCP/IP既能用于局域网,也能用于广域网。除了TCP/IP,还有其他的协议,例如UDPFTPHTTP等。



网络模型

主要由两个OSITCP/IP模型,其分层情况如下:


TCPUDP在传输层。IP协议在网络层。FTP协议、HTTP协议在应用层。


数据封包:数据从应用层开始,每经过一层都加入该层的标识信息,直到物理层,这个过程就叫数据封包。之后变成二进制01数据从物理设备上传输。


数据拆包:与封包过程正好相反,指去掉每层的标识信息,获取数据的过程。


网络编程:现阶段主要是在网际层传输层。而javaWeb开发主要是在应用层


Java中,对各个层都创建了有对象与之对应,以方便我们的开发使用。下边主要学习传输层的一些对象,比如InetAddressSocketDatagramSocket


IP地址


    IP地址是数字,使用时不容易记忆,所以有与这个地址名想对应的主机名。主机名IP地址相对应。

    体系结构:

|----InetAddress

|----Inet4Address,

        |----Inet6Address

InetAddress中没有提供构造函数,但它提供了多种静态方法,以获取本类对象。



Socket地址


装的是带端口号的IP地址

|——SocketAddress

        |———InetSocketAddress


程序示例:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.net.*;    
  2. class  IPDemo    
  3. {    
  4.     public static void main(String[] args) throws Exception    
  5.     {    
  6. /*  
  7.  *      //下边这句话会报未知主机异常  
  8.  *      //获取本地主机IP地址  
  9.         InetAddress i= InetAddress.getLocalHost();  
  10.           
  11.         System.out.println(i.toString());  
  12.         String adr = i.getHostAddress();  
  13.         String name = i.getHostName();  
  14.         sop("adr="+adr+" name="+name);  
  15. */          
  16.         //获取其他主机IP地址    
  17.         //通过主机名获取IP地址,需要解析,所以较慢,而且对于百度、新浪这样的主机名,获得的IP地址可能不唯一。    
  18.         InetAddress i= InetAddress.getByName("www.sina.com");    
  19.             
  20.         System.out.println(i.toString());    
  21.         String adr = i.getHostAddress();//获得IP地址的字符串表现形式    
  22.         String name = i.getHostName();//获取主机名    
  23.         sop("adr="+adr+" name="+name);    
  24.     
  25.     
  26.     }    
  27.     public static void sop(Object obj)    
  28.     {    
  29.         System.out.println(obj);    
  30.     }    
  31. }   

TCPUDP


TCPUDP都是在网络传输层


TCPUDP的区别【重要】


UDP是面向无连接的,它将数据集源和目的封装成数据包中,不需要建立连接;它的每个数据报(Datagram)的大小限制在64k以内;因为无连接,是不可靠的协议;同样因为不需要建立里连接,所以速度快。

应用特点:只求速度快,数据丢失对程序影响不大。


应用程序实例:聊天室工具、视频、网络会议等。


TCP:需要建立连接,形成传输数据的通道;在连接中进行大量数据传输;通过三次握手协议完成连接,是可靠的协议;因为必须建立连接,所以效率会稍低。(TCP不像UDP需要封包,而且每个包不能超过64k,它可以大量的传输数据)。


应用特点:适合对数据完整性要求高的程序。

应用程序实例:下载程序。


Socket


网络编程其实就Socket编程,Socket是为网络服务的一种机制。(Socket,中文意思为插座)

每个应用程序都有一个socket信端点,可以把Socket想象为每个应用程序上的码头。

通信的两端都有Socket,网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输。

 

 

UDP传输


UDP两端是发送端接受端

  • DatagramSocketUDP传输专用的程序通信端点,既能发送也能接受。
  • DatagramPacket:数据报包,封装了UDP传输的数据报。里边可以设置要发送的目的地IP地址、端口及要发送的数据。

广播地址:每个网段尾数为255IP地址,例如192.168.1.255


代码示例:

1、发送端

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2. 需求:通过Udp传输方式,将一段文字数据发送出去。 
  3. 定义一个udp发送端。 
  4. 思路: 
  5. 1。建立UDPsocket服务。 
  6. 2.提供数据,并将数据封装到数据包中。 
  7. 3.通过socket服务的发送功能,将数据包发出去。 
  8. 4.关闭资源。 
  9. */  
  10. import java.net.*;  
  11. class UdpSend   
  12. {  
  13.     public static void main(String[] args) throws Exception  
  14.     {  
  15.         //1.创建UDP服务,通过DatagramSocket对象。  
  16.         DatagramSocket ds = new DatagramSocket(8888);  
  17.         //2.确定数据,并封装成数据包。DatagramPacket(byte[] buf, int length, InetAddress address, int port)   
  18.         byte[] buf = "这是通过UDP发送的数据!".getBytes();  
  19.         DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10000);  
  20.         //3.通过socket服务,将已有的数据包发送出去,通过send方法。      
  21.         ds.send(dp);  
  22.         //4.关闭资源。它使用了底层的资源,所以要关闭资源。  
  23.         ds.close();  
  24.   
  25.     }  
  26. }  
2、接收端

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2. 需求; 
  3. 定义一个应用程序,用于接收并处理数据。 
  4.  
  5. 定义udp接收端 
  6. 思路: 
  7. 1.定义socket服务,通常会监听一个端口,其实就是给这个接受网络应用程序定义一个数字标示。 
  8.     方便与明确哪些数据过来该应用程序可以处理。 
  9. 2.定义一个数据包,因为要存储要接受到的字节数据, 
  10. 因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。 
  11. 3.通过socket服务的receive方法将收到的数据存入已定义的数据包中。 
  12. 4.通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。 
  13. 5.关闭资源。 
  14. */  
  15. import java.net.*;  
  16. class UdpRece  
  17. {  
  18.     public static void main(String[] args) throws Exception  
  19.     {  
  20.         //1.创建udpsocket,建立端点。这里必须定义监听的端口,其实就是给网络应用程序定义数字标识。  
  21.         DatagramSocket ds = new DatagramSocket(10000);  
  22.         //2.定义数据包,用于存储数据。  
  23.         byte [] buf = new byte[1024];         
  24.         DatagramPacket dp = new DatagramPacket(buf,buf.length);  
  25.         //3.通过服务的receive方法,将收到的数据存入到数据包中。     
  26.         ds.receive(dp);//receive是一个阻塞式方法没有接受到数据,会一直等待  
  27.         //4.获取数据包中的方法获取其中的数据。  
  28.         InetAddress  i= dp.getAddress();  
  29.         String ip = i.getHostAddress();  
  30.         String  data = new String(dp.getData(),0,dp.getLength());  
  31.         int port = dp.getPort();  
  32.         System.out.println(ip+"::"+data+"::"+port);  
  33.         //5、关闭资源  
  34.         ds.close();  
  35.     }  
  36. }  

改进:让接收端始终处于接收状态,发送端可以发送多条数据并以“over”结束

发送端:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package tan;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class UDPSend2 {  
  5.     public static void main(String[] args) throws Exception{  
  6.         DatagramSocket socket=new DatagramSocket();//可以不填写端口保持默认  
  7.         //读取键盘录入···  
  8.         BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
  9.       
  10.         DatagramPacket dp=null;  
  11.         String line=null;  
  12.         while((line=br.readLine())!=null){  
  13.             //聊天中输入over结束聊天  
  14.             if("over".equals(line)){  
  15.                 break;  
  16.             }  
  17.             byte[]buf=("sender:"+line).getBytes();  
  18.              //192.168.1.255是这个网段的广播地址,用它就能实现局域网群聊。    
  19.             InetAddress ip=InetAddress.getByName("127.0.0.1");  
  20.             //打包数据  
  21.             dp=new DatagramPacket(buf, buf.length,ip,10001);  
  22.             //发送数据  
  23.             socket.send(dp);  
  24.         }  
  25.           
  26.         socket.close();  
  27.           
  28.           
  29.           
  30.     }  
  31. }  

接收端:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package tan;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class UDPRec2 {  
  5.     public static void main(String[] args) throws Exception {  
  6.         DatagramSocket ds=new DatagramSocket(10001);  
  7.         byte []buf=new byte[64];  
  8.         DatagramPacket dp=new DatagramPacket(buf, buf.length);  
  9.         //无限循环,始终处于接收状态  
  10.         while(true){  
  11.             ds.receive(dp);  
  12.             String ip=dp.getAddress().getHostAddress();  
  13.             int port=dp.getPort();  
  14.             String data=new String(dp.getData(),0,dp.getLength());  
  15.             System.out.println("IP"+ip+":"+port+":"+data);  
  16.         }  
  17.     }  
  18. }  

UDP练习:聊天程序

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package tan;  
  2. import java.io.*;  
  3. import java.net.*;  
  4.   
  5. class Send implements Runnable{  
  6.     private DatagramSocket ds;  
  7.     public Send(DatagramSocket ds) {  
  8.         this.ds=ds;  
  9.     }  
  10.     @Override  
  11.     public void run() {  
  12.         try {  
  13.             ds=new DatagramSocket();  
  14.             BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));  
  15.             String line=null;  
  16.             while((line=bufr.readLine())!=null){  
  17.                 if("over".equals(line)){  
  18.                     break;  
  19.                 }  
  20.                 byte []buf=line.getBytes();  
  21.                 DatagramPacket dp=new DatagramPacket(buf, 0,buf.length,InetAddress.getByName("127.0.0.1"),10002);  
  22.                 ds.send(dp);  
  23.                   
  24.             }  
  25.             ds.close();  
  26.         } catch (Exception e) {  
  27.             throw new RuntimeException("发送端发送失败!");  
  28.         }  
  29.           
  30.     }  
  31. }  
  32.   
  33.   
  34. class Receive implements Runnable{  
  35.     private DatagramSocket ds;  
  36.     public Receive(DatagramSocket ds) {  
  37.         this.ds=ds;  
  38.     }  
  39.     @Override  
  40.     public void run() {  
  41.         try {  
  42.             while(true){  
  43.                 byte []buf=new byte[1024];  
  44.                 DatagramPacket dp=new DatagramPacket(buf, buf.length);  
  45.                 ds.receive(dp);  
  46.                 String ip=dp.getAddress().getHostAddress();  
  47.                 String data=new String(dp.getData(),0,dp.getLength());  
  48.                   
  49.                 System.out.println("ip:"+ip+" data:"+data);  
  50.                   
  51.                   
  52.             }  
  53.         } catch (Exception e) {  
  54.             throw new RuntimeException("接收端失败!");  
  55.         }  
  56.           
  57.     }  
  58. }  
  59.   
  60.   
  61. public class chatDemo {  
  62.     public static void main(String[] args) throws Exception {  
  63.         DatagramSocket sendSocket=new DatagramSocket();  
  64.         new Thread(new Send(sendSocket)).start();  
  65.           
  66.         DatagramSocket receSocket=new DatagramSocket(10002);  
  67.         new Thread(new Receive(receSocket)).start();  
  68.     }  
  69. }  

TCP传输


TCP的两端对应的是客户端服务端

SocketTCP传输客户端通信端点。

SeverSocketTCP传输服务端通信端点。

 

因为TCP需要建立连接,所有Socket客户端一建立就要指定服务端的IP和端口。而在服务端建立时,要设置监听的端口。


TCP连接成功后,在客户端和服务端就会产生网络流

客户端Socket提供对网络流进行读写的输入流和输出流对象。

服务端操作网络流时,先获取客户端Socket对象,然后利用该Socket的字节输入输出流进行读写操作。

客户端与服务端进行多次交互时,注意阻塞式方法对程序的影响。


ServerSocket(int port, int backlog),这个构造函数中backlog用于指定客户端的最大连接数。


TCP传输中的两个问题


一、 客户端与服务端交互时同时阻塞


     在示例代码3中,当客户端和服务端中都都有多个阻塞式方法时,如果使用缓冲区读取每行,没有刷新并加入换行符时,会导致客户端与服务端同时处于阻塞等待状态。


二、 定义文件结束标记


在示例代码4中,当上传文件时,客户端把本地文件变成了IO流,并写入网络输出流时,因为去掉了去掉了window加入的结束标记,使得该IO流没有结束标记,导致服务端读取流中数据时,无法结束。这就需要自定义标记。
自定义标记有三种方法:

1.定义”over”这样的字符结束标记;

2.盖时间戳。

3.使用SocketshutdownOutput( )shutdownInput()方法。


示例代码1------给服务器发送一个文本


建立TCP客户端

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /*   
  2. 客户端:   
  3. 通过查阅Socket对象,发现在该对象建立时,就可以连接指定的主机。   
  4. 因为TCP是面向连接的,所以在建立Socket服务时,就要有服务端存在,   
  5. 并连接成功,形成通路后,在该通道进行数据的传输。   
  6.    
  7. 需求:给服务段发送一个文本数据。   
  8. 步骤:   
  9. 1.创建Socket服务,并指定要连接的主机和端口。   
  10.    
  11. */    
  12. package TCP;  
  13. import java.io.*;  
  14. import java.net.*;  
  15. public class TCPClient {  
  16.     public static void main(String[] args) throws Exception, IOException {  
  17.         //1.创建客户端的Socket服务,指定目的主机和端口    
  18.         Socket s=new Socket("127.0.0.1"10003);  
  19.         //2.为了发送数据,应该获取Socket流中的输出流。    
  20.         OutputStream out=s.getOutputStream();  
  21.         //3.获取输出流后写入数据  
  22.         out.write("我是客户端~~".getBytes());  
  23.          //socket关闭客户端Socket服务,也就关闭了网络流资源。    
  24.         s.close();  
  25.           
  26.           
  27.     }  
  28. }  
建立服务端:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /*   
  2. 服务端:   
  3. 需求:定义端点,接受数据,并打印在控制台上。   
  4.    
  5. 服务端:   
  6. 1.建立服务端的Socket服务,通过SeverSocket();并监听一个端口。   
  7. 2. 获取连接过了的客户端对象。   
  8.     通过SeverSocket的accept方法。没有就会等,所以这个方法是阻塞式的。   
  9. 3.客户端如果发过了数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流读取发过了的数据。并打印在控制台上。   
  10. 4.关闭服务。(可选操作,服务端一般是一直开着的)。   
  11. */    
  12. package TCP;  
  13. import java.io.*;  
  14. import java.net.*;  
  15.   
  16. public class TCPServer {  
  17.     public static void main(String[] args) throws Exception{  
  18.     //1.建立服务端的Socket服务,通过SeverSocket();并监听一个端口。  
  19.         ServerSocket ss=new ServerSocket(10003);  
  20.     //2. 获取连接过了的客户端对象。 通过SeverSocket的accept方法。没有就会等,所以这个方法是阻塞式的。  
  21.         Socket s=ss.accept();  
  22.     //3.获取客户端发过来的数据,那么要使用客户端对象的读取流方法读取对象。这个流是网络流    
  23.         InputStream is=s.getInputStream();  
  24.         byte[]buf=new byte[1024];  
  25.         int len=is.read(buf);  
  26.         System.out.println(new String(buf, 0, buf.length));  
  27.       
  28.     //4.关闭客户端,服务端(可选)  
  29.         s.close();  
  30.           
  31.         ss.close();//关闭服务端(可选)  
  32.           
  33.           
  34.     }  
  35. }  
  36.   
  37. //服务端接收到客户端的信息后并没有反馈信息  


示例代码2-------完成客户端与服务端的一次交互

演示tcp的传输的的客户端和服务端的互访。

需求:客户端给服务端发送数据,服务端收到数据后,给客户端反馈信息。


建立客户端程序:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /*   
  2. 客户端:   
  3. 1.建立socket服务,指定要连接的主机和端口   
  4. 1.获取Socket流中的输出流,将数据写入到该流中,通过网络发送给服务端。   
  5. 3.获取socket流中的输入流,将服务端反馈的数据获取到,并打印,   
  6. 4.关闭客户端资源。   
  7.    
  8. */    
  9.     
  10. package TCP;  
  11. import java.io.*;  
  12. import java.net.*;  
  13. public class TCPClient2 {  
  14.     public static void main(String[] args) throws Exception {  
  15.         Socket s=new Socket("127.0.0.1"10004);  
  16.         //利用输出流向服务器发出数据  
  17.         OutputStream out=s.getOutputStream();  
  18.         out.write("服务端你好!我是客户端".getBytes());  
  19.         //利用输入流接收服务器返回的数据  
  20.         InputStream in=s.getInputStream();  
  21.         byte[]buf=new byte[1024];  
  22.         int len=in.read(buf);//阻塞式方法,等待服务端返回信息  
  23.         System.out.println(new String(buf,0,len));  
  24.         s.close();  
  25.           
  26.     }  
  27. }  
建立服务端程序:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package TCP;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class TCPServer2 {  
  5.     public static void main(String[] args) throws Exception {  
  6.         ServerSocket ss=new ServerSocket(10004);  
  7.         Socket s=ss.accept();  
  8.         //获取客户端ip  
  9.         String ip=s.getInetAddress().getHostAddress();  
  10.         System.out.println(ip+"......is connected!");  
  11.         //利用输入流接收客户端发送的信息  
  12.         InputStream in=s.getInputStream();  
  13.         byte []buf=new byte[1024];  
  14.         int len=in.read(buf);  
  15.         System.out.println(new String(buf, 0, len));  
  16.         //利用输出流向客户端发送反馈信息  
  17.         OutputStream out=s.getOutputStream();  
  18.         out.write("你好客户端!我是服务端,已经接收到了你发送的数据".getBytes());  
  19.           
  20.         s.close();//关闭socket连接    
  21.     }  
  22. }  

示例代码3------键盘录入数据通过服务端转成大写形式。

需求:建立一个文本转换服务器。 
客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端。 
而且,客户端可以不断的进行文本转换。当客户端输入over,转换就结束。 
 
分析: 
客户端: 
既然是操作设备上的数据,那么就可以使用io技术,并按照IO的操作规律来思考。 
源:键盘录入 
目的:网络设备,也就是网络输出流, 
而且操作的是文本数据,可以选择字符流。 
 
步骤: 
1,建立服务 
2.获取键盘录入 
3,将数据发给服务端。 
4.获取服务端返回的大写数据。 
5.结束,关闭资源。 
 
都是文本数据,可以使用字符流进行操作。同时提高效率,要加入缓冲。


客户端代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package TCP;  
  2. import java.io.*;  
  3. import java.net.*;  
  4.   
  5. public class TransClient {  
  6.     public static void main(String[] args) throws Exception {  
  7.         //1.建立socket服务  
  8.         Socket s=new Socket("127.0.0.1"10005);  
  9.         //2.读取键盘数据(获取键盘录入)  
  10.         BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));  
  11.         //3.定义目的,将数据写入socket输出流,发给服务器。  
  12.         //BufferedWriter bufout=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));  
  13.         //代码简化:如下···  
  14.         PrintWriter out=new PrintWriter(s.getOutputStream(),true);  
  15.         //4.定义一个socket读取流,读取服务端返回的信息  
  16.         BufferedReader bufin=new BufferedReader(new InputStreamReader(s.getInputStream()));  
  17.         String line=null;  
  18.         while((line=bufr.readLine())!=null){//阻塞位置1,等待键盘录入,没有数据则等待    
  19.             if("over".equals(line)){  
  20.                 break;  
  21.             }  
  22. //          bufout.write(line);//写到缓冲区中去了  
  23. //          bufout.newLine();//换行,没有这一句,则阻塞2的readline不会返回字符,  
  24. //          bufout.flush();//没有这一句,则数据留在缓冲区中,阻塞2同样阻塞。  
  25.               
  26.             out.println(line);//打印流的println()方法可以实现自动刷新  
  27.               
  28.             String str=bufin.readLine();//阻塞位置3  
  29.             System.out.println("server:"+str);  
  30.               
  31.         }  
  32.         //5.关闭资源  
  33.         bufr.close();  
  34.         s.close();//给socket在加入结束标记(-1)  
  35.           
  36.     }  
  37. }  

服务端代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /*  
  2. 服务端:  
  3. 源.socket读取流。  
  4. 目的:socket输出流  
  5. 都是文本,装饰  
  6. */   
  7. package TCP;  
  8. import java.io.*;  
  9. import java.net.*;  
  10. public class TransServer {  
  11.     public static void main(String[] args) throws Exception {  
  12.         ServerSocket ss=new ServerSocket(10005);  
  13.         Socket s=ss.accept();  
  14.         String ip=s.getInetAddress().getHostAddress();  
  15.         System.out.println(ip+"....已经连接!");  
  16.           
  17.         BufferedReader bufin=new BufferedReader(new InputStreamReader(s.getInputStream()));  
  18.           
  19. //      BufferedWriter bufout=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));  
  20.         //简化书写:  
  21.         PrintWriter out=new PrintWriter(s.getOutputStream(), true);  
  22.       
  23.         String line=null;//定义字符串来接收输出的数据  
  24.           
  25. //      阻塞位置2,没有数据+换行符(win中是“\r\n”)则等待,尤其注意换行符    
  26.         while((line=bufin.readLine())!=null){  
  27. //          bufout.write(line.toUpperCase());  
  28. //          bufout.newLine();  
  29. //          bufout.flush();  
  30.             System.out.println(line);  
  31.             out.println(line.toUpperCase());//小写转大写  
  32.               
  33.         }  
  34.           
  35.         s.close();  
  36.         ss.close();   
  37.     }  
  38. }  
  39. /*  
  40. 该例子出现的问题:  
  41. 现象:客户端和服务端都在莫名的等待。  
  42. 为什么呢?  
  43. 因为客户端和服务端都有阻塞式的方法,这些方法没有读到结束标记,那么就一直等。  
  44. 而导致两端,都在等待。  
  45. */    



示例代码4------TCP上传文件(使用IO字符流)


把文件从客户端传到服务器端上。

容易出现的问题:没有客户端上传完后,文件结束标记,服务器无法停止连接Socket连接。


客户端代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package TCP;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class TextClient {  
  5.     public static void main(String[] args) throws Exception {  
  6.         Socket s=new Socket("127.0.0.1"10006);  
  7.           
  8.         //源:封装成文件对象  
  9.         BufferedReader bufr=new BufferedReader(new FileReader("TCPClient.java"));  
  10.           
  11.         //目的:通过打印流将文件写入到socket输出流中以便于服务端接收  
  12.         //Attention:在上传文件前,最好先把文件名发给服务端   
  13.         PrintWriter out=new PrintWriter(s.getOutputStream(), true);  
  14.         String line=null;  
  15.         while((line=bufr.readLine())!=null){  
  16.             out.println(line);  
  17.         }  
  18.           
  19.         s.shutdownOutput();//关闭客户端的输出流,相当于给流中加入一个结束标记 -1;   
  20.           
  21.         //读取来自服务端的socket输入流  
  22.         BufferedReader bufin=new BufferedReader(new InputStreamReader(s.getInputStream()));  
  23.         String str=bufin.readLine();  
  24.         System.out.println("server:"+str);  
  25.           
  26.         //勿忘记关闭资源  
  27.         bufr.close();  
  28.         s.close();  
  29.           
  30.     }  
  31. }  

服务端代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package TCP;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class TextServer {  
  5.     public static void main(String[] args) throws Exception {  
  6.         ServerSocket ss=new ServerSocket(10006);  
  7.         Socket s=ss.accept();  
  8.           
  9.         String ip=s.getInetAddress().getHostAddress();  
  10.         System.out.println(ip+".....is connected !");  
  11.           
  12.         //源:来自客户端发送的socket输入流  
  13.         BufferedReader bufin=new BufferedReader(new InputStreamReader(s.getInputStream()));  
  14.           
  15.         //目的:将文字写入到server.txt中  
  16.         PrintWriter out=new PrintWriter(new FileWriter("server.txt"),true);  
  17.         String line=null;  
  18.         while((line=bufin.readLine())!=null){  
  19.             out.println(line);  
  20.         }  
  21.           
  22.           
  23.         //向客户端发送消息  
  24.         PrintWriter pw=new PrintWriter(s.getOutputStream(),true);  
  25.         pw.println("文件上传成功!");  
  26.           
  27.           
  28.         //别忘了关闭资源  
  29.         out.close();  
  30.         s.close();  
  31.         ss.close();  
  32.     }  
  33. }  


示例代码5------TCP上传图片使用IO字符流)

客户端代码:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /*客户端:  
  2. 1.建立服务端点 
  3. 2.读取客户端已有的图片数据  
  4. 3.通过socket的输出流将数据发给服务端。  
  5. 4.读取服务端反馈信息。  
  6. 5.关闭。*/  
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package TCP;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class PicClient {  
  5.     public static void main(String[] args) throws Exception{  
  6.         //建立socket服务  
  7.         Socket s=new Socket("127.0.0.1"10007);  
  8.           
  9.         //读取数据-->字节流  
  10.         FileInputStream fis=new FileInputStream("1.jpg");  
  11.         //输出数据-->获取socket输出流向服务端写数据  
  12.         OutputStream out=s.getOutputStream();  
  13.         byte []buf=new byte[1024];//定义缓冲区  
  14.         int len=0//字节长度  
  15.         while((len=fis.read(buf))!=-1){  
  16.             out.write(buf, 0, len);  
  17.         }  
  18.         s.shutdownOutput(); //告诉服务端已写完数据   
  19.   
  20.           
  21.         //接受服务器发送的反馈信息  
  22.         InputStream in=s.getInputStream();  
  23.         byte[]bufin=new byte[1024];  
  24.         int inLen=in.read(buf);  
  25.         System.out.println("From Server:"+new String(buf, 0, inLen));  
  26.           
  27.           
  28.         //关闭资源  
  29.         fis.close();  
  30.         s.close();  
  31.   
  32.     }  
  33. }  

服务端:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package TCP;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class PicServer {  
  5.     public static void main(String[] args) throws Exception{  
  6.         ServerSocket ss=new ServerSocket(10007);  
  7.         Socket s=ss.accept();  
  8.           
  9.         String ip=s.getInetAddress().getHostAddress();  
  10.         System.out.println(ip+"...... is connected!");  
  11.           
  12.         InputStream in=s.getInputStream();  
  13.         FileOutputStream fos=new FileOutputStream("server.jpg");  
  14.         byte[]buf=new byte[1024];  
  15.         int len=0;  
  16.         while((len=in.read())!=-1){  
  17.             fos.write(buf, 0, len);  
  18.         }  
  19.           
  20.         //向客户端发送消息(socket输出流)  
  21.         OutputStream out=s.getOutputStream();  
  22.         out.write("图片上传成功!".getBytes());  
  23.           
  24.         //关闭资源  
  25.         fos.close();  
  26.         s.close();  
  27.         ss.close();  
  28.           
  29.           
  30.           
  31.           
  32.     }  
  33. }  


TCP客户端并发访问

     客户端并发访问服务器时,把服务端的处理代码封装在Runnable实现子类的run方法中,并把服务器获的的Socket对象传给该实现子类的构造函数。服务端通过while循环启动多个线程,对多个客户端请求进行并发处理。这也是一般服务器的基本原理。


示例代码6:上传图片(多客户端并发访问)

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /*客户端:  
  2. 1.建立服务端点 
  3. 2.读取客户端已有的图片数据  
  4. 3.通过socket的输出流将数据发给服务端。  
  5. 4.读取服务端反馈信息。  
  6. 5.关闭。*/  
  7. package TCP;  
  8. import java.io.*;  
  9. import java.net.*;  
  10. public class PicClient {  
  11.     public static void main(String[] args) throws Exception{  
  12.     //过滤连接(演示要用DOS窗口)  
  13.         //限定java命令传入的参数只有一个(主函数录入)  
  14.         if(args.length!=1){  
  15.             System.out.println("请选择一个jpg格式的图片");  
  16.             return;  
  17.         }  
  18.         //判断是否是文件而不是目录  
  19.         File file=new File(args[0]);  
  20.         if(!(file.exists()&& file.isFile())){  
  21.             System.out.println("该文件有问题,不存在或者不是文件");  
  22.             return;  
  23.         }  
  24.         //判断这个文件的 后缀名是不是JPG  
  25.         if(!file.getName().endsWith(".jpg")){  
  26.             System.out.println("图片格式错误");  
  27.             return;  
  28.         }  
  29.         //限定图片大小为5MB以内  
  30.         if(file.length()>1024*1024*5){  
  31.             System.out.println("图片过大,应在5M以内");  
  32.             return ;  
  33.         }  
  34.           
  35.           
  36.           
  37.     //建立socket服务  
  38.           
  39.         Socket s=new Socket("127.0.0.1"10007);  
  40.           
  41.         //读取数据-->字节流  
  42.         FileInputStream fis=new FileInputStream("1.jpg");  
  43.         //输出数据-->获取socket输出流向服务端写数据  
  44.         OutputStream out=s.getOutputStream();  
  45.         byte []buf=new byte[1024];//定义缓冲区  
  46.         int len=0//字节长度  
  47.         //记得要将buf数组传进去,你咋老是记不住呢???  
  48.         while((len=fis.read(buf))!=-1){  
  49.             out.write(buf, 0, len);  
  50.         }  
  51.         s.shutdownOutput(); //告诉服务端已写完数据   
  52.   
  53.           
  54.         //接受服务器发送的反馈信息  
  55.         InputStream in=s.getInputStream();  
  56.         byte[]bufin=new byte[1024];  
  57.         int inLen=in.read(buf);  
  58.         System.out.println("From Server:"+new String(buf, 0, inLen));  
  59.           
  60.           
  61.         //关闭资源  
  62.         fis.close();  
  63.         s.close();  
  64.   
  65.     }  
  66. }  


服务端代码演示:

  1. 示例5服务端有个局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程。 
  2. 这时B客户端连接,只有等待。因为服务端还没有处理完A客户端的请求。还没有循环回来 
  3. 执行下次accept方法。所以,暂时获取不到B客户端对象。那么为了可以让多个客户端 
  4. 同时并发访问服务端。那么服务端最好就是将每个客户端封装到一个单独的线程中去。这 
  5. 样就可以同时处理多个客户端请求。如何定义线程?只要明确了每一个客户端要在服务端 
  6. 执行的代码即可。将该代码存入run方法中。 

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package TCP;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class PicServer {  
  5.     public static void main(String[] args) throws Exception{  
  6.         ServerSocket ss=new ServerSocket(10007);  
  7.           
  8.         while(true){  
  9.             Socket s=ss.accept();  
  10.             new Thread(new PicThread(s)).start();  
  11.         }  
  12.           
  13.     }  
  14. }  
  15. class PicThread implements Runnable{  
  16.     private Socket s;  
  17.       
  18.     public PicThread(Socket s) {  
  19.         this.s=s;  
  20.     }  
  21.       
  22.     @Override  
  23.     public void run() {  
  24.         String ip=s.getInetAddress().getHostAddress();  
  25.         int count=0;  
  26.         try {  
  27.         System.out.println(ip+"......is connnected!");  
  28.         InputStream in = s.getInputStream();  
  29.           
  30.           
  31.           
  32.         File file=new File(ip+"("+(count)+")"+".jpg");  
  33.         while(file.exists()){  
  34.             file=new File(ip+"("+(count++)+")"+".jpg");  
  35.         }  
  36.           
  37.         FileOutputStream fos=new FileOutputStream(file);  
  38.         byte[]buf=new byte[1024];  
  39.         int len=0;  
  40.         //又在这犯错误了!要记得将buf传进去,不然输出的肯定是空  
  41.         while((len=in.read(buf))!=-1){  
  42.             fos.write(buf, 0, len);  
  43.         }  
  44.           
  45.         //向客户端发送消息(socket输出流)  
  46.         OutputStream out=s.getOutputStream();  
  47.         out.write("图片上传成功!".getBytes());  
  48.           
  49.         //关闭资源  
  50.         fos.close();  
  51.         s.close();  
  52.           
  53.         } catch (Exception e) {  
  54.             throw new RuntimeException(ip+"上传失败!");  
  55.         }  
  56.           
  57.           
  58.     }  
  59. }  


示例代码7:------客户端并发登陆

客户端通过键盘录入用户,服务端对这个用户名进行校验。如果该用户存在,在服务端显示xxx,已登陆。并在客户端显示 xxx,欢迎光临。 如果该用户不存在,在服务端显示xxx,尝试登陆。并在客户端显示 xxx,该用户不存在。 最多登录三次。 登陆成功、登录三次都直接断开Socket

客户端代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package tan;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. class LoginClient {  
  5.     public static void main(String[] args) throws Exception {  
  6.         Socket s = new Socket("127.0.0.1"10008);  
  7.         //读取键盘录入  
  8.         BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));  
  9.         //客户端发送所读取的数据  
  10.         PrintWriter out = new PrintWriter(s.getOutputStream(), true);  
  11.         //读服务端发送的socket流(反馈信息)  
  12.         BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));  
  13.         //只能发送三次  
  14.         for (int x = 0; x < 3; x++) {  
  15.             //先读取键盘录入的数据  
  16.             String line = bufr.readLine();  
  17.             //读取的有可能为空,要判断发送的信息是否为空  
  18.             if (line == null)  
  19.                 break;  
  20.             //发送这一行:line  
  21.             out.println(line);  
  22.             //读服务端发送的反馈信息(一行)  
  23.             String info = bufIn.readLine();  
  24.             System.out.println("info:" + info);  
  25.             if (info.contains("欢迎")){  
  26.                 break;    
  27.             }  
  28.         }  
  29.         //关闭资源  
  30.         bufr.close();  
  31.         s.close();  
  32.     }  
  33. }  
服务端代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package tan;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. public class LoginServer {  
  5.     public static void main(String[] args) throws Exception {  
  6.         ServerSocket ss=new ServerSocket(10008);  
  7.         while(true){  
  8.             Socket s=ss.accept();  
  9.             new Thread(new UserThread(s)).start();  
  10.         }  
  11.     }  
  12. }  
  13.   
  14.   
  15. //定义用户线程类  
  16. class UserThread implements Runnable {  
  17.     private Socket s;  
  18.     UserThread(Socket s) {  
  19.         this.s = s;  
  20.     }  
  21.     public void run() {  
  22.         String ip = s.getInetAddress().getHostAddress();  
  23.         System.out.println(ip + "....connected");  
  24.          
  25.         try {  
  26.              //校验3次  
  27.             for (int x = 0; x < 3; x++) {  
  28.                 //获取客户端发送的socket流-->利用到转换流  
  29.                 BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));  
  30.                 //用户名  一个  
  31.                 String name = bufIn.readLine();  
  32.                 if (name == null){  
  33.                     break;  
  34.                 }  
  35.                 //服务端读取user.txt文件  
  36.                 BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));  
  37.                 //想客户端发送信息  
  38.                 PrintWriter out = new PrintWriter(s.getOutputStream(), true);  
  39.                       
  40.                 String line = null;  
  41.                 boolean flag = false;//定义标记 记录判断的结果 ,默认false  
  42.                 //一行行的读 可以读到结尾(操作的是文件)  
  43.                 while ((line = bufr.readLine()) != null) {  
  44.                     //怎么判断呢?判断有了怎样?没有又怎样?要根据循环判断的结果来判断  
  45.                     if (line.equals(name)) {  
  46.                         flag = true;//如果找到了数据则直接跳出  
  47.                         break;  
  48.                     }  
  49.                 }  
  50.                 //根据循环后的结果来判断  
  51.                 if (flag) {  
  52.                     //结果为真,则服务端输出name  
  53.                     System.out.println(name + ",已登录");  
  54.                     //服务端写出去告诉客户端  
  55.                     out.println(name + ",欢迎光临");  
  56.                     //已经校验成功,则直接break  
  57.                     break;  
  58.                 } else {  
  59.                     System.out.println(name + ",尝试登录");  
  60.                     out.println(name + ",用户名不存在");  
  61.                 }  
  62.             }  
  63.             s.close();  
  64.         } catch (Exception e) {  
  65.              
  66.             throw new RuntimeException(ip + "校验失败");  
  67.         }  
  68.     }  
  69. }  

URL

URL-:代表一个统一资源定位符,它是指向互联网资源的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。

URI 是统一资源标识符,而 URL是统一资源定位符。因此,笼统地说,每个 URL都是 URI,但不一定每个 URI都是 URL。这是因为 URI还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。


常用方法示例

intgetDefaultPort() :获取与此 URL关联协议的默认端口号。

 String getFile() :获取此 URL的文件名。

 String getHost() :获取此 URL的主机名(如果适用)。

 String getPath() :获取此 URL的路径部分。

 int getPort() :获取此 URL的端口号。

 String getProtocol():获取此 URL的协议名称。

 String getQuery() :获取此 URL的查询部分。


[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.net.*;    
  2. class  URLDemo    
  3. {    
  4.     public static void main(String[] args) throws Exception    
  5.     {    
  6.         //URL url=new URL("http://192.168.1.13:11000/myweb/demo.html");    
  7.         URL url=new URL("http://192.168.1.13:11000/myweb/demo.html?name=haha&age=30");    
  8.         System.out.println("getProtocol():"+url.getProtocol()); //http              
  9.         System.out.println("getHost():"+url.getHost());//192.168.1.13    
  10.         System.out.println("getDefaultPort():"+url.getDefaultPort());//80,如果关联的协议没有默认的端口,则值为-1;    
  11.         System.out.println("getPort():"+url.getPort()); //  11000,如果没有设置则为-1;    
  12.         System.out.println("getPath():"+url.getPath());// /myweb/demo.html    
  13.         System.out.println("getFile():"+url.getFile());///myweb/demo.html?name=haha&age=30    
  14.         System.out.println("getQuery():"+url.getQuery());//name=haha&age=30    
  15.     
  16.     /*  int port = url.getPort();  
  17.         if(port==-1)  
  18.             port =80;  
  19.         getPort() = -1  
  20.     */    
  21.     }    
  22. }    

URLConnection

 

它的作用于Socket类似,其实它内部封装了Socket,所以可以获取网络输入输出流。通过URLURLConnection可以方便地对应用层网络数据进行读取和操作。

URLConnection———应用层

Socket-————传输层


方法示例:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.net.*;    
  2. import java.io.*;    
  3.     
  4. class URLConnectionDemo     
  5. {    
  6.     public static void main(String[] args) throws Exception    
  7.     {    
  8.         URL url=new URL("http://192.168.1.13:8080/myweb/demo.html");    
  9.         URLConnection conn = url.openConnection();    
  10.         System.out.println(conn);    
  11.         InputStream in = conn.getInputStream();    
  12.         byte [] buf =new byte[1024];    
  13.         int len = in.read(buf);    
  14.         System.out.println(new String(buf,0,len));    
  15.     
  16.     }    
  17. }    

改写MyIEByGUI中的showWebPage()方法


示例:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. private void showWebPage() throws Exception    
  2. {    
  3.     //使用URL和URLConnection对showWebPage改写。    
  4.     //很明显,这两个封装的应用层对象使用起来更方便简洁    
  5.     ta.setText("");         
  6.     String urlPath =tf.getText();           
  7.     URL url=new URL(urlPath);    
  8.     URLConnection conn = url.openConnection();    
  9.     //System.out.println(conn);    
  10.     InputStream in = conn.getInputStream();    
  11.     byte [] buf =new byte[1024];    
  12.     int len = in.read(buf);    
  13.     ta.setText(new String(buf,0,len));    
  14. }   

域名解析


Ip地址登陆网站是,不好记忆,人们习惯用主机名。从主机名到获得该主机名对应的Ip地址的过程,就是域名解析。域名解析一般是DNS服务器完成的。


域名解析主要分为两步:


第一步:查找本地的Ip地址和主机名映射表。

这个表,在C:\Windows\System32\drivers\etc文件夹中的hosts文件。127.0.01localhost的对应关系就在这个表中。

hosts文件进行设置可以实现一些功能,比如:破解,将软件的官网地址域名与127.0.0.1对应;过滤垃圾网站、恶意网站。


第二步:如果第一步没有找到,那么就去你设置的DNS服务器中去查找映射表,找到对应的IP地址。获得IP地址后,再与之连接。

 

小结:

进入网络编程,你会发现,它把异常、多线程、IO、GUI等等知识都串联了起来,每一个小程序都是各种技术的结合体,只用把前边的学熟学透,你才能再接触新技术时很快上手。重要的还是基础。


from: http://blog.youkuaiyun.com/u010834071/article/details/38498089

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值