【Java网络】Socket 多线程, 一对多服务

2 篇文章 0 订阅
再说ServerSocket;
 1.C/S模式中,Server需要创建特定端口的ServerSocket.->其负责接收client连接请求.
 2.线程池->包括一个工作队列和若干工作线程->工作线程不断的从工作队列中取出任务并执行.-->java.util.concurrent->线程池
 3.server可利用多线程来处理与多个客户的通信任务.
 
1.构造ServerSocket
 1.重载形式
  ServerSocket() throws IOException
  ServerSocket(int port) throws IOException
  ServerSocket(int port,int backlog) throws IOException
  ServerSocket(int port,int backlog,InetAddress bindAddr) throws IOException
 port指定服务器要绑定的端口,即服务器要监听的端口;backlog指定客户连接请求队列的长度;bindAddr指定服务器要绑定的IP地址.
 
 2.除了第一个构造方法,其他方法都会与特定端口绑定.
  a.如ServerSocket serverSocket = new ServerSocket(80)
  如果运行时无法绑定到80端口,则会抛出BindException.原因->
   1.端口已经被其他服务器进程占用
   2.某些os中,如果没有以超级用户的身份来运行server,则os不允许绑定到1-1023之间的端口
  b.port设为0->表示由os来为server分配一个任意可用的端口->匿名端口->
   ->大部分服务器需要使用明确的端口.->因为client程序需要事先知道server端口,才能方便访问server.
   ->匿名端口一般适用于server与client之间的临时通信,通信结束则断开连接,且SeverSocket占用的临时端口也被释放.{@link ServerSocket#getLocalPort()}
   ->如ftp协议->两个并行的TCP连接->控制连接(21端口)和数据连接.
    a.TCP client创建一个监听匿名端口的ServerSocket.再把这个ServerSocket监听的端口好发送给TCP服务器.然后TCP服务器主动请求与client连接.此连接用于和服务器建立临时的数据连接.这种方式就是用了匿名端口.
 
 3.设定client连接请求队列的长度
  1.server运行时,可能会同时监听到多个client的连接请求.
  2.管理client连接请求的任务是由os来完成的.os将这些连接请求存储在一个先进先出队列中.
  3.许多os限定了队列的最大长度.一般为50.
  4.当队列中的连接请求达到了队列的最大容量时,server进程所在的主机会拒绝新的连接请求.->只有当server进行通过ServerSocket#accept从队列中取出连接请求->队列腾出空位->队列才能继续加入新的连接请求.
  5.对于client来说,如果其连接请求被加入的server的连接队列中,就意味着client与server连接建立成功->client进程从Socket的构造方法中返回
  ->如果client进程发出的连接请求被server拒绝,则Socket构造方法抛出ConnectionException. 
  6.ServerSocket构造中的backlog参数用来显示的设置连接请求队列的长度,其将覆盖os限定的队列的最大长度.->注意依然采用os限定的队列的长度的情况:
   1.backlog参数的值大于os限定的队列的最大长度.
   2.backlog参数的值小于或等于0
   3.其构造方法中未设置backlog参数.
   
 4.设定绑定的ip地址
  1.如果主机只有一个ip地址,默认情况下,server程序与其绑定.
  2.ServerSocket构造方法可显示的指定服务器要绑定的ip地址,该构造方法适用于具有多个ip地址的主机.如一个主机具有两个网卡.一个用于连接Internet,以一个用于连接本地局域网.如果server仅仅被本地局域网访问则可显示指定绑定的ip地址.
  
 5.默认构造方法的作用
  1.ServerSocket有一个不带参数的构造方法->其创建的ServerSocket不与任何端口绑定->需调用bind与特定端口绑定.
  2.其用于在于允许server在绑定到特定端口之前,可先设置SerevrSocket的一些选项->因为一旦与特定端口绑定,有些选项就不能再用.
   如:
    ServerSocket serverSocket = new ServerSocket();
    serverSocket.setReuseAddress(true);//先设置serverSocket的选项,该选项必须在bind之前才有效
    serverSocket.bind(new InetAddress(8000));//再执行绑定端口
多线程的ServerSocket
多线程的好处不用多说,而且大多数的场景都是多线程的,无论是我们的即时类游戏还是IM,多线程的需求都是必须的。下面说说实现方式:
  • 主线程会循环执行ServerSocket.accept();
  • 当拿到客户端连接请求的时候,就会将Socket对象传递给多线程,让多线程去执行具体的操作;
实现多线程的方法要么继承Thread类,要么实现Runnable接口。当然也可以使用线程池,但实现的本质都是差不多的。
 
这里举例:
下面代码为服务器的主线程。为每个客户分配一个工作线程:
public void service(){
    while(true){
        Socket socket=null;
        try{
            socket=serverSocket.accept();                        //主线程获取客户端连接
            Thread workThread=new Thread(new Handler(socket));    //创建线程
            workThread.start();                                    //启动线程
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

当然这里的重点在于如何实现Handler这个类。Handler需要实现Runnable接口:
class Handler implements Runnable{
    private Socket socket;
    public Handler(Socket socket){
        this.socket=socket;
    }
    
    public void run(){
        try{
            System.out.println("新连接:"+socket.getInetAddress()+":"+socket.getPort());
            Thread.sleep(10000);
        }catch(Exception e){e.printStackTrace();}finally{
            try{
                System.out.println("关闭连接:"+socket.getInetAddress()+":"+socket.getPort());
                if(socket!=null)socket.close();
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}



【程序实现】

客户端不变

服务器端:加入线程类

接受和传递客户的信息都放在thread类里面

主类:server主要负责建立连接,一旦和用户取得连接,就分配一个thread

package socketDemo;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class chatServerThread {
	private int port=8099;
	public chatServerThread(){}
	//创建指定端口的服务器
	public chatServerThread(int port){
	this.port=port;
	}
	//提供服务
	public void service(){
		int i=0;
		try{//建立服务器连接
			ServerSocket server= new ServerSocket(port,3);//可连接的数量
			//等待客户连接
			while(true){
				Socket socket=server.accept();
				i++;
				System.out.println("客户端"+i+"连接成功!");
				new Thread(new ServerThread(socket)).start();
			}
			}catch(IOException e){
				e.printStackTrace();
			}
			
	}
	
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new chatServerThread().service();  
	}

}
class ServerThread implements Runnable{
	private Socket socket;
	public ServerThread(Socket socket){
		this.socket=socket;	
	}
 @overide
	//任务是为一个用户提供服务	
	public void run(){
		try{
			try{
			//读取客户端传过来信息的DataInputStrem
			DataInputStream in= new DataInputStream(socket.getInputStream());
			//向客户端传过来信息的DataOutputStram
			DataOutputStream out= new DataOutputStream(socket.getOutputStream());
			//获取控制台输入的Scanner:
			Scanner scanner = new Scanner(System.in);
			while(true){
				//读取来自客户端的信息
				String accept= in.readUTF();				
				out.writeUTF("服务器:"+accept);
				
			}
		}finally{
			socket.close();
		}	
	}catch(IOException e){
		e.printStackTrace();
	}
	}
}

 

运行结果:

                                 



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值