Java笔记二十四.TCP网络编程

    TCP网络编程
转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空)
从上面一节内容可以知道,利用UDP通信的两个程序是平等的,无主次之分,两个程序代码可以完全一样。但利用TCP协议进行通信的两个应用程序,是有主从之分的,一个称为服务器程序,另外一个称为客户机程序。Java中提供了ServerSocket类用于创建服务器端的socket,Socket类用于创建客户端socket。
一、APIs简介
java.net.ServerSocket
(1)功能:用于创建服务器端Socket,服务端Socket将等待网络上的客户端应用发来连接请求。
(2)构造方法
>ServerSocket() :
>ServerSocket(int port)
>ServerSocket(int port, int backlog)
>ServerSocket(int port, int backlog, InetAddress bindAddr)
   用第一个构造方法创建一个ServerSocket对象,没有与任何端口号绑定,不能被直接使用,还要继续调用bind方法,才能完成其他构造方法所完成的功能。
     用第二个构造方法创建一个ServerSocket对象,并与指定的端口号绑定。如果port为0,系统会应用分配一个还没有被其他网络程序所使用的端口号,作为服务器程序,端口号必须事先指定,其他客户才能根据这个端口号进行连接
     用第三个构造方法创建一个ServerSocket对象,与指定的端口号绑定并根据backlog参数指定在服务器忙时可以与之保持连接请求的等待客户数量(默认50)。
      用第三个构造方法创建一个ServerSocket对象,与指定的端口号绑定、指定等待客户数量、指定本机IP。
(3)常用方法
Socket accept():服务器端等待客户端发送连接请求,并返回服务器端的Socket
void bind(SocketAddress endpoint):将服务器Socket与指定的地址(IP地址和端口号)绑定
void close():关闭该Socket
InetAddress getInetAddress():返回该服务器socket的本地地址(IP地址和端口号)
int getLocalPort():返回该服务器socket的本地端口号
java.net.Socket
(1)功能:客户端要与服务器建立连接,必须先要创建一个Socket对象。
(2)构造方法
>Socket()
>Socket(String host, int port) 
>Socket(InetAddress address, int port)
>Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
>Socket(String host, int port, InetAddress localAddr, int localPort) 
    用第一个构造方法创建Socket对象,不与任何服务器建立连接,不能被直接使用,需要调用connect方法。
    用第二或个构造方法创建Socket对象,与指定的IP地址和端口号的服务器程序建立连接。其中,String host为字符串格式地址,InetAddress address为InetAddress对象所包装的地址。
    用第四或五个构造方法创建Socket对象,与指定的IP地址和端口号的服务器程序建立连接,并且指定了本地Socket(即客户端)所绑定的本地IP地址和端口号
(3)常用方法
>void connect(SocketAddress endpoint) :将该客户端socket与指定的服务器连接
>void connect(SocketAddress endpoint, int timeout) :将该客户端socket与指定的服务器连接,并指定时限
>void close() :关闭客户端socket
>InetAddress getInetAddress():返回服务器程序的IP地址
>int getPort():返回服务器程序的端口号
>InputStream getInputStream() :返回该客户端socket的一个输入流
>OutputStream getOutputStream() :返回该客户端socket的一个输出流

二、TCP协议的Server-Client模型
1.建立Server-Client模型
(1)服务器程序创建一个ServerSocket,然后调用accept方法等待客户来连接;
(2)客户端程序创建一个Socket并请求与服务器建立连接;
(3)服务器接收客户的连接请求,并创建一个新的Socket(属于服务器)与该客户建立专线连接;
(4)刚才建立了连接的两个Socket在一个单独的线程(由服务器程序创建)上对话;
(4)服务器开始等待新的连接请求。
Java笔记二十四.TCP网络编程_第1张图片
   
    服务器端程序调用ServerSocket.accept方法(返回服务器的Socket)等待客户的连接请求,一旦accept接收了客户连接请求,该方法将返回一个与该客户建立了专线连接的Socket对象,而不用去创建这个服务器Socket对象。当客户端和服务器端的两个Socket建立了专线连接后,连接的一端能向另一端连续写入字节,也从另一端连续读入字节,即建立了专线连接的两个Socket是以IO流的方式进行数据交换的。Java提供了Socket.getInputStream方法返回Socket的输入流对象,Socket.getOutputStream方法返回Socket的输出流对象。只要连接的一段向该输出流对象写入了数据,连接的另一端就能从其输入流对象中读取到这些数据。

三、源码实战
1.最简单的TCP服务器程序
实现:开发一个简单的TCP服务端应用,设定其端口号,并将Windows下的telnet工具作为客户端与之通信。
ServerTCP.java
<span style="font-size:18px;">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerClient {
 public static void main(String[] args)
 {
 
  try
  {
   //1.创建一个ServerSocket对象,用于返回服务器的Socket并指定服务器的端口号
   ServerSocket server = new ServerSocket(8000);
   Socket socket=server.accept();
   //2.获取专线Socket的输入输出流
   InputStream is = socket.getInputStream();
   OutputStream os = socket.getOutputStream();
   //3.向输出流写入字节数据
   String str = "My name is jiangdongguo.How are you!";
   os.write(str.getBytes());
   //4.从输入流读取数据并存储到指定的字节数组中
   BufferedReader br = new BufferedReader(new InputStreamReader(is));
   System.out.println(br.readLine());
//	 byte[] buf = new byte[1024];
//	 int len=is.read(buf);
//	 System.out.println(new String(buf,0,len));	//打印实际读到的数据
   socket.close();
   is.close();
   os.close();
   server.close();
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
}</span>
源码分析
(1)ServerSocket server = new ServerSocket(8000); 语句作用为服务端程序创建一个在8000端口上等待连接的ServerSocket对象,当接收到一个客户的连接请求后,程序从与这个客户建立了连接的Socket对象中获得输入输出流对象,通过输出流首先向客户端发送一串字符,然后通过输入流读取客户发送过来的信息,并将这些信息存放到一个字节数组中,最后关闭所有有关的资源。
(2)由于telnet工具有输入就发送,而不等回车。如有如果服务器向将接收到的数据按一行行的格式输出。Java为我们提供了一个BufferedReader类,通过该类实现按行处理输入流。
效果演示
Java笔记二十四.TCP网络编程_第2张图片
    从下面效果可知,服务器先将数据写入Socket的输出流。当Telnet与开发的服务器连接成功后,Telent作为客户端从该socket的输入流读取数据并在终端显示。另外,我们(telnet)客户端的命令终端写入数据到socket的输出流后,服务器程序会读取socket的输入流并打印到Eclipse终端。

2.Server_Client模型应用开发
(1)服务器端
功能:在上一个程序的基础上,实现服务器程序能够接收多个客户的连接请求,并为每个客户连接创建一个单独的线程与客户进行对话。
>ServiceThread.java
    子线程功能代码。每个连接的数据交换,需要放在一个循环语句中,保证两者可以不停地交换数据。客户端每向服务器发送一个字符串,服务器就将这个字符串中的所有字符反向排序后回送给客户端,直到客户端向服务器发送quit命令,结束两端的对话。
<span style="font-size:18px;">import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
//实现Runnable接口子类,完成子线程任务
public class ServiceThread implements Runnable
{
 Socket socket;
 
 /*构造方法,传递一个Socket对象参数*/
 public ServiceThread(Socket socket)
 {
  this.socket=socket;
 }
 
 /*成员方法*/
 public void run()
 {
  try
  {
   /*-----------------------------------------------------------------------------------------*/
   //1.获取Socket的输入流、输出流对象
   InputStream is =socket.getInputStream();
   OutputStream os=socket.getOutputStream();
   //2.为输入流、输出流创建两个包装类
   BufferedReader br = new BufferedReader(new InputStreamReader(is));	//输入流包装类
   DataOutputStream dos = new DataOutputStream(os);	 //输出流包装类
    /*-----------------------------------------------------------------------------------------*/
   //3.该线程不停的读取输入流中的数据并倒序
   while(true)
   {
    String str=br.readLine();	 //从输入流中读取一行字符串
    if(str.equalsIgnoreCase("quit"))
      break;
    String strReverse = (new StringBuffer(str).reverse().toString());	//将字符串倒序
    dos.writeBytes(str+"------>"+strReverse+System.getProperty("line.separator"));	//向输出流中写入字节数据
   }
   br.close();
   dos.close();
   socket.close();
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
}</span>
源码分析:
(1)BufferedReader、DataOutputStream类:这两个类为IO包装类,BufferedRead类可以方便得从底层字节输入流中以整行的形式读取一个字符串;DataOutputStream类可以将一个字符串以字节数组的形式写入底层字节输出流中
(2)System.getProperty("line.separator")用于根据不同的操作系统返回相应的换行符。
(3)在创建服务器程序Socket时需要指定其端口号(客户端才能知道哪个网络程序为服务器),然后,获取Socket的输入流、输出流用以与客户端进行数据交互。
-------------------------------------------------------------------------------------------------------------------------------------------------------
>TCPServer.java
    主线程。一次accept方法调用只接受一个连接,accept方法需要方法一个循环语句中,以便接收多个客户端连接。
<span style="font-size:18px;">import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
 public static void main(String[] args)
 {
  try
  {
   ServerSocket ss = new ServerSocket(8010);//实例化一个ServerSocket对象
   while(true)
   {
    Socket socket = ss.accept();                                 //等待客户端连接请求,成功后返回服务器的socket
    new Thread(new ServiceThread(socket)).start();	 //当连接成功,为socket通信创建一个线程并启动该线程
   }
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
}</span>
(2)客户端
>TCPClient.java
<span style="font-size:18px;">import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
 public static void main(String[] args)
 {
 
  try
  {
   //1.创建客户端socket并指定服务器IP和端口号
   Socket socket = new Socket(InetAddress.getByName("192.168.1.100"),8010);
   //2.获取socket的输入流、输出流
   InputStream is = socket.getInputStream();
   OutputStream os=socket.getOutputStream();
   //3.实例化两个包装类
   BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
   DataOutputStream dos=new DataOutputStream(os);
   BufferedReader bri = new BufferedReader(new InputStreamReader(is));
   while(true)
   {
    String strKey = br.readLine();	 //从输入流读一行数据
    dos.writeBytes(strKey+System.getProperty("line.separator"));	//将键盘数据写入输出流
    if(strKey.endsWith("quit"))	 //当输入quit时,断开连接
      break;
    else
     System.out.println(bri.readLine());//从输入流读取一行数据并打印
   }
   socket.close();
   br.close();
   dos.close();
   bri.close();
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
}</span>

源码分析
(1)Socket socket = new Socket(InetAddress.getByName("192.168.1.100"),8010); 语句的作用是创建客户端Socket并指定服务器的IP地址和端口号。但很多时候,为了使我们的应用程序灵活性更大,可以通过命令终端来输入服务器的IP地址和端口号:
Socket socket=null;
if(args.length<2)            //指定默认服务器IP和端口号
{
      socket = new Socket(InetAddress.getByName("192.168.1.100"),8010);
}
else                                //通过命令行终端指定
{
    socket=Socket((InetAddress.getByName(args[0]),Integer.parseInt(args[1]));
}
注:上述方法对服务器设置端口为一致思想。

效果演示1:
    这里使用Telnet作为客户端,观察下面结果我们可以看出,在命令行运行多个"Telnet 服务器IP 服务器端口号"后,客户端程序与服务端在一个单独的线程中建立连接,实现数据交换。
Java笔记二十四.TCP网络编程_第3张图片
效果演示2:
    这里使用我们编写好的应用作为客户端,每一个客户都可以同服务器单独对话,直到客户输入quit命令后结束。其效果与telnet一致。
Java笔记二十四.TCP网络编程_第4张图片

你可能感兴趣的:(java)