网络编程之基于TCP协议的网络编程



TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

1.TCP协议
TCP协议是英文Transmission Control Protocol的缩写,即传输控制协议。使用TCP协议进行传输时,TCP协议会在两个端点建立一个连接。所以被称为端到端协议,它是可靠的。
使用TCP传输,需要客户端和服务端。

TCP特点:面向连接的、可靠的。

2.客户端Socket
使用TCP通信的两端有客户端和服务端的区分。客户端通常可使用Socket来作为客户端。
构造器:
Socket(InetAddress address, int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。

客户端和服务端建立连接后,就有了对应的Socket流,所以不需要再创建流,只要获取流就可以进行通信了。
方法;
InputStream getInputStream():返回此套接字的输入流。
OutputStream getOutputStream():返回此套接字的输出流。

InetAddress getInetAddress():返回套接字连接的地址。

void shutdownOutput():用于在输出数据结束时写一个结束标识发送给客户端,告诉客户端通信结束,以便客户端停止接收。

3.服务端ServerSocket
在两个通信实体没有建立连接之前,必须有一个通信实体先做出“主动姿态”,主动接收来自其他通信实体的连接请求。
Java通过ServerSocket来表示服务端的Socket。ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。
ServerSocket有一个特点:本身没有流对象,在与一个客户端连接后,使用客户端的流对象,这样避免了不同客户端之间的通信冲突。
构造器:
ServerSocket(int port):创建绑定到特定端口的服务器套接字。

方法:
Socket accept():如果接收到一个客户端Socket的连接请求,该方法返回一个与客户端Socket对应的Socket;负责该方法将一直处于等待状态,线程也被阻塞。

4.步骤
4.1 客户端
1)建立客户端Socket。
2)获取Socket流的输出流用于发送数据。
3)使用输出流向服务端发送数据。
4)关闭资源。

4.2 服务端
1)建立服务端Socket——ServerSocket。
2)使用accept方法与客户端建立连接。
3)获取客户端的输入流用于接收数据。
4)使用输入流接收客户端数据。
5)关闭客户端。
6)关闭服务端,可选。

例1 下面程序简易介绍客户端向服务端发送数据:发送一句话
//客户端
public class TCPClient {

	public static void main(String[] args) throws Exception{

		//1.建立Socket
		Socket s = new Socket("192.168.0.102", 1000);
		//2.获取输出流
		OutputStream out = s.getOutputStream();
		//3.向输出流写入数据
		out.write("hello,server".getBytes());
		//4.获取输入流,接收服务器反馈数据
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf, 0, len));
		
		//5.关闭资源
		s.close();
		
	}

}

//服务端
public class TCPServer {

	public static void main(String[] args) throws Exception{
		
		//1.建立服务端Socket
		ServerSocket ss = new ServerSocket(1000);
		//2.获取客户端Socket
		Socket s = ss.accept();
		//3.获取输入流接收数据
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf, 0, len));
		//4.获取输出流
		OutputStream out = s.getOutputStream();
		out.write("hello,client".getBytes());
		//5.关闭客户端
		s.close();
	}

}


例2  客户端向服务端发送客户端的键盘录入数据。
//客户端
public class TCPClient {

	public static void main(String[] args) throws Exception{

		//创建客户端Socket
		Socket s = new Socket("192.168.0.102", 1000);
		//获取Socket流中的输出流,向服务端写入数据
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		//获取Socket流中的输入流,接收服务端数据
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		//获取键盘输入
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));		
		
		String line = null;
		while((line = bufr.readLine()) != null){
			if("over".equals(line))
				break;
			//向服务端写入数据
			out.println(line);
			//获取服务端的反馈数据
			String str = in.readLine();
			System.out.println("server:");
			System.out.println(str);
		}
		
		//关闭资源
		bufr.close();
		s.close();
		/*
		 * s.close()语句执行后,会在Socket流中加入-1,这时候服务端还在循环读取,读到-1,读取结束。
		 * 向下执行到ss.close,服务端也会结束。
		 * */
	}

}

//服务端
public class TCPServer {

	public static void main(String[] args) throws Exception{

		//建立服务端Socket
		ServerSocket ss = new ServerSocket(1000);
		//获取客户端Socket对象
		Socket s = ss.accept();
		//获取客户端ip
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip + "客户已连接");
		//获取输入流来获取客户端数据
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		//获取输出流来向客户端反馈数据
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		
		String line = null;
		while((line = in.readLine()) != null){
			System.out.println(line);
			out.println(line.toUpperCase());
		}
		
		//关闭资源
		s.close();
		ss.close();
	}

}


例3   服务端将客户端发送的文本文件保存在本地。
//客户端
public class TextClient {

	public static void main(String[] args) throws Exception{

		//建立客户端Socket
		Socket s = new Socket("192.168.0.102", 1000);
		//读取本地文本文件
		BufferedReader bufr = new BufferedReader(new FileReader("d:\\demo.txt"));
		//获取Socket流中输出流并包装
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		
		String line = null;
		while((line = bufr.readLine()) != null){
			out.println(line);
		}
		//读取文件遇到文件末尾可以结束循环
		//但是服务端不会自动结束循环,所以要加上标记告诉服务端上传结束
		s.shutdownOutput();
		
		//获取Socket流的输入流以便获取服务端发聩信息
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String info = in.readLine();
		System.out.println(info);
		
		//关闭资源
		bufr.close();
		s.close();
		
	}

}

//服务端
public class TextServer {

	public static void main(String[] args) throws Exception{

		//建立服务端Socket
		ServerSocket ss = new ServerSocket(1000);
		//获取客户端Socket建立连接
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip + "---客户已连接");
		//获取输入流并包装
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		//保存到本地
		PrintWriter pw = new PrintWriter(new FileWriter("demo.txt"));
		String line = null;
		while((line = in.readLine()) != null){
			pw.println(line);
		}
		
		//上次完毕后向客户端反馈
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		out.println("上传完毕");
		
		//关闭资源
		pw.close();
		s.close();
		ss.close();
		
	}

}


例4  使用多线程,多个客户端向服务端发送图片,将图片保存在服务端。
//客户端
public class PicClient {

	public static void main(String[] args) throws Exception{

		//将文件路径作为参数在运行程序时输入
		//如果传入参数不等于1,传入错误
		if(args.length != 1){
			System.out.println("请写入正确的文件路径");
		}
		File file = new File(args[0]);
		
		//如果文件不存在或者不是文件,提示错误
		if(!(file.exists() && file.isFile())){
			System.out.println("不是文件或文件不存在");
			return;
		}
		
		//如果不是图片文件,提示错误
		if(!file.getName().endsWith(".png")){
			System.out.println("不是png格式的图片,请从新选择");
			return;
		}
		
		//如果文件大于5M,提示错误
		if(file.length() > 1024*1024*5){
			System.out.println("文件过大");
			return;
		}
		
		//建立客户端Socket
		Socket s = new Socket("192.168.0.102", 1000);
		//读取图片文件
		FileInputStream fis = new FileInputStream(file);
		//获取网络流的输出流上传文件
		OutputStream out = s.getOutputStream();
		byte[] buf = new byte[1024];
		int len = 0;
		while((len = fis.read(buf)) != -1){
			out.write(buf, 0, len);
		}
		
		//给服务端一个结束标识使服务端停止接收
		s.shutdownOutput();
		
		//获取网络流的输入流以便接收服务端反馈信息
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = in.readLine();
		System.out.println(line);
		
		//关闭资源
		fis.close();
		s.close();
	}

}

//服务端的线程类,每个线程类负责处理一个客户端请求
public class PicThread implements Runnable{

	//客户端
	private Socket s;
	
	public PicThread(Socket s){
		this.s = s;
	}
	
	public void run(){
		
		//计数器,用于记录文件名的编号
		int count = 1;
		String ip = s.getInetAddress().getHostAddress();
		FileOutputStream fos = null;
		try{
			System.out.println(ip + "已连接");
			//以客户端ip地址为图片命名,如果该名称已存在,那么加上编号
			File file = new File(ip + ".png");
			while(file.exists()){
				file = new File(ip + "(" + (count++) + ").png");
			}
			
			//获取网络流中的输入流
			InputStream in = s.getInputStream();
			//向本地写数据
			fos = new FileOutputStream(file);
			byte[] buf = new byte[1024];
			int len = 0;
			while((len = in.read(buf)) != -1){
				fos.write(buf, 0, len);
			}
			
			//向客户端反馈信息
			PrintWriter out = new PrintWriter(s.getOutputStream(), true);
			out.println("上传成功");
			
		}catch(Exception e){
			throw new RuntimeException(ip + "上传失败");
		}finally{
			//关闭资源
			try{
				if(fos != null)
					fos.close();
			}catch(IOException e){
				e.printStackTrace();
			}
			
			try{
				s.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
	}
}

//服务端
public class PicServer2 {

	public static void main(String[] args) throws Exception{

		//服务端
		ServerSocket ss = new ServerSocket(1000);
		
		while(true){
			//连接客户端
			Socket s = ss.accept();
			new Thread(new PicThread(s)).start();
		}
	}

}


例5  

客户端使用键盘录入用户名。服务端进行校验。

如果存在,服务端显示**已登录,客户端显示欢迎

不存在,服务端显示**尝试登录,客户端显示用户名不存在

最多登录三次。

//客户端
public class UserClient {

	public static void main(String[] args) throws Exception{

		//客户端
		Socket s = new Socket("192.168.0.102", 1000);
		//获取键盘录入
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		//获取网络流的输出流
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		//获取服务端反馈的信息
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		
		//循环三次
		for(int i = 0; i < 3; i++){
			//获取键盘录入的用户名
			String user = bufr.readLine();
			//如果用户名为空,退出
			if(user == null)
				break;
			//将用户名发送到服务端
			out.println(user);
			
			//获取服务端的反馈
			String info = in.readLine();
			System.out.println(info);
			//如果反馈信息包含“成功”,说明登录成功,退出登录
			if(info.contains("成功"))
				break;
		}
		
		//关闭资源
		bufr.close();
		s.close();
	}

}

//用户线程类,用来处理每个客户端的请求
public class UserThread implements Runnable{

	private Socket s;
	public UserThread(Socket s){
		this.s = s;
	}
	
	public void run(){
		//获取客户端ip
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip + "已连接");
		try{
			//循环三次,判断用户名是否存在
			for(int i = 0; i < 3; i++){
				//获取客户端输入的用户名
				BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
				String user = in.readLine();
				//如果用户名为空,退出
				if(user == null)
					break;
				
				//读取本地数据库
				BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));
				String name = null;
				//输出流反馈信息
				PrintWriter out = new PrintWriter(s.getOutputStream(), true);
				
				//定义标记
				boolean flag = false;
												
				//遍历数据库比对用户名
				while((name = bufr.readLine()) != null){
					//如果用户名存在,将标记置为真,退出判断
					if(name.equals(user)){
						flag = true;
						break;
					}
				}
				
				//判断标记
				//如果标记为真,那么用户名存在
				if(flag){
					//服务端显示用户已登录
					System.out.println(user + "已经登录");
					//向客户端反馈登录成功
					out.println(user + "登陆成功");
					//结束三次循环判断
					break;
				}else{
					//否则,用户名不存在
					System.out.println(user + "尝试登录");
					out.println("用户名不存在");
				}
			}
			
			//关闭资源
			s.close();			
		}catch(Exception e){
			throw new RuntimeException(ip + "登录异常");
		}
	}
}

//服务端
public class UserServer {

	public static void main(String[] args) throws Exception{

		//服务端
		ServerSocket ss = new ServerSocket(1000);
		
		while(true){
			
			//和客户端连接
			Socket s = ss.accept();
			
			new Thread(new UserThread(s)).start();
		}
	}

}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值