`
webdev2014
  • 浏览: 670332 次
文章分类
社区版块
存档分类
最新评论

步步测试完善Java中Socket通信图解法(三)

 
阅读更多

目录


简介

Java中Socket通信简介

单线程一对一服务器1——>1客户端

单线程一对一服务器1<——>1客户端

多线程一对多服务器1<——>N客户端【非聊天室的服务器通过用户输入发送数据】

多线程一对多服务器1<——>N客户端【聊天室】

多线程最终服务器和客户端集成一体【swing程序】



【多线程】一对多服务器1<——>N客户端(非聊天室的服务器通过用户输入发送数据)
对,解决方案,就是发送数据和接受数据不在同一个进程,这样,这两个进程互不影响。
解决方案如下:服务器端和客户端如下:
当然,若下文要考虑,多个客户端连接同一个服务端时,所以,应该对每个sockt连接开启一个线程。但是对于客户端而言,只要把发送和接受分离即可。所以在客户端中,可以把发送数据和主进程放在一块。如下图:
代码实现如下:
服务器端:
 package com17.tcp;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;


public class MyServer {
    
     public static void main(String[] args) {
          try {
               ServerSocket ss=new ServerSocket(30000);
               while(true)
               {
                    //此行代码会阻塞,将一直等待别人的连接
                    Socket s=ss.accept();
                    if(s.isConnected())
                    {
                         System.out.println("一个客户端连接此服务器"+s.getInetAddress());
                    }
                    //每当客户端连接后启动一条ServerThread线程为该客户端服务
                    new Thread( new ServerThread(s)).start();
                  //发送数据到客户端
                    new Thread(new ServerThread2(s)).start();
               }
          } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }

     }

}

接受数据的并打印的线程:
package com17.tcp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ServerThread implements Runnable {

     //定义当前线程所处理的socket
     Socket s=null;
     //该线程所处理的socket所对应的输入流
     BufferedReader br=null;
    
     public ServerThread(Socket s)
     {
          try
          {
               this.s=s;
               //初始化socket对应的输入流
               br=new BufferedReader(new InputStreamReader(s.getInputStream()));
              
          }
          catch(IOException e)
          {
               e.printStackTrace();
          }
    
     }
    
     @Override
     public void run() {

         try
         {
              String content=null;
             //采用循环不断从Socket中读取客户端发送过来的数据
             while((content=readFromClient())!=null)
             {
                  System.out.println("来自客户端消息:"+content);
             }
             System.out.println("消息:"+content);
             PrintStream ps=new PrintStream(s.getOutputStream());
             ps.println(br.readLine());
         }catch(Exception e)
         {
              try
              {
                   s.close();
              }catch(IOException ex)
              {
                   ex.printStackTrace();
              }
         }

     }
    
     //定义读取客户端数据的方法
     private String readFromClient()
     {
          try
          {
               return br.readLine();
          }
          //如果捕捉到异常,表明该socket对应的客户端已经关闭
          catch(IOException e)
          {
              e.printStackTrace();
          }
        return null;
     }
}

发送数据到客户端的线程:
package com17.tcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

public class ServerThread2 implements Runnable {

     private Socket s;
     //用来处理发送数据的
     private PrintStream ps=null;
     public ServerThread2(Socket s)
     {
          try
          {
               this.s=s;
               ps=new PrintStream(s.getOutputStream());
          }
          catch(IOException e)
          {
               try
               {
                    s.close();
               }
               catch(IOException ex)
               {
                    ex.printStackTrace();
               }
              
          }

     }
     @Override
     public void run() {
          try
          {
                 //发送数据到客户端
                  String line=null;
                 
                  //不断读取键盘的输入
                  BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
                  while((line=br.readLine())!=null)
                  {
                       //将用户的键盘输入内容写入socket对应的输出流
                       ps.println(line);
                  }
          }
          catch(IOException e)
          {
               e.printStackTrace();
          }

     }

}

客户端:
package com17.tcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

//负责读取用户的键盘输入,并将用户输入的数据写入socket对应的输出流中
//一条负责读取socket对应输入流中的数据(从服务器发送过来的数据)
//并将这些数据打印输出。其中负责读取用户键盘输入的线程由MyClient负责,也就是由程序的主线程负责。
public class MyClient {


	public static void main(String[] args) {
		
		try
		{
		   Socket s=new Socket("127.0.0.1",30000);
		   //客户端启动clientThread县城不断读取来自服务器的数据
		   new Thread(new ClientThread(s)).start();
		   //获取该socket对应的输出流
		   PrintStream ps=new PrintStream(s.getOutputStream());
		   String line=null;
		   //不断读取键盘的输入
		   BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		   while((line=br.readLine())!=null)
		   {
			   //将用户的键盘输入内容写入socket对应的输出流
			   ps.println(line);
		   }
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
		
       
	}

}
客户端线程:

package com17.tcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class ClientThread implements Runnable {

	//该线程负责处理的socket
	private Socket s;
	//该线程所处理的socket所对应的输入流
	BufferedReader br=null;
	
	public ClientThread(Socket s) throws IOException
	{
		this.s=s;
		br=new BufferedReader(new InputStreamReader(s.getInputStream()));	
	}
	
	@Override
	public void run() {
		
        try
        {
        	String content=null;
        	//不断读取socket输入流中的内容,讲这些内容打印输出
        	while((content=br.readLine())!=null)
        	{
        		System.out.println("来自服务器端消息:"+content);
        	}
        }
        catch(IOException e)
        {
        	e.printStackTrace();
        }
	}

}

效果如下:
但是我们通过以上的代码,要实现这种如下:
虽然上述代码中使用了多线程,每个客户端请求,都会产生一个读线程和写线程。但是也不能完成上图的要求。
为什么呢?
主要在于写的线程。
因为写的线程,虽然捕捉了发送的socket套接字,但是写线程中是时刻监听用户输入System.in。
这样,当多个客户端连接服务器端时,则服务器端有多个线程监听用户输入System.in。但是令人头疼的是,服务器端只有一个dos窗口,而不像客户端一个dos窗口监听一个用户输入System.in。
访问中服务器端结果如下:
所以服务器想通过用户输入形式发送给多个客户端,这种形式是不可行的。
代码中,永远只能发给具有活动的线程。结果不是你想象的那样,发给你想象的线程。
当然,若是服务器不是通过监听用户输入则当然可以实现上述的要求的
我们改一下服务器端:不通过监听用户输入。
服务器端中,接受线程中,一接受到客户的数据,则发送数据。
服务器端,线程如图:
接受线程中修改上述的demo
           //采用循环不断从Socket中读取客户端发送过来的数据
        while((content=readFromClient ())!=null )
        {
              System. out.println( "来自客户端消息:" +content);
              ps.println( "客户端"+s.getInetAddress()+"消息已经收入到,消息为:" +content);
        }

效果图如下:
【多线程】一对多服务器1<——>N客户端(聊天室)
我想通过上述几个例子,想必对通信的方式已经了解,并且也掌握通信的原理以及通信方法。
那我们再说,聊天室通信,就是类似我们。一人发表了看法,则其他的人都能看到此消息。
猜想qq实现方式,就是通过服务器转发【仅此个人看法】
那我们这个例子实现方式如下图:
那这个例子 不在贴代码,我们说一下思路,在服务器端有个列表,专门用来保存客户端的socket。只要客户端连接服务器,就把客户端的socket添加到服务器列表中。当接受到任何客户端数据时,就把数据转发给列表中任何一个客户端socket。
部分代码如下:
        //采用循环不断从Socket中读取客户端发送过来的数据
        while((content=readFromClient())!= null)
        {
              //便利socketList中的每一个socket
              //将读到的内容向每个socket发送一次
              for(Socket s:MyServer. socketList)
              {
                   PrintStream ps= new PrintStream(s.getOutputStream());
                   ps.println(content);
              }
        }

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics