Java之------socket系列(四)TCP

TCP传输

TCP Socket:IP地址和端口,套接字

Socket和ServerSocket

建立客户端和服务器端

建立连接后,通过Socket中的IO流进行数据的传输

关闭socket

同样,客户端与服务器端是两个独立的应用程序。

TCP传输编程

☆基本思路(客户端)

客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。

连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(), getOutputStream()获取即可。

与服务端通讯结束后,关闭Socket。

☆基本思路(服务器端)

服务端需要明确它要处理的数据是从哪个端口进入的。

当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。

当该客户端访问结束,关闭该客户端。

基于TCP的Socket通信流程

TCP传输编程

☆客户端

通过Socket建立对象并指定要连接的服务端主机以及端口。

Socket s = new Socket(“192.168.1.1”,9999);
OutputStream out = s.getOutputStream();
out.write(“hello”.getBytes());
s.close();

☆服务器端

建立服务端需要监听一个端口

ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();

具体实例:

客户端:

package cn.hncu.tcp;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class MyClientSocket {

	public static void main(String[] args) {
		try {
			Socket s = new Socket("192.168.31.168",8888);//和服务器进行三次握手,若失败则出异常,否则返回和对方通讯的socket
			//发送数据
			OutputStream out = s.getOutputStream();
			out.write( "Hello Server!".getBytes() );

			//接收服务器的应答数据
			InputStream in = s.getInputStream();
			DataInputStream din = new DataInputStream(in);
			System.out.println( din.readUTF() );
			s.close();
			din.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

服务器端:

package cn.hncu.tcp;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerSocket {
	public static void main(String[] args) {
		try {
			ServerSocket server = new ServerSocket(8888);
			while(true){
				System.out.println("准备接收一个连接....");
				Socket s = server.accept();//阻塞式方法
				System.out.println("收到一个连接....");

				//读--从客户端读数据
				InputStream in = s.getInputStream();
				byte buf[] = new byte[20];
				in.read(buf);
				System.out.println("read info:"+ new String(buf) );

				//写--应答客户端--向他写数据
				OutputStream out = s.getOutputStream();
				DataOutputStream dout = new DataOutputStream(out);
				dout.writeUTF("Hello,"+s.getInetAddress()+"port#"+s.getPort()+"\nbye!");
				dout.close();
				s.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

☆TCP传输最容易出现的问题

客户端连接上服务端,两端都在等待,没有任何数据传输。

通过例程分析:

因为read方法或者readLine方法是阻塞式。

解决办法:

自定义结束标记

使用shutdownInput,shutdownOutput方法。

编程练习

1、上传文本文件(本例只考虑只有一个客户端)

读取一个本地文本文件,将数据发送到服务端,服务器端对数据进行存储。 存储完毕后,给客户端一个提示。

客户端:

package cn.hncu.tcp.upload;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class UploadTextClient {

	public static void main(String[] args) {
		try {
			Socket s = new Socket("192.168.1.106",10005);
			//思路:把本地文件的数据读取出来通过 s.getOutputStream()获得的out对象发送出去
			BufferedReader br = new BufferedReader(new FileReader("tempfiles\\client.txt"));

			OutputStream out = s.getOutputStream();//这里的输出流 对应的是服务器端的输入流
			PrintWriter pw = new PrintWriter(out, true);//建议不要用BufferedWriter
			String line = null;
			while( (line=br.readLine())!=null){
				pw.println(line);
			}
			//给服务器发送结束标记---上传结束,要加结束标记,否则服务器在数据接收完毕时再调用read()或readLine()时会出异常
			//法1:pw.println("over#[email protected]#@$");//不能出现文件中存在的结束关键字---搞特殊一点
			//法2---建议采用该种方式---由socket内部来指定结束标记
			s.shutdownOutput();
			br.close();

			//读取服务器上传成功的反馈消息
			BufferedReader br2 = new BufferedReader(new InputStreamReader(s.getInputStream()));
			String info = br2.readLine();
			System.out.println("server应答:"+info);
			s.close();
			br2.close();

		}catch (IOException e) {
			e.printStackTrace();
		}

	}

}

服务器端:

package cn.hncu.tcp.upload;

import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadTextServer {
	public static void main(String[] args) {
		try {
			ServerSocket server = new ServerSocket(10005);
			Socket s = server.accept();
			System.out.println(s.getInetAddress().getHostAddress()+"...connected");

			//读取客户端上传过来的文本文件
			//源 ---socket(字节流)---额外:需要转换成字符流  ,缓存流
			BufferedReader br = new BufferedReader( new InputStreamReader(s.getInputStream()));
			//目的 ---硬盘字符流 FileWriter---额外:打印流
			PrintWriter pw = new PrintWriter(new FileWriter("tempfiles\\server.txt"), true);
			String line=null;
			while ( (line=br.readLine())!=null ){
//				if("over#[email protected]#@$".equals(line)){
//					break;
//				}
				pw.println(line);
			}
			pw.close();
			//上传成功,给客户端一个提示信息
			PrintWriter pw2 = new PrintWriter(s.getOutputStream(),true);
			pw2.println("上传成功!");
			s.close();
			server.close();
			pw2.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}

2、上传图片文件(本例考虑有多个客户端)

客户端需求:把一个图片文件发送到服务端并读取回馈信息。要求判断文件是否存在及格式是否为jpg或gif并要求文件小于2M。

服务端需求:接收客户端发送过来的图片数据。进行存储后,回馈一个 上传成功字样。支持多用户的并发访问。

客户端:

package cn.hncu.tcp.upload;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class UploadPicClient {

	public static void main(String[] args) {

		//卫条件
		if(args.length!=1){
			System.out.println("请指定文件");
			return;
		}
		File file = new File(args[0]);
		if(!(file.exists() && file.isFile()) ){
			System.out.println("上传的文件不存在");
			return;
		}
		if( !(file.getName().endsWith(".jpg")||file.getName().endsWith(".gif"))  ){
			System.out.println("文件扩展名必须是jpg或gif");
			return;
		}
		if( file.length()>=1024*1024*2){
			System.out.println("文件过大,不应超过2M,请重新上传!");
			return;
		}

		//上传
		try {
			Socket s = new Socket("127.0.0.1",9999);
			FileInputStream fin = new FileInputStream(file);
			OutputStream out = s.getOutputStream();
			byte buf[] = new byte[1024];
			int len=0;
			while( (len=fin.read(buf))!=-1){
				out.write(buf, 0, len);
			}
			s.shutdownOutput();//告诉服务器,文件上传完毕

			//读取服务器的回馈信息
			InputStream in = s.getInputStream();
			byte buf2[] = new byte[1024];
			int len2 = in.read(buf2);
			String info = new String(buf2,0,len2);
			System.out.println(info);

			//关流
			fin.close();
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

服务器端:

package cn.hncu.tcp.upload;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadPicServer {
	public static void main(String[] args) {
		try {
			ServerSocket server = new ServerSocket(9999);
			while(true){
				Socket s = server.accept();//只负责和客户端进行握手
				new Thread( new UploadThread(s)).start();
			}
			//server.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

class UploadThread implements Runnable{
	private Socket s;
	public UploadThread(Socket s) {
		this.s = s;
	}

	@Override
	public void run() {
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"...connected");

		try {
			InputStream in = s.getInputStream();
			File dir = new File("d:\\mypic");
			if(!dir.exists()){
				dir.mkdir();
			}
			int count=1;
			File file = new File(dir,ip+".jpg");
			while(file.exists()){
				file = new File(dir,ip+"("+(count++) +")"+".jpg"); //带号的文件名
			}
			FileOutputStream fout = new FileOutputStream(file);

			//从socket流中读取数据,存储到本地文件。相当于对拷
			byte buf[] = new byte[1024];
			int len=0;
			while( (len=in.read(buf))!=-1){
				fout.write(buf, 0, len);
			}
			//图片接收完毕

			//向客户端发送回馈信息
			OutputStream out = s.getOutputStream();
			out.write( "上传成功".getBytes() );
			fout.close();
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
时间: 2024-10-09 15:05:09

Java之------socket系列(四)TCP的相关文章

Java笔记二十四.TCP网络编程

 TCP网络编程 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 从上面一节内容可以知道,利用UDP通信的两个程序是平等的,无主次之分,两个程序代码可以完全一样.但利用TCP协议进行通信的两个应用程序,是有主从之分的,一个称为服务器程序,另外一个称为客户机程序.Java中提供了ServerSocket类用于创建服务器端的socket,Socket类用于创建客户端socket. 一.APIs简介 java.net.ServerSocket (1

Java之------socket系列(一)

网络编程 相关基础概念: 1.计算机网络与Internet 2.TCP/IP协议 3.Internet地址 IP地址,形如xxx.xxx.xxx.xxx 域名系统.例如www.edu.cn URL(统一资源定位符) 协议 :// 主机 [: 端口] [/ 文件] [# 引用] 客户-服务器(Client-Server)模式 网络模型与通讯要素: 网络模型 OSI参考模型 TCP/IP参考模型 网络通讯要素 IP地址 端口号 传输协议 ☆七层简述: 1.物理层:主要定义物理设备标准,如网线的接口类

Java之------socket系列(二)UDP

☆ UDP: 将数据及源和目的封装成数据包中,不需要建立连接 每个数据报的大小在限制在64k内 因无连接,是不可靠协议 不需要建立连接,速度快 DatagramSocket和 DatagramPacket类 UDP传输: DatagramSocket与DatagramPacket 建立发送端,接收端. 建立数据包. 调用Socket的发送接收方法. 关闭Socket. 发送端与接收端是两个独立的运行程序. UDP传输编程: ☆发送端 在发送端,要在数据包对象中明确目的地IP及端口. Datagr

Java设计模式菜鸟系列(四)工厂方法模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39760895 工厂方法模式(Factory Method) 工厂方法:顾名思义,就是调用工厂里的方法来生产对象(产品)的. 工厂方法实现方式有3种: 一.普通工厂模式.就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 1.uml建模图: 2.代码实现 /** * 示例(一):普通工厂方法 * * 缺点:如果传递的字符串出错,则不能正确创建对象 */ interface Sen

深入理解JAVA I/O系列四:RandomAccessFile

一.简述 这个是JDK上的截图,我们可以看到它的父类是Object,没有继承字节流.字符流家族中任何一个类.并且它实现了DataInput.DataOutput这两个接口,也就意味着这个类既可以读也可以写. 二.存在的意义 1.是JAVA I/O流体系中功能最丰富的文件内容访问类,它提供了众多方法来访问文件内容. 2.由于可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,RandomAccessFile将是更好的选择. 3.可以用来访问保存数据记录的文件,文件的记录的大小不必相同,但是

Java之------socket系列(三)

练习:UDP聊天程序 通过键盘录入获取要发送的信息. 将发送和接收分别封装到两个线程中. 聊天方一: <span style="font-size:14px;">package cn.hncu.url.udp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.

java网络socket编程详解

7.2 面向套接字编程    我们已经通过了解Socket的接口,知其所以然,下面我们就将通过具体的案例,来熟悉Socket的具体工作方式 7.2.1使用套接字实现基于TCP协议的服务器和客户机程序    依据TCP协议,在C/S架构的通讯过程中,客户端和服务器的Socket动作如下: 客户端: 1.用服务器的IP地址和端口号实例化Socket对象. 2.调用connect方法,连接到服务器上. 3.将发送到服务器的IO流填充到IO对象里,比如BufferedReader/PrintWriter

Java设计模式菜鸟系列总结及博客全目录

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/40031567 今天来对这23种设计模式做个总结.咱使用设计模式的目的是为了可重用代码.让代码更容易被他人理解.保证代码可靠性,当然设计模式并不是万能的,项目中的实际问题还有具体分析.咱不能为了使用设计模式而使用,而是在分析问题的过程中,想到使用某种设计模式能达到咱需要的效果,而且比不使用设计模式更有优势,那么咱该考虑使用设计模式了. 一.设计模式的一般分类 创建型(Creator)模式(

java基础解析系列(十一)---equals、==和hashcode方法

java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer缓存及装箱拆箱 java基础解析系列(三)---HashMap原理 java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 j