zoukankan      html  css  js  c++  java
  • 网络编程之TCP编程

    • 网络编程之TCP编程

    前面已经介绍过关于TCP协议的东西,这里不做赘述。Java对于基于TCP协议的网络通信提供了良好的封装,Java使用socket对象来代表两端的通信窗口,并通过Socket产生IO流来进行网络通信。说白了,玩基于Socket的网络编程无非就是使用ServerSocket创建一个服务器,然后使用socket进行通信,如果复杂一点呢,在加入多线程操作,用NIO代替IO来实现非阻塞Socket通信。


    在很久以前我就听过一个前辈讲过,他说这个Socket翻译的很是别扭,叫做什么套接字,具体的不管了,反正他就叫这名字。那么关于这个套接字,要如何理解呢?一个Socket相当于一个电话机。OutputStream(输出)相当于话筒,InputStream(输入)相当于听筒。


    服务器端要创建的对象:java.Net.ServerSocket。那么如何创建一个TCP服务器端程序呢?
           1). 创建一个ServerSocket
           2). 从ServerSocket接受客户连接请求
           3). 创建一个服务线程处理新的连接
           4). 在服务线程中,从socket中获得I/O流
           5). 对I/O流进行读写操作,完成与客户的交互
           6). 关闭I/O流
           7). 关闭Socket
    客户端要创建的对象:java.Net.Socket。那么如何创建一个TCP客户端程序呢?
          1). 创建Socket
          2). 获得I/O流
          3). 对I/O流进行读写操作
          4). 关闭I/O流
          5). 关闭Socket


          以下代码实现了一个服务器端和一个客户端:

    import java.net.*;
    import java.io.*;
    
    public class Server
    {
    	public static void main(String[] args) throws IOException
    	{
    		// 创建一个ServerSocket,用于监听客户端Socket的连接请求
    		ServerSocket ss = new ServerSocket(30000);
    		// 采用循环不断接受来自客户端的请求
    		while (true)
    		{
    			// 每当接受到客户端Socket的请求,服务器端也对应产生一个Socket
    			Socket s = ss.accept();
    			// 将Socket对应的输出流包装成PrintStream
    			PrintStream ps = new PrintStream(s.getOutputStream());
    			// 进行普通IO操作
    			ps.println("您好,您收到了服务器的新年祝福!");
    			// 关闭输出流,关闭Socket
    			ps.close();
    			s.close();
    		}
    	}
    }
    

    import java.net.*;
    import java.io.*;
    
    public class Client
    {
    	public static void main(String[] args) throws IOException
    	{
    		Socket socket = new Socket("127.0.0.1", 30000);
    		// 将Socket对应的输入流包装成BufferedReader
    		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    		// 进行普通IO操作
    		String line = br.readLine();
    		System.out.println("来自服务器的数据:" + line);
    		// 关闭输入流、socket
    		br.close();
    		socket.close();
    	}
    }
    

    上面的代码比较简单,当然我们可以加入多线程,这样子更加符合实际应用,因为前面的单线程很容易在IO流做读取操作的时候发生阻塞。下面的代码,服务器为每个socket单独启动一个线程,每条线程负责和一个客户端进行通信,客户端同样单独启动一个线程,该线程专门负责读取服务器数据。具体代码如下:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    
    public class MyServer
    {
    	//定义保存所有Socket的ArrayList
    	public static ArrayList<Socket> socketList = new ArrayList<Socket>();
    
    	public static void main(String[] args) throws IOException
    	{
    		ServerSocket ss = new ServerSocket(30000);
    		while (true)
    		{
    			// 此行代码会阻塞,将一直等待别人的连接
    			Socket s = ss.accept();
    			socketList.add(s);
    			// 每当客户端连接后启动一条ServerThread线程为该客户端服务
    			new Thread(new ServerThread(s)).start();
    		}
    	}
    }
    
    class ServerThread implements Runnable
    {
    	// 定义当前线程所处理的Socket
    	Socket s = null;
    	// 该线程所处理的Socket所对应的输入流
    	BufferedReader br = null;
    
    	public ServerThread(Socket s) throws IOException
    	{
    		this.s = s;
    		// 初始化该Socket对应的输入流
    		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    	}
    
    	public void run()
    	{
    		try
    		{
    			String content = null;
    			// 采用循环不断从Socket中读取客户端发送过来的数据
    			while ((content = readFromClient()) != null)
    			{
    				// 遍历socketList中的每个Socket,
    				// 将读到的内容向每个Socket发送一次
    				for (Socket s : MyServer.socketList)
    				{
    					PrintStream ps = new PrintStream(s.getOutputStream());
    					System.out.println("这里是服务器。。。");
    					System.out.println(content);
    					ps.println(content);
    				}
    			}
    		}
    		catch (Exception e)
    		{
    			e.printStackTrace();
    		}
    	}
    
    	// 定义读取客户端数据的方法
    	private String readFromClient()
    	{
    		String str = null;
    		try
    		{
    			str = br.readLine();
    		}
    		// 如果捕捉到异常,表明该Socket对应的客户端已经关闭
    		catch (Exception e)
    		{
    			// 删除该Socket。
    			MyServer.socketList.remove(s);
    		}
    		return str;
    	}
    }
    

    import java.io.*;
    import java.net.*;
    
    public class MyClient
    {
    	public static void main(String[] args) throws Exception
    	{
    		Socket s = new Socket("127.0.0.1", 30000);
    		// 客户端启动ClientThread线程不断读取来自服务器的数据
    		new Thread(new ClientThread(s)).start(); 
    		// 获取该Socket对应的输出流
    		PrintStream ps = new PrintStream(s.getOutputStream());
    		String line = null;
    		// 不断读取键盘输入
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		while ((line = br.readLine()) != null)
    		{
    			// 将用户的键盘输入内容写入Socket对应的输出流
    			ps.println(line);
    		}
    	}
    }
    
    class ClientThread implements Runnable
    {
    	// 该线程负责处理的Socket
    	private Socket s;
    	// 该线程所处理的Socket所对应的输入流
    	BufferedReader br = null;
    
    	public ClientThread(Socket s) throws IOException
    	{
    		this.s = s;
    		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    	}
    
    	public void run()
    	{
    		try
    		{
    			String content = null;
    			// 不断读取Socket输入流中的内容,并将这些内容打印输出
    			while ((content = br.readLine()) != null)
    			{
    				System.out.println("这里是客户端。。。");
    				System.out.println(content);
    			}
    		}
    		catch (Exception e)
    		{
    			e.printStackTrace();
    		}
    	}
    }
    


  • 相关阅读:
    jQuery的遍历方法
    xampp配置host和httpd可以随意访问任何本机的地址
    JavaScript的this简单实用
    移动端rem布局和百分比栅格化布局
    你知道用AngularJs怎么定义指令吗?
    谈谈Angular关于$watch,$apply 以及 $digest的工作原理
    深入了解Angularjs指令中的ngModel
    如何将angularJs项目与requireJs集成
    requireJS(二)
    requireJS(一)
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5233093.html
Copyright © 2011-2022 走看看