☆ TCP

建立连接,形成传输数据的通道。

在连接中进行大数据量传输

通过三次握手完成连接,是可靠协议

必须建立连接,效率会稍低

Socket 和

ServerSocket类

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();

最简单的TCP演示实例:

客户端代码:

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; /**
* 客户端
* @author 陈浩翔
*
* 2016-5-10
*/
public class MyClientSocket { public static void main(String[] args) {
try {
//因为是在自己本机上演示,IP就直接填写本机10.30.7.95的了。
//这个端口和IP都是服务器端的(自己可以改的)
Socket s = new Socket("10.30.7.95", 9999);
//和服务器进行三次握手,若失败则出异常,否则返回和对方通讯的socket OutputStream os = s.getOutputStream();
//发送数据
os.write("你好,服务器!".getBytes()); //接收服务器端的反馈
InputStream in = s.getInputStream(); DataInputStream din = new DataInputStream(in); System.out.println(din.readUTF()); s.close();
din.close(); } catch (UnknownHostException e) {
e.printStackTrace();
} 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; /**
* 服务器端
* @author 陈浩翔
*
* 2016-5-10
*/
public class MyServerSocket { public static void main(String[] args) { try {
ServerSocket server = new ServerSocket(9999);
while(true){ System.out.println("准备接收一个数据...");
Socket s = server.accept();//阻塞式方法
System.out.println("接收了一个数据..."); //读--从客户端读数据
InputStream in = s.getInputStream();
byte buf[] = new byte[1024];
in.read(buf);
System.out.println("read info: "+new String(buf)); //写--应答客户端--向他写数据
OutputStream out = s.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
dout.writeUTF("你好,"+s.getInetAddress().getHostAddress()+" ,你的信息已收到。");
dout.close();
s.close();
}
} catch (IOException e) {
e.printStackTrace();
} } }

客户端运行结果:

你好,10.30.7.95  ,你的信息已收到。

服务器端运行结果:

☆思考

  对于Web服务器而言,当有多个客户端同时访问服务器时,服务端又如何提供服务呢?

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

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

通过例程分析:

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

解决办法:

自定义结束标记(必须定义传输文件中没有这个这个字符串的,不然会出现接收数据不完整)

使用shutdownInput,shutdownOutput方法。

编程练习

☆上传文本文件

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

文本文件发送的客户端

package cn.hncu.tcp.upload;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException; /**
* 文本文件发送的客户端
* @author 陈浩翔
*
* 2016-5-10
*/
public class UploadTextClient { public static void main(String[] args) { try {
Socket s = new Socket("", 10006); //思路:把本地文件的数据读取出来通过 s.getOutputStream()获得的out对象发送出去
BufferedReader bf = new BufferedReader(new FileReader("tempfiles\\client.txt")); OutputStream out = s.getOutputStream();//这里的输出流 对应的是服务器端的输入流
PrintWriter pw = new PrintWriter(out,true);//建议不要用BufferedWriter
//!!!!!!!!!!!!!!!!!这个true不要忘了!---自动刷新
//现在大家写网络传输文件,一般是用PrintWriter String str=null;
while((str=bf.readLine())!=null){
pw.println(str);
} //给服务器发送结束标记---上传结束,要加结束标记,
//否则服务器在数据接收完毕时再调用read()或readLine()时会出异常
//法1:pw.println("over#$@#@$");//不能出现文件中存在的结束关键字---搞特殊一点
//法2---建议采用该种方式---由socket内部来指定结束标记
s.shutdownOutput();
bf.close(); //接收服务器端反馈 InputStream in = s.getInputStream();
DataInputStream din = new DataInputStream(in);
System.out.println("server应答:"+din.readUTF());
s.close();
din.close(); } catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } }

文本文件接收的服务器端

package cn.hncu.tcp.upload;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket; /**
* 文本文件接收的服务器端
* @author 陈浩翔
*
* 2016-5-10
*/
public class UploadTextServer { public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(10006); Socket s = server.accept();
System.out.println(s.getInetAddress().getHostAddress()+"...发送消息来"); //读取客户端上传过来的文本文件
//源 ---socket(字节流)---额外:需要转换成字符流 ,缓存流
InputStream in = s.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//目的 ---硬盘字符流 FileWriter---额外:打印流
PrintWriter pw = new PrintWriter(new FileWriter("tempfiles\\server.txt"),true);
String line = null;
while((line=br.readLine())!=null){
// if("over#$@#@$".equals(line)){//自己定义的结束标志
// break;
// }
pw.println(line);
} pw.close(); //上传成功,给客户端一个提示信息 OutputStream out = s.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
dout.writeUTF("文件上传成功!");
s.close();
server.close();
dout.close(); } catch (IOException e) {
e.printStackTrace();
} } }

测试通过!

☆上传图片文件

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

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

图片文件的发送-客户端

package cn.hncu.tcp.upload;

import java.io.BufferedInputStream;
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;
import java.util.Scanner; import javax.swing.JOptionPane; /**
* 图片文件的发送-客户端
* @author 陈浩翔
*
* 2016-5-10
*/
public class UploadPicClient { public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入图片路径:");
String str = sc.next(); File file = new File(str);
if(!(file.exists()&& file.isFile())){
JOptionPane.showMessageDialog(null, "文件不存在!");
return ;
} if(!(file.getName().endsWith(".jpg")||file.getName().endsWith(".gif"))){
JOptionPane.showMessageDialog(null, "文件格式不对,文件扩展名必须是jpg或gif!");
return ;
}
if( file.length()>=1024*1024*2){
JOptionPane.showMessageDialog(null, "文件过大,不应超过2M,请重新上传!");
return;
} //上传
try {
Socket s = new Socket("10.30.7.95", 10007); BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str)); OutputStream out = s.getOutputStream(); byte buf[] = new byte[1024];
int len=0;
while((len=bin.read(buf))!=-1){
out.write(buf, 0, len);
} s.shutdownOutput();//告诉服务器,文件上传完毕 //读取服务器的回馈信息
InputStream in = s.getInputStream();
byte buf2[] = new byte[1024];
int len2 = in.read(buf2);
System.out.println(new String(buf2, 0, len2)); //关流
out.close();
bin.close();
s.close(); } catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } }

图片文件接收-服务器端

package cn.hncu.tcp.upload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; /**
* 图片文件接收-服务器端
* @author 陈浩翔
*
* 2016-5-10
*/
public class UploadPicServer { public static void main(String[] args) { try {
ServerSocket server = new ServerSocket(10007);
while(true){
Socket s = server.accept();//阻塞方法
//只负责和客户端进行握手
new Thread(new UploadThread(s) ).start();
}
} 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+"...发来图片"); try {
BufferedInputStream bin = new BufferedInputStream(s.getInputStream()); File dir = new File("g:\\mypic"); if(!dir.exists()){
dir.mkdir();//文件夹不存在,创建mypic文件夹
} 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=bin.read(buf))!=-1){
fout.write(buf, 0, len);
}
//图片接收完毕 //向客户端发送回馈信息
OutputStream out = s.getOutputStream();
out.write( "上传成功".getBytes() ); fout.close();
s.close(); } catch (IOException e) {
e.printStackTrace();
} }
}

有一个小bug。是服务端接收的,因为我把后缀名统一为jpg了,gif的图片可以上传,只是变成静图了(jpg)。可以通过改后缀名,再把这图片改回去动图。还有一种方法,在上传的时候,把后缀名也上传,再通过服务器解析就可以解决这个问题。

因为时间问题,我就不写了,这个很简单的。

只是多发送了一个后缀名过去而已。

我还是把那个后缀名的解决办法写了下:

在客户端:修改的代码如下:

//上传
try {
Socket s = new Socket("10.30.7.95", 10007); BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str)); //上传文件后缀###########增加的
OutputStream out = s.getOutputStream();
String fileName = file.getName();
out.write(fileName.substring(fileName.length()-4, fileName.length()).getBytes()); byte buf[] = new byte[1024];
int len=0;
while((len=bin.read(buf))!=-1){
out.write(buf, 0, len);
} s.shutdownOutput();//告诉服务器,文件上传完毕 //读取服务器的回馈信息
InputStream in = s.getInputStream();
byte buf2[] = new byte[1024];
int len2 = in.read(buf2);
System.out.println(new String(buf2, 0, len2)); //关流
out.close();
bin.close();
s.close(); } catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

服务器端:

@Override
public void run() { String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...发来图片"); try { BufferedInputStream bin = new BufferedInputStream(s.getInputStream()); File dir = new File("g:\\mypic"); if(!dir.exists()){
dir.mkdir();//文件夹不存在,创建mypic文件夹
} //读取上传过来的图片后缀是什么!#########
char cbuf[] = new char[4];
InputStreamReader insr = new InputStreamReader(bin);
insr.read(cbuf);
String str = new String(cbuf); int count=1;
//我觉得这里的后缀名,需要通过发送方也发过来的
File file = new File(dir, ip+str); while(file.exists()){
file = new File(dir,ip+"("+(count++) +")"+str); //带号的文件名
} FileOutputStream fout = new FileOutputStream(file); //从socket流中读取数据,存储到本地文件。相当于对拷
byte buf[] = new byte[1024];
int len=0;
//#########必须有这一句
bin.read(buf, 0, 8);
while( (len=bin.read(buf))!=-1){
fout.write(buf, 0, len);
}
//图片接收完毕 //向客户端发送回馈信息
OutputStream out = s.getOutputStream();
out.write( "上传成功".getBytes() ); fout.close();
s.close(); } catch (IOException e) {
e.printStackTrace();
} }

好了,基本上就是这样的,如果想要更好,做个图形界面就好看了。自己动下手吧~~

Java---网络编程(3)-TCP-互传文件和图片的更多相关文章

  1. Java 网络编程 -- 基于TCP 模拟多用户登录

    Java TCP的基本操作参考前一篇:Java 网络编程 – 基于TCP实现文件上传 实现多用户操作之前先实现以下单用户操作,假设目前有一个用户: 账号:zs 密码:123 服务端: public c ...

  2. java 网络编程 UDP TCP

    网络编程 网络编程主要用于解决计算机与计算机(手机.平板..)之间的数据传输问题. 网络编程: 不需要基于html页面就可以达到数据之间的传输. 比如: feiQ , QQ , 微信....网页编程: ...

  3. java网络编程基础——TCP网络编程一

    基于TCP协议的网络编程 TCP/IP协议是一种可靠的网络协议,它的通信的两端各自建立一个Socket,从而在通信的两端之间形成网络虚拟链路. Java使用Socket对象来代表两端的通信端口,并通过 ...

  4. Java 网络编程 -- 基于TCP实现文件上传

    Java TCP 操作基本流程 一.创建服务器 1.指定端口, 使用serverSocket创建服务器 2.阻塞式连接 accept 3.操作:输入流 输出流 4.释放资源 二.创建客户端 1.使用S ...

  5. java网络编程(5)——Tcp

    Tcp相对于Udp就是面向连接的协议,最主要的特点要建立连接之后才能通信,客户端用Socket需要指点地址与端口,服务端用ServeSocket,只需指点监听某个端口,因为是面向连接,所以服务器端一定 ...

  6. java网络编程基础——TCP网络编程三

    AIO实现非阻塞通信 java7 NIO2 提供了异步Channel支持,这种异步Channel可以提供更高效的IO,这种基于异步Channel的IO被称为异步IO(Asynchronous IO) ...

  7. java网络编程基础——TCP网络编程二

    1.半关闭的Socket 前面的服务器和客户端通信时总是以行为最小数据单位,但是在某些协议里,通信的数据单位可能是多行的,当出现多行数据时就 出现一个问题:Socket输出流如何表示输出数据已经结束. ...

  8. java 网络编程基础 TCP/IP协议:服务端ServerSocket;客户端Socket; 采用多线程方式处理网络请求

    1.Java中客户端和服务器端通信的简单实例 Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一 ...

  9. Java 网络编程 -- 基于TCP 实现聊天室 群聊 私聊

    分析: 聊天室需要多个客户端和一个服务端. 服务端负责转发消息. 客户端可以发送消息.接收消息. 消息分类: 群聊消息:发送除自己外所有人 私聊消息:只发送@的人 系统消息:根据情况分只发送个人和其他 ...

  10. 用java网络编程中的TCP方式上传文本文件及出现的小问题

    自己今天刚学java网络编程中的TCP传输,要用TCP传输文件时,自己也是遇到了一些问题,抽空把它整理了一下,供自己以后参考使用. 首先在这个程序中,我用一个客户端,一个服务端,从客户端上传一个文本文 ...

随机推荐

  1. 初识 .NET平台下作业调度器——Quartz.NET

    Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...

  2. Android - 向服务器发送数据(POST) - HTTPClient.

    该篇文章主要说明使用Apache提供的HTTPClient,通过post方式,向服务器发送数据.由于有些东西在 Android - 向服务器发送数据(GET)中提到过,就不再重复. 一,Android ...

  3. 深入理解shared pool共享池之library cache系列二

    背景 继续上文:深入理解shared pool共享池之library cache系列一,学习library cache数据结构,本文主要学习library cache object(lco)的数据结构 ...

  4. CoreAnimation

    CoreAnimation 1.CABasicAnimation // position CABasicAnimation *ba = [CABasicAnimation animationWithK ...

  5. 前端开发构建工具gulp的安装使用

    曾几何时还在使用grunt作为前端的构建工具,直到有一天同事向我推荐了gulp,在这里博主将不讨论gulp与grunt各自优势的比较,只为大家介绍gulp如何安装和使用. Gulp 是用 nodejs ...

  6. C++线程类的封装

    简单的C++线程操作的封装,使用了智能指针管理对象的释放. 可运行对象基类 class SimpleRunable:public RefCountedBase { public: SimpleRuna ...

  7. php 单引号与双引号区别

    一.单引号与双引号区别 1." "双引号里面的字段会经过编译器解释,然后再当作HTML代码输出. 2.' '单引号里面的不进行解释,直接输出. 从字面意思上就可以看出,单引号比双引 ...

  8. InstallShield:自己备份

    LIST listData;//声明listData listData = ListCreate(STRINGLIST);//创建一个空的实际字符串或数字列表. //参数都是在上个界面中赋值,然后在下 ...

  9. Object之魔术函数__toString() 直接输出对象引用时自动调用

    __toString()是快速获取对象的字符串信息的便捷方式 在直接输出对象引用时自动调用的方法. __toString()的作用 当我们调试程序时,需要知道是否得出正确的数据.比如打印一个对象时,看 ...

  10. vbox里面的Ubuntu虚拟机与主机win7之间设置共享文件夹

    有时候我们希望虚拟机和主机之间进行通信,例如传一些文件.那么设置共享文件夹就是一种很好的方式. 这里我的主机是win7系统,vbox里面的虚拟机是Ubuntu. 1.首先安装vbox的VBOXGues ...