Java专题 Socket编程之慕课网版本

Java网络编程是基于Socket的编程,我们可以从以下5个方面学习,Java网络编程。
1、网络基础知识
2、InetAddress
3、URL
4、基于TCP的Socket编程
5、基于UDP的Socket编程

第一章 网络基础知识
1、IP地址、协议,端口的概念
IP地址现在的标准是IPv4 
端口的作用就是区别同一台主机上不同应用程序的数据
范围是0-65535  其中0-1023为系统所保留的,最好别用
一些常见的端口为:
http:80   ftp :21 telnet:23 
2、TCP/IP协议的模型


第二章 InetAddress
1、用于标识网络上的硬件资源,也就是互联网协议(IP)地址。
2、具体有哪些方法和方法对应的作用可查API帮助文档

第三章  URL
1、统一资源定位符,格式如下:
http: // www.imooc.com?username=tom&password=123#test
http为协议,?后面表示参数,#后面表示锚点
2、具体用法可以查API,其中几个有用的如openStream等可以记一下,会用到。


第四章  基于TCP进行Socket编程
1、所要用到的API类为:
ServerSocket、Socket

2、服务端对应的步骤为:
a、创建ServerSocket对象,并绑定相应的端口号。
b、通过ServerSocket对象的accept()方法对指定的端口进行Socket连接的监听。
c、获得指定的Socket连接对象
d、通过连接对象获得输入流,对客户端的输入进行读取。
e、通过连接对象获取输出流,将自己要发送给客户端的信息通过输出流发送给客户端
f、关闭相应的资源。

3、客户端需要做的事为:
@1、创建Socket对象对指定的主机的端口进行连接
@2、通过获取输出流,将要发送给服务器的信息写入。
@3、通过获取输入流,获得服务器的反馈信息。
@4、关闭相应的资源

代码示例如下:
案例背景:
    实现一个类似与QQ的即时通讯工具,实现客户端与服务器端的即时通讯,由客户端发起对话,之后需要持续显示对方说话的内容来输入要输入的话。
    在服务器端要首先读一下InputStream,看看客户端输入了什么,然后在决定输出什么,所以在循环外有一个独立的System. out.println("Client" +is.readLine());
    然后在循环中再次读取。

public class TCPServer {

        /**
        *服务器端
        */
        public static void main(String[] args) {        
       try {
              ServerSocket server= null ;
               try {   
                  server= new ServerSocket(8000);  //创建serverSocket对象,对指定端口进行监听  
              } catch (Exception e){
                     System. out .println( "Server cannot start normally!" );
              }
              Socket socket= null ;
               try {
                     socket=server.accept(); //通过serverSocket实例获得Socket对象,完成连接,此方法为阻塞方法
              } catch (Exception e){
                     System. out .println(e);
              }
              String line;
              BufferedReader is= new BufferedReader( new InputStreamReader(socket.getInputStream()));//获取输入流,读客户端发送的数据
              PrintWriter os= new PrintWriter(socket.getOutputStream());//获取输出流,用来发送自己的数据
              BufferedReader sin= new BufferedReader( new InputStreamReader(System. in ));
              System. out .println( "Client" +is.readLine());
              line=sin.readLine();
               while (!line.equals( "exit" )){
                     os.println(line);
                     os.flush();//注意这里的顺序,因为顺序必须为读一次写一次那样,在此服务器端,先读客户的,上面的is.readLine()完了之后进行输入,然后将输入结果回馈给用户,这样用户得消息后会在输入,这样我们在下面的is.readLine()的内容就是下一次用户的输入了,一定要注意这个顺序。如果只输入一次,然后读两次,第二次就会一直读不到,程序阻塞
                     System. out .println( "Server:" +line);
                     System. out .println( "Client" +is.readLine());
                     line=sin.readLine();
              }
              os.close();
              is.close();
              socket.close();
              server.close();
              
       } catch (Exception e){
              e.printStackTrace();
       }
}
}
  
public class TCPClient {

        /**
        * 客户端
        */
        public static void main(String[] args) {
                      try {
                           Socket socket= new Socket( "192.168.2.145" ,8000);
                           BufferedReader sin= new BufferedReader( new InputStreamReader(System. in ));
                           PrintWriter os= new PrintWriter(socket.getOutputStream());
                           BufferedReader is= new BufferedReader( new InputStreamReader(socket.getInputStream()));
                           String readLine;
                           readLine=sin.readLine();
                         while (!readLine.equals( "exit" )){
                            os.println(readLine);//由客户端开始,发送数据给服务器。
                            os.flush();
                            System. out .println( "Client:" +readLine);
                            System. out .println( "Server:" +is.readLine());
                            readLine=sin.readLine();
                         }
                         os.close();
                         is.close();
                         socket.close();
                     } catch (UnknownHostException e) {
                           e.printStackTrace();
                     } catch (IOException e) {
                           e.printStackTrace();
                     }      
       }

}



4、 总结:在这里一定要理解输入输出流的意思,输入流和输出流的不同在于他们的作用不同,一个用来输入数据,一个用来输出数据,但是他们操作的是同一个文件,就比如说,就像在Socket中,InputStream中的数据是由OutputStream来输入的,他俩的内容是一样的,而且,输入一次才可以输出一次,比如你Writer了一次,现在可以Reader一次,如果read两次,第二次并不会执行,因为没有东西可以read,只能进行再次的write才能进行read。

那么现在我来写一个自己的QQ程序:跟上面差不多。
客户端:
public class TCPClient {

      /*
      * Socket 网络通信客户端
      */
     
      public static void main(String[] args) throws UnknownHostException, IOException {
     
          Socket socket = new Socket( "127.0.0.1" ,8888);
          BufferedReader sysin= new BufferedReader( new InputStreamReader(System. in ));
          PrintWriter os= new PrintWriter(socket.getOutputStream());
          
          BufferedReader is= new BufferedReader( new InputStreamReader(socket.getInputStream()));
          
          String line;
           while (!(line=sysin.readLine()).equals( "exit" )){
          os.println(line);
          os.flush();
          System. out .println( "I am Client, what I say is:" +line);
          System. out .println( "I am Client , what Server say is :" +is.readLine());
          
          
     }
          os.close();
          is.close();
          socket.close();

}
}
服务器端:

public class TCPServer {

        /**
        * Socket网络通信服务器端
        */
        public static void main(String[] args) {
             
      try {
              ServerSocket server= null ;
               try {
                    
                  server= new ServerSocket(8888);
                
              } catch (Exception e){
                     System. out .println( "Server cannot start normally!" );
              }
              Socket socket= null ;
               try {
                 System. out .println( "************服务器已经启动****************" );
                     socket=server.accept();
                    
              } catch (Exception e){
                     System. out .println(e);
              }
             
              BufferedReader sysin= new BufferedReader( new InputStreamReader(System. in ));
              BufferedReader is= new BufferedReader( new InputStreamReader(socket.getInputStream()));
              PrintWriter os= new PrintWriter(socket.getOutputStream());
              System. out .println( "I am Server,Client says:" + is.readLine());
              String  line=sysin.readLine();
            
              while (!(line.equals( "exit" ))){
                System. out .println( "I am Server,I am saying :" +line);
                os.println(line);
                os.flush();//在这里就要注意顺序了。先把自己的数据发了再去获取下一次的。
                System. out .println( "I am Server,Client says:" + is.readLine());
               
                line=sysin.readLine();
               
              }
            
              os.close();
              is.close();
              socket.close();
              server.close();
             
       } catch (Exception e){
              e.printStackTrace();
       }
}
}


5、基于多线程的Socket编程
在实际的网络通信中,通常是一个服务器要为很多台客户机提供服务,
在这种情况下就要对来自不同IP地址的连接进行分别的处理,首先想到的肯定是线程,因为线程是独立运行,在run()方法中可以对需要独立执行的代码进行执行。
此时就要建立一个Socket连接处理的线程类,每获得一个Socket连接,就要实例化一个该类。
public class ServerThread extends Thread {
      // 和本线程相关的Socket
     Socket socket = null ;

      public ServerThread(Socket socket) {
           this . socket = socket;
     }
      //线程执行的操作,响应客户端的请求
      public void run(){
          InputStream is= null ;
          InputStreamReader isr= null ;
          BufferedReader br= null ;
          OutputStream os= null ;
          PrintWriter pw= null ;
           try {
               //获取输入流,并读取客户端信息
              is = socket .getInputStream();
              isr = new InputStreamReader(is);
              br = new BufferedReader(isr);
              String info= null ;
               while ((info=br.readLine())!= null ){ //循环读取客户端的信息
                   System. out .println( "我是服务器,客户端说:" +info);
              }
               socket .shutdownInput(); //关闭输入流
               //获取输出流,响应客户端的请求
              os = socket .getOutputStream();
              pw = new PrintWriter(os);
              pw.write( "欢迎您!" );
              pw.flush(); //调用flush()方法将缓冲输出
          } catch (IOException e) {
              e.printStackTrace();
          } finally {
               //关闭资源
               try {
                    if (pw!= null )
                        pw.close();
                    if (os!= null )
                        os.close();
                    if (br!= null )
                        br.close();
                    if (isr!= null )
                        isr.close();
                    if (is!= null )
                        is.close();
                    if ( socket != null )
                         socket .close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
     }
}

然后再监听的服务端可以这么写:
public class Server {
      public static void main(String[] args) {
           try {
               //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
              ServerSocket serverSocket= new ServerSocket(8888);
              Socket socket= null ;
               //记录客户端的数量
               int count=0;
              System.out.println( "***服务器即将启动,等待客户端的连接***" );
               //循环监听等待客户端的连接
               while ( true ){
                    //调用accept()方法开始监听,等待客户端的连接
                   socket=serverSocket.accept();
                    //创建一个新的线程
                    ServerThread serverThread= new ServerThread(socket);
                    //启动线程
                   serverThread.start();
                   
                   count++; //统计客户端的数量
                   System.out.println( "客户端的数量:" +count);
                   InetAddress address=socket.getInetAddress();
                   System.out.println( "当前客户端的IP:" +address.getHostAddress());
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }

客户端就不用变了。


第五章  基于UDP的编程
1、所要用的核心API有:
DatagramSocket、DatagramPacket

2、服务器端的过程如下:
@1、创建DatagramSocket,指定端口号
@2、创建DatagramPacket制定用来存放接受数据的位置
@3、接受数据
@4、读取接受的数据


3、客户端的过程如下:
@1、定义各种信息,InetAddress,port以及所要发送的数据
@2、创建DatagramPacket,用来生成发送的数据包
@3、创建DatagramSocket,用来发送数据
@4、发送数据

代码如下:
/*
 * 服务器端,实现基于UDP的用户登陆
 */
public class UDPServer {
      public static void main(String[] args) throws IOException {
           /*
           * 接收客户端发送的数据
           */
           //1.创建服务器端DatagramSocket,指定端口
          DatagramSocket socket= new DatagramSocket(8800);
           //2.创建数据报,用于接收客户端发送的数据
           byte [] data = new byte [1024]; //创建字节数组,指定接收的数据包的大小
          DatagramPacket packet= new DatagramPacket(data, data. length );
           //3.接收客户端发送的数据
          System. out .println( "****服务器端已经启动,等待客户端发送数据" );
          socket.receive(packet); //此方法在接收到数据报之前会一直阻塞
           //4.读取数据
          String info= new String(data, 0, packet.getLength());
          System. out .println( "我是服务器,客户端说:" +info);
          
           /*
           * 向客户端响应数据
           */
           //1.定义客户端的地址、端口号、数据
          InetAddress address=packet.getAddress();
           int port=packet.getPort();
           byte [] data2= "欢迎您!" .getBytes();
           //2.创建数据报,包含响应的数据信息
          DatagramPacket packet2= new DatagramPacket(data2, data2. length , address, port);
           //3.响应客户端
          socket.send(packet2);
           //4.关闭资源
          socket.close();
     }
}

/*
 * 客户端
 */
public class UDPClient {
      public static void main(String[] args) throws IOException {
           /*
           * 向服务器端发送数据
           */
           //1.定义服务器的地址、端口号、数据
          InetAddress address=InetAddress.getByName( "localhost" );
           int port=8800;
           byte [] data= "用户名:admin;密码:123" .getBytes();
           //2.创建数据报,包含发送的数据信息
          DatagramPacket packet= new DatagramPacket(data, data. length , address, port);
           //3.创建DatagramSocket对象
          DatagramSocket socket= new DatagramSocket();
           //4.向服务器端发送数据报
          socket.send(packet);
          
           /*
           * 接收服务器端响应的数据
           */
           //1.创建数据报,用于接收服务器端响应的数据
           byte [] data2= new byte [1024];
          DatagramPacket packet2= new DatagramPacket(data2, data2. length );
           //2.接收服务器响应的数据
          socket.receive(packet2);
           //3.读取数据
          String reply= new String(data2, 0, packet2.getLength());
          System. out .println( "我是客户端,服务器说:" +reply);
           //4.关闭资源
          socket.close();
     }
}

总结:
    
    重点:
              Socket通信原理
              基于TCP的Socket编程
    经验与技巧:
             多线程中要设置线程的优先级,否则会很慢,因为系统的线程居多,不定什么时候运行呢
             是否关闭输入输出流,有时候关闭输入输出流会导致Socket也关闭了,这不是我们想要的
             我们进行传输的时候,用的都是字符串的示例,但是实际生活中,更多的是传对象或者文件。
   
            





































  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值