网络编程之二:java.net包的Socket和SocketServer

来源:互联网 发布:空调结构设计知乎 编辑:程序博客网 时间:2024/04/28 21:50
本博文简要阐述java.net包下的基于TCP/IP的Socket编程以及其源码分析

    写网络编程这一章,初衷是为了讲诉HttpClient、Jetty、Netty的使用心得,NIO和BIO的比较,Thrift和Avro等,无奈今天被合作伙伴鄙视了,他看到我用了Apache的Httpclient(C)+Jetty(S),说:你只不过是传一些数据,用Socket多简单迅速,我们的量一天十几T,难怪我这边一堆的timeout。后来他告诉我他用得是“c调用linux的Socket接口”写的自己的库,很骄傲表示速度杠杠的(工程量变大是一弊端),于是我也看了一下java.net基础包下的东西,会有两章,本章主要是将应对TCP/IP的Socket部分。

    java.net包含以下几类:DatagranSocket一类,Socket/ServerSocket一类,InetXAddress一类,URL相关一类以及其他,本章主要讲诉的是Socket/ServerSocket
    Socket编程,无非就是一个Socket(C)去连接一个ServerSocket(S),所以编写了两个简单的程序并进行分析,功能有一个DataAcceptServer,可以创建4444端口,能够最多接受50个连接;两个DataSender,创建长连接,知道发送byebye为止。为方便,使用UTF-8字符流。程序如下:
//Server
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.net.ServerSocket;
importjava.net.Socket;
publicclass DataAcceptServer {
       public static voidmain(String args[]) throws IOException {
             ServerSocket server = null;
              server =new ServerSocket(4444);// 设置server的端口
              Socketsocket = null;
              int number= 0;
              booleanflag = true;
              try{
                    while (flag) {
                           socket = server.accept();//监听连接,方法阻塞,能够建立多个长连接
                           number++;
                           createHandlerThread(socket,number);// 为每一个连接建立线程
                    }
              } finally{
                    server.close();
             }
       }

       private static voidcreateHandlerThread(Socket socket, int number) {
             DataAcceptServer das = new DataAcceptServer();
              Handlerhandler = das.new Handler(socket, number);
              Thread t =new Thread(handler);
             t.start();
       }

       public class Handlerimplements Runnable {
              privateSocket socket = null;
              privateint number = 0;

              publicHandler(Socket socket, int number) {
                    this.socket = socket;
                    this.number = number;
             }

             @Override
              publicvoid run() {
                    System.out.println("get the connetion withclient" + number);
                    BufferedReader in = null;
                    PrintWriter out = null;
                    try {
                           in = new BufferedReader(newInputStreamReader(
                                        socket.getInputStream(), "UTF-8")); //获取socket的接收流
                           out = new PrintWriter(newOutputStreamWriter(
                                        socket.getOutputStream(), "UTF-8"));//获取socket的发送流
                    } catch (IOException e) {
                          e.printStackTrace();
                    }
                    String getline = null;
                    boolean end = false;
                    while (!end) {
                           try {
                                  getline =in.readLine(); // 获取消息
                           } catch (IOException e){
                                 e.printStackTrace();
                                 break;
                           }
                           System.out.println("getmessage from client" + number + " :"
                                  + getline);
                           out.write("ok\n"); //返回ok
                           out.flush();
                           if (getline.equals("byebye")){
                                  end =true;
                           }
                    }
                    try {
                           in.close();
                           out.close();
                           socket.close();
                    } catch (IOException e) {
                          e.printStackTrace();
                    }
                    System.out.println("release the connetion withclient" + number);
             }
       }
}
    ServerSocket类在java.net下并不起眼,也不完成主要的工作(SocketImpl的子类PlainSocketImpl和它的子类SocksSocketImpl完成了最重要的工作)
    第一步:ServerSocket server = newServerSocket(4444)
    1、转化为ServerSocket(int port, intbacklog, InetAddress bindAddr)构造方法,port(必需)+ 最大连接数(默认50)+服务地址(默认本机)。ip+端口会以InetSocketAddress类形式存在。
    2、SecurityManager security =System.getSecurityManager();security会去检测此port是否可用
    3、bind(),由PlainSocketImpl中的native方法socketBind来完成,也就是使用系统调用来完成创建和绑定端口的工作
    4、listen(),PlainSocketImpl中的native方法socketListen来完成,也就是使用系统调用来完成设置最大连接数,创建queuefor incoming connectionindications(a request toconnect),其中的count参数就是最大连接数。如果thequeue满了了话,将会refuse后面的connection
    第二步:Socket socket= server.accept();
    1、accept(SocksSocketImpl s),是一个阻塞方法,由PlainSocketImpl的native方法socketAccept来完成,也就是使用系统调用来监听是否有连接进来,直到有一个连接
    2、响应连接,创建Socket对象,同时赋予其一个非常最要的对象SocksSocketImpl,它完成了我们所见到socket对象的所有的工作。
    第三步:socket.getOutputStream()和socket.getInputStream()来获取SocksSocketImpl对象的输入输出流。接下来的工作就使用流来处理了。

//client
importjava.io.BufferedReader;
import java.io.IOException;
importjava.io.InputStreamReader;
importjava.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
importjava.net.UnknownHostException;
public class DataSender {
       public static voidmain(String args[]) throwsUnknownHostException,
                    IOException {
              Socketsocket = new Socket("127.0.0.1", 4444); //设置server的ip和端口
              //socket.setSoTimeout(10000);//设置超时时间
             BufferedReader sin = new BufferedReader(newInputStreamReader(
                          System.in));
             PrintWriter out = new PrintWriter(newOutputStreamWriter(
                           socket.getOutputStream(),"UTF-8"));//获取socket的发送流
             BufferedReader in = new BufferedReader(newInputStreamReader(
                           socket.getInputStream(),"UTF-8"));//获取socket的接收流
              Stringreadline = null;
              readline =sin.readLine();
              while(!readline.equals("byebye")) {//以byebye终止连接
                    out.println(readline);
                    out.flush();
                    System.out.println("Client:" +readline);
                    System.out.println("Server:" +in.readLine());
                    readline = sin.readLine();
             }
             out.close();
             in.close();
             socket.close();
      }
}
    Socket类就更为简单了,但是背后的秘密也是值得挖掘,他完成的主要是创建对某个服务的连接,并传输数据或者命令
    第一步:Socketsocket = new Socket("127.0.0.1", 4444);
    1、首先为自己创建一个SocksSocketImpl对象
    2、bind(),PlainSocketImpl中的native方法socketBind来完成,也就是使用系统调用来完成绑定端口的工作
    3、connect(),由SocksSocketImpl对象的connect来进入(因为涉及到安全验证和各种不同的connect方法如代理等),最终由PlainSocketImpl的socketConnect来完成,也是使用系统调用来完成connect的工作。
    第二步:socket.getOutputStream()和socket.getInputStream()来获取SocksSocketImpl对象的输入输出流。接下来的工作就使用流来处理了。

    看到这里,其实socket编程最核心的工作都并不是由它本身来完成,而是使用非java方法来完成,JNI调用系统C库(会在之后的章程介绍),因此我认为,在承受范围之内,慢并不是java的错,而是我实现的有问题。
    最后总结一张图:


    由于博主知识有限,如有误,请指正点评,欢迎交流

0 0
原创粉丝点击