再说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));//再执行绑定端口
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();
}
}
}
运行结果: