Java中基于TCP协议实现网络通信的两个类:客户端的Socket和服务器端的ServerSocket。

Socket通信模型如图所示:

不管Socket通信的功能有多复杂,任何socket通信过程的基本结构都是一样的。其基本步骤为:

①分别在客户端和服务器端创建Socket和ServerSocket实例;服务器端通过.accept()方法等待请求并阻塞。请求收到后,建立连接Socket对象。

②通过getInputStream和getOutputStream方法分别在客户端和服务器端打开输入输出流

③利用IO流进行读写操作

④关闭所有的流资源和套接字资源。

其中,编程工作主要集中在第三步,其他的部分代码基本相同。所有步骤都可能抛出IO异常!

我在编写一个简单的socket程序时,使用的Socket通信出现了一个问题:我在客户端写入的数据,在服务器端无法输出。当我从客户端断开连接时,之前写入的所有数据立刻在服务器端输出出来了。经过反复的验证和求解,以下是我的结论和解决方法。希望有同样问题的小伙伴看完可以解决问题。

通过一端的Socket建立了PrintWriter类来写入数据,通过另一端的Socket建立了BufferedReader类来读取数据并输出。如果数据写入后没有被显示,可能的原因有两种:

一、写入的数据存储在缓冲区中,没有被写入IO流中:

如果不主动的干涉,写入的数据会一直堆在缓冲区中,直到缓冲区满了引发JVM自动刷新缓冲区。显然这不符合我们的需求。对于这种情况,PrintWriter类提供了flush()方法来强制刷新缓冲区,将缓冲区数据写入IO流中。另外,PrintWriter类的构造器有一个参数”boolean autoflush“,这个参数默认为false,如果设置为true,则会开启自动刷新缓冲区功能。但是请注意,这里的自动刷新是有触发条件的,那就是:PrintWriter类写入数据的方法必须是println、printf或者format方法时,才会触发自动刷新。如果是调用write()这类方法写入数据,是不会触发自动刷新的!总结起来,就是三点:autoflush参数设置,write和println方法的选择,flush方法的使用。对这三个进行组合,就能保证在Socket通信的某一端写入数据时,数据一定能成功地写入到IO流中!

二、读取数据使用了readLine()方法,该方法没有正常的结束:

请注意,BufferedReader类的readLine()方法是一个阻塞函数!也就是说,这个方法本身是读取一行数据,但是它自己识别不了什么叫做“一行”!当调用该方法读取完一段数据后,它会阻塞,而不会return它的读取数据。这就是为什么有的时候明明已经刷新了缓冲区正确的写入数据了,还是通过输入流读取数据并显示出来的原因。

对于readLine()方法,它解除阻塞、正确结束并返回读取的值,只有以下几种情况:

①读取的数据里含有回车符"\r"或者换行符"\n"或者回车换行符"\r\n";

②读取的数据是在另一端通过println方法写入的,因为println方法自带换行符;

③BufferedReader类的缓冲区满了,那么JVM会自动刷新缓冲区从而释放“积攒”的数据(但是鉴于默认缓冲区大小为8192个字符,对于小数据量的通信,显然触发不了);

④对于读取的数据,写入这些数据的流发生异常或者直接关闭,那么readLine()就会把它吃的数据全部吐出来。这就刚好解释了,为什么在我的程序中,断开客户端Socket连接,服务器端立刻输出所有客户端消息的原因。

综上,在Socket通信过程中,保证某一端输出流的缓冲被刷新,保证另一端的readLine方法能正常停止,即可解决写入的数据在另一端无法输出的问题。

以下是我修改后能成功运行的代码,分别是服务器端Socket和客户端Socket。

over!

import java.io.BufferedReader;
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;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
public class ShakingServer{
public static void main(String[] args) throws IOException { //创建服务器套接字实例,设置监听端口为2000
ServerSocket server=new ServerSocket(2000);
//开始监听客户端的请求,并阻塞
Socket socket=server.accept();
//请求收到后,自动建立连接。通过IO流进行数据传输
System.out.println("连接建立成功"); OutputStream os=socket.getOutputStream();
PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)),true);
pw.write("欢迎访问摇头耶稣的世界!");
pw.flush();
//因为我关闭了输出流,所以另一端的readLine方法才正常结束了
socket.shutdownOutput(); InputStream is=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
BufferedReader br=new BufferedReader(isr);
while(true) {
String str=br.readLine();
if(str.equals("quit")) {
break;
}
System.out.println("Client said: "+str);
}
socket.shutdownInput();
//socket.shutdownOutput();
socket.close();
server.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket; public class ShakingClient{
public static void main(String[] args) throws IOException{ //创建客户端的套接字,设置连接的服务器的IP地址和端口号
Socket socket=new Socket("169.254.132.203",2000);
//输入流读取服务器发送的信息
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//开启自动刷新缓冲区
PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
//从键盘读取数据
BufferedReader ii=new BufferedReader(new InputStreamReader(System.in));
System.out.println(br.readLine());
//因为开启了自动刷新,且调用的是println方法,所以可以不调用flush方法
pw.println("请求进入摇头耶稣的世界");
//pw.flush();
while(true) {
String str=ii.readLine();
//使用了回车符来保证另一端的readLine方法正常结束
pw.write(str+"\r");
pw.flush();
//如果输入quit则退出聊天室
if(str.equals("quit")) {
break;
}
}
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
}
}

Java Socket通信以及可能出现的问题解决的更多相关文章

  1. java socket通信-传输文件图片--传输图片

    ClientTcpSend.java   client发送类 package com.yjf.test; import java.io.DataOutputStream; import java.io ...

  2. java Socket通信使用BufferedReader和BufferedWriter的注意事项

    注意事项:readLine()要求有换行标识,write()要输出换行标识,要调用flush()刷新缓冲区. 以下是取自java socket通信中的一小段代码. BufferedReader rea ...

  3. Java Socket通信读取相关信息代码

    转自:http://developer.51cto.com/art/201003/190206.htm Java Socket通信读取有不少需要我们注意的知识点.当我们在使用的时候有很多的问题摆在我们 ...

  4. Java socket通信

    首先抛开语言层面,简单介绍一下socket通信过程: 1.服务器端开启监听端口,阻塞进程  等待客户端连接 2.客户端连接,这时就产生了一个socket socket就相当于一个传递消息的通道,一般都 ...

  5. Java Socket通信实现私聊、群聊

    前言 闲言少叙,上代码! 代码编写 server服务端 /** * 服务端 */ public class Server { private static ServerSocket server = ...

  6. Java Socket 通信实例 - 转载

    基于Tcp协议的简单Socket通信实例(JAVA)   好久没写博客了,前段时间忙于做项目,耽误了些时间,今天开始继续写起~ 今天来讲下关于Socket通信的简单应用,关于什么是Socket以及一些 ...

  7. Java Socket通信实例

    一.简单的客户端与服务器一对一连接: Socket通信的步骤: 1.创建ServerSocket和Socket 2.打开连接到Scket的输入/输出流 3.按照协议对Socket进行读/写操作 4.关 ...

  8. 【Java】Java Socket 通信演示样例

    用socket(套接字)实现client与服务端的通信. 这里举两个样例: 第一种是每次client发送一个数据,服务端就做一个应答. (也就是要轮流发) 另外一种是client能够连续的向服务端发数 ...

  9. java Socket通信,客户端与服务端相互发消息

    1.通信过程 网络分为应用层,http.ssh.telnet就是属于这一类,建立在传输层的基础上.其实就是定义了各自的编码解码格式,分层如下: 2.Socket连接 上述通信都要先在传输层有建立连接的 ...

随机推荐

  1. spring mvc:ueditor跨域多图片上传失败解决方案

    公司在开发一个后台系统时需要使用百度的UEditor富文本编辑器,应用的场景如下: UEditor的所有图片.js等静态资源在一个专有的静态服务器上: 图片上传在另外一台服务器上: 因为公司内部会使用 ...

  2. Android WebView基本使用

    转载请注明出处: http://blog.csdn.net/lowprofile_coding/article/details/77928614 WebView介绍 Android WebView在A ...

  3. ios 初体验<窗口的创建>

    1. 创建工程,这步很简单,百度下即可,在info.plist 里面 去掉 Main 的 ,便于新手练习ios,创建ios工程后,在AppDelegate.m,里面的方法application 加上几 ...

  4. 基于CAS的SSO(单点登录)实例

    第一步 部署CAS-Server(服务端) 1.从CAS官方网站(http://developer.jasig.org/cas/)下载最新版本的CAS-Server(当前最新版本cas-server- ...

  5. 【★】KMP算法完整教程

    KMP算法完整教程 全称:                               Knuth_Morris_Pratt Algorithm(KMP算法) 类型:                 ...

  6. .opt,frm,.MYD,.MYI文件如何转为.sql文件?

    假如你是网站测试人员,数据库管理员从服务器上导出数据库,如下图: 你会发现这不是.sql文件,需要将其转化. 其实很简单,只要你本地比如D盘有安装“phpstudy”和“SQLyog”就可以,你可以直 ...

  7. 201521123025 《Java程序设计》第2周学习总结

    1. 本章学习总结 一些注意: (1)在JAVA中,不加后缀的浮点数被默认为double型,如果要用float型就要在数据后加上f或F后缀,如float a=32.6f(正确);float a=32. ...

  8. 201521123024 《Java程序设计》第1周学习总结

    一.本周章学习总结 1.Java的版本迁移 2.运用eclipse和notepad++编写Java 3.对JDK,JRE,JVM有初步的了解 二.书面作业 1.为什么java程序可以跨平台运行?执行j ...

  9. 201521123005《Java程序设计》第十三周学习总结

    1. 本周学习总结 1.网络基本概念 2.网络常用命令 ipconfig 查看网络配置信息,如ip地址 ping 测试网络是否连通 telnet 远程登录 ftp 登陆ftp Fport (xp下)查 ...

  10. java程序与编译

    Java 源文件(.java)   使用 Java编译器(javac.exe)编译 生成 java字节码文件(.class) 使用 解释执行器(java.exe) 将字节码文件加载到java虚拟机(j ...