网络编程之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();
		}
	}
}


原文地址:https://www.cnblogs.com/LinkinPark/p/5233093.html