网络编程中“TCP”编程——简单通信

前言

        计算机网络是指两台或多台计算机组成的网络,在同一个网络中,任意两台计算机都可以直接通信,因此所有计算机必须遵循同一种网络协议

常用协议

TCP/IP协议(TCP协议和IP协议):

        TCP协议时一种面向连接,可靠传输的协议;

        IP协议是一种分组交换传输的协议;

UDP协议:

        UDP协议是一种无连接,不可靠传输的协议;

基于TCP/IP协议的TCP编程        

         在学习TCP编程时,我们最先要了解的就是Socket,那什么是Socket呢?在开发网络应用程序的时候一定会遇到Socket这个概念,Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络;

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

        当Socket连接成功在服务器端和客户端之间建立之后:

  对于服务器端来说,他是Socket指定的IP地址和指定的端口号;

  对于客户端来说,他的Socket是它所在计算机IP地址和一个由操作系统分配的随机端口号;

服务器端:

要使用TCP编程,我们首先要编写服务器端程序,Java标准库提供了java.net.ServerSocket来实现对指定IP和指定端口的监听,ServerSocket的构造方法如下:

ServerSocket server = new ServerSocket(5237);

5237:自定义端口号;这里我们没有指定IP地址,表示在计算机的所有网络接口上进行监听。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo04 {
	public static void main(String[] args) {
		//ServerSocket:服务器端进行通信的对象
		try (ServerSocket server = new ServerSocket(5237)) {
			//死循环:不断接受客户端的连接
			while (true) {
				//服务器进入“等待”状态
				//如果有客户端连接,该方法返回客户的Socket
				Socket client=server.accept();
				InetAddress clientAdress=client.getInetAddress();
				System.out.println("客户端"+clientAdress.getHostAddress()+"开始连接......");
				//接受来自客户端上传的图片
				//输入流:读取来自客户端发送的图片文件流
				//输出端:写入本地图片
				String imgName=clientAdress.getHostName()+".jpg";
				try(InputStream in=client.getInputStream();
						BufferedInputStream bis=new BufferedInputStream(in);
						BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("D:\\琐碎\\ServerImg\\"+imgName))){	
					//每次读取来自客户端的图片文件流
					//写入本地
					byte[] buff=new byte[1024];
					int len=-1;
					while((len=bis.read(buff))!=-1) {
						bos.write(buff, 0, len);
					}
					System.out.println("图片读取结束");
					//输出提示信息=>客户端
					try(BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));){
						writer.write("upLoad Success!!!");
						writer.newLine();
					}
					
				}
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

         如果ServerSocket监听成功,我们就使用一个死循环来处理客户端的连接,以上代码中server.accept()表示每当有新的客户端连接,就返回一个Socket实例,这个Socket对象就是用来和刚连接的客户端进行通信的;

        如果没有客户端连接进来,accept()方法就会阻塞并一直等待。如果有多个客户端同时连接进来,ServerSocket会把连接添加到队列里,然后逐个进行处理。对于Java程序而言,只需要通过循环不断调用accept()就可以连续获取新的连接。

客户端:

相比服务器端,客户端程序就要简单很多。一个典型的客户端程序如下:

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class Demo03 {
	public static void main(String[] args) {
		//客户端
		//Socket:客户端进行通信的组件
		//本地图片读取 => 通过输出流(发送)至服务器
		//OutputStream:输出流,将读取到的本地图片文件流。发送(输出)至服务器
		//BufferedInputStream:输入流,读取本地图片
		try (Socket socket = new Socket("192.168.254.***",5237);
				OutputStream out=socket.getOutputStream();
				BufferedInputStream in=new BufferedInputStream(
						new FileInputStream("D:\\琐碎\\20220220101644.jpg"));) {
			//每次读取1024个字节
			byte[] buff=new byte[1024];
			int len=-1;
			while((len=in.read(buff))!=-1) {
				//将读取到的内容,通过输出流发送至服务器
				out.write(buff);
			}
			//“输出”暂时结束(Socket没有关闭)
			socket.shutdownOutput();
			
			//读取来自服务器的反馈
			try(BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()))){
				String s=reader.readLine();
				System.out.println(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

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

        写入网络数据时,必须要调用flush()方法。如果不调用flush(),就会出现客户端和服务器都收不到数据的情况,这并不是Java标准库的设计问题,而是我们以流的形式写入数据的时候,并不是一写入就立刻发送到网络,而是先写入内存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,这样设计的目的是为了提高传输效率。如果缓冲区的数据很少,而我们又想强制把这些数据发送到网络,就必须调用flush()强制把缓冲区数据发送出去

.

 

 

 

你可能感兴趣的:(java,eclipse)