网络编程——TCP编程

一.概述
计算机网络是指两台或更多计算机组成的网络,在同一网络中,任意两台计算机都可以直接通信,因为所有计算机都需要遵循同一协议。
互联网是网络的网络,即把很多计算机网络连接起来,形成一个全球统的网络。如果计算机网络各自的通讯协议不同,就无法将两个不同的网络连接起来形成互联网。因此,为了把计算机网络接入互联网,就必须使用TCP/IP协议。只有使用TCP/IP地计算机才能联入互联网,使用其他网络协议是无法联入互联网的。

定义: IP协议是一个分组交换协议,他不保证可靠运输,而TCP协议是传输控制协议,它是面向连接的协议,支持可靠传输和双向通信。TCP协议是建立在IP协议之上。

IP协议值负责发数据包,不保证顺序和正确性,而TCP协议复制控制数据包传输:在传输数据之前还需要建立连接,建立连接之后才能传输数据,传输完后还需要断开连接。

TCP之所以能够保证数据的可靠传输,是通过接受确认,超时重传这些机制实现的。而且TCP协议允许双向通信,即通信双方可同时发送和接受数据。

二.TCP协议

1.什么是Socket?

Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过一个TCP/IP协议把数据传输到网络。
Java提供的几个Socket相关的类封装了操作系统提供的接口:ServerSocket类,Socket类。

> 网络通信的过程:使用Socket进行网络编程时,本质就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程充当客户端,它必须主动连接客户端的IP地址和指定端口,如果连接成功,服务器端何客户端就成功建立了连接,双方就可以随时发送和接收数据。

当Socket成功建立起服务器端和客户端的连接后:
·对服务器端来说,它的Socket是指定的IP地址和指定的端口号;
·对客户端来说。它的Socket是它所在计算机的一个IP地址和一个由操作系统分配的随机端口号。

2. 服务器端

Java提供了ServerSocket来实现对指定IP和指定端口的监听。

  ServerSocket ss = new ServerSocket(6666);

过程:
如果ServerSocket监听成功,我们就使用一个无限循环来不断接收客户端的连接,ss.accept()表示每当有新的用户端连接,就会返回一个Socket实例,这个Socket实例就是用来和刚连接的客户端进行通信的。
如果没有客户端连接进来,accept()方法就会阻塞并一直等待。如果有多个客户端同时连接进来,ServerSocket会把连接扔到队列里,一个一个处理。对于Java程序而言,只需要通过while()循环不断调用 ServerSocket 的accept()就可以获取新的连接。

public class Server {
    public static void main(String[] args) throws IOException {
        // 监听指定端口
        ServerSocket ss = new ServerSocket(6666);
        System.out.println("server is running...");

        //  无限循环:不断接受客户端的连接
        while (true) {
            // 服务器进入等待状态
		   	// 当有客户端连接时,该方法返回客户端的Socket
            Socket sock = ss.accept();
            
            // 使用Socket流进行网络通信
            // ...
            System.out.println("connected from " + sock.getRemoteSocketAddress());
        }
    }
}

3.客户端

客户端通过 Socket sock = new Socket(“localhost”, 6666); 连接到指定服务器和端口。如果连接成功,将返回一个Soocket实例,用于后续通信。

public class Client {
    public static void main(String[] args) throws IOException {
        // 连接指定服务器和端口
        Socket sock = new Socket("localhost", 6666); 
        
       // 使用Socket流进行网络通信
       // ...
        
        // 关闭
        sock.close();
        System.out.println("disconnected.");
    }
}

4.Socket流
当Socket连接创建成功后,无论是服务器端还是客户端,我们都使用Socket实例进行网络通信。TCP是一种基于流的协议,因此使用InputStreanm和OutputStream来封装Socket的数据流,和普通IO流类似:

// 用于读取网络数据:
InputStream in = sock.getInputStream();

// 用于写入网络数据:
// 本地图片读取 ==> 通过输出流发送至服务器
// OutputStream:将读取到的本地图片流,发送至服务器
OutputStream out = sock.getOutputStream();

注意:写入网络时,必须调用flush()方法。如果不调用flush(),客户端和服务器都收不到数据,因为我们以流的形式写入数据时,并不是一写入就立刻发送到网络,而是先存入内存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,可提高传输效率。如果缓冲区的数据很少,而我们又想强制把这些数据发送到网络,就必须调用flush()强制把缓冲区数据发送出去。
5.这是一个模拟对话的客户端和服务器端交流的代码的实现片段;

public class ChatClient {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		// 客户端
		while(true) {
		// 创建Socket,连接服务器
		try (
		    Socket client = new Socket("172.20.10.2",9966);
				BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
				BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
				) {
		    
			 // 获取控制台的输入
			 String question = input.nextLine();
			 if(question.equals("over")) {
				 break;
			 }
		
			 // 发送问题至服务器
		     writer.write(question);
		     writer.flush();
		
		     // 暂时结束本次输出
		    client.shutdownOutput();
		
			// 获取来自服务器的答案
			String answer = reader.readLine();
			System.out.println("【客户端】来自服务器的答案:" + answer);		
		} catch (IOException e) {
			e.printStackTrace();
		}
		}	
		System.out.println("Game over");
	}
}
public class ChatServer {
	public static void main(String[] args) {
		
		Map<String, String> chatMap = new HashMap<String, String>(){
			{
				put("你好", "你好呀");
				put("hi", "hi~");
				put("hello", "哈喽");
				put("吃了吗", "没呢,你呢");
				put("孤勇者", "爱你孤身走暗巷");
				put("有请潘周聃", "潘周聃,今年29岁,苏黎世理工大学.....");
				put("很高兴认识你", "我也是哦");
			}
		};

		// 服务器端
		try (ServerSocket server = new ServerSocket(9966)) {
			
			while(true) {
				// 发生客户端连接
				Socket client = server.accept();
				
				// 获取该客户端的ip地址
				String ipName = client.getInetAddress().getHostAddress();
				//System.out.println("客户端" + ipName + "开始连接...");
				
				try(BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
				BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));){
				
				// 获取该客户端的提问
				String question = reader.readLine();
				System.out.println("【服务器】来自客户端" + ipName + "的提问:" + question);
				
				// 获取该问题的答案
				String answer = chatMap .get(question);
				answer = answer == null ? "我不知道你在说什么" : answer;
				
				// 将答案发送至客户端
				writer.write(answer);
				writer.flush();
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
}

【运行结果】
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值