1、TCP/IP协议是一种可靠的网络协议,在通信的两端各建立以个Socket,从而在两端之间形成网络虚拟链路。
IP协议只负责在两个客户端之间传输数据,但不能处理数据分组在传输过程中可能出现的问题,所以连上Internet的计算机还是要安装TCP协议来提供可靠的无差错通信服务。
TCP协议重发机制:当一个通信实体给另一个通信实体发送消息后,需要收到另一个通信实体的确认信息,如果没有收到另一个通信实体的确认消息,则会重发刚才发送的消息。
2、使用ServerSocket创建TCP服务器端
两个通信实体没有建立虚拟链路之前,必须要有一个通信实体先做出“主动姿态”,主动接受来自其他通信实体的连接请求。
Java中接受其他通信实体连接请求的类是ServerSocket对象,可以监听来自客户端的Socket连接,如果没有连接,则一直处于等待状态。
ServerSocket包含一个监听来自客户端请求方法:Socket Accpet(),这个方法返回一个与连接客户端Socket对应的Socket。
ServerSocket的构造方法:

  • ServerSocket(int port),用指定端口创建。
  • ServerSocket(int port,int backlog),增加一个用来改变连接队列长度的参数backlog
  • ServerSocket(int port,int backlog,InetAddress localAddr),在机器存在多个IP地址情况下,使用localAddr这个参数将这个Socket绑定到指定的IP地址。

3、使用Socket进行通信
客户端使用Socket的构造器来连接指定服务器。
Socket构造方法:

  • Socket(InetAddress/String remoteAddress,int port),创建连接指定远程主机,远程端口的Socket,默认使用本地主机默认的IP地址,系统动态分配的端口。
  • Socket(InetAddress/String remoteAddress,int port,InetAddress localAddr,int localPort),指定本地IP地址和本地端口。

创建了Socket后 ,就会连接到指定的服务器让服务器端的ServerSocket的accpt()方法向下执行。于是服务器端和客户端就产生了一对互相通信的Socket。
之后服务器端使用输出流输出数据,客户端使用输入流接受客户端输入的流就可以进行数据通信了。
假如程序需要为Socket连接服务器时指定超时时长,程序需要先创建一个无连接的Socket,再调用Socket的connect()方法来连接远程服务器。代码如下:

Socket socket = new socket();
socket.connet(new InetSocketAddress(host,port),10000);
//10000就是设定的超时时间

4、加入多线程
实际应用中的客户端可能需要和服务器端保持长时间的通信,即客户端需要不断的读取客户端数据,并向客户端写入数据,同时客户端也需要持续的做这些动作。而这个过程线程会被阻塞,程序无法继续运行。
所以服务器应该为每个Socket单独启动一条线程,每条线程负责与一个客户端进行通信。
同样的,客户端读取服务器数据的线程同样会被阻塞,所以系统应该单独启动一条线程,这个线程专门负责读取服务器的数据。
服务器端程序如下:

public class Socket_server {


    public static ArrayList<Socket> socketList = new ArrayList<Socket>();

    public static void main(String[] args) {
        // TODO Auto-generated method stub
         try {
            ServerSocket serverSocket = new ServerSocket(8080);
            while (true) {
                Socket socket = new Socket();
                socket = serverSocket.accept();
                socketList.add(socket);
                new Thread(new ServerThread(socket)).start();

            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


}

服务器端为每个线程启动吧一个线程,线程类的代码如下:

public class ServerThread implements Runnable {
     Socket s = null;

     BufferedReader br = null;

     public  ServerThread(Socket s) {
         s = this.s;
         try {
            br = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8"));
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
       String content = null;
       while ((content = readFromClient()) != null) {
        for(int i =0;i<Socket_server.socketList.size();i++){
            s = Socket_server.socketList.get(i);
            try {
                OutputStream outputStream = s.getOutputStream();
                outputStream.write((br.readLine()+"\n").getBytes("utf-8"));

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();

            }

        }
    }
    }

    private String readFromClient() {
        // TODO Auto-generated method stub
        try {
            String line = br.readLine();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Socket_server.socketList.remove(s);
        }
        return null;
    }

}

客户端的程序包含两条线程,一条杜泽生成界面,响应用户动作,并将用户输入的数据写入socket对应的输出流中,另一条负责读取从socket输入流中的数据,并将这些数据在界面上显示出来。客户端程序如下:

public class MainActivity extends ActionBarActivity {
private Button send;
private EditText input;
private TextView show;
private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        send = (Button)findViewById(R.id.send); 
        input = (EditText)findViewById(R.id.input);
        show = (TextView)findViewById(R.id.show);
        handler = new Handler(){
            public void handleMessage(Message msg) {
                if(msg.what == 1){
                    show.append("\n"+msg.obj.toString());           }
            };
        };

        ClientThread clientThread = new ClientThread(handler);
        new Thread(clientThread).start();


        send.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String content = input.getText().toString();
                Message message = new Message();
                message.what = 0;
                message.obj = content;
              clientThread.revHandler.sendMessage(message);

            }
        });

    }



}

为了避免UI进程被阻塞,所以与网络有关的操作都被封装到clientThread中,通过clientThread中的revhandler来传递消息,同时clientThread中的UI操作都通过UI activity中的handler来处理。
clientThread的代码如下:

public ClientThread(Handler handler) {
    // TODO Auto-generated constructor stub
    handler = this.handler ;
}
    @Override
    public void run() {
        // TODO Auto-generated method stub
         try {

         socket = new Socket("10.0.2.2", 8080);
         br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
         out = socket.getOutputStream();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
         new Thread(){

            String content = null;

             public void run() {
                 try {
                    while ((content = br.readLine().toString())!=null){
                         Message message = new Message();
                         message.obj = content;
                         message.what = 1;
                         handler.sendMessage(message);
                     }
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
             };
         }.start();
         Looper.prepare();
         revHandler = new Handler(){
             @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                super.handleMessage(msg);
                while (msg.what == 0) {
                    try {
                        out.write(msg.obj.toString().getBytes("utf-8"));


                    } catch (UnsupportedEncodingException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                }
            }
         };
         Looper.loop();




    }

}