JavaSE——TCP网络编程(二)
1.Java网络编程与多线程的综合应用:
类Socket提供了方法getInputStream ()和getOutStream()来得到对应的输入/输出流以进行读/写操作,这两个方法分别返回InputStream和OutputSteam类对象。为了便于读/写数据,我们可以在返回的输入/输出流对象上建立过滤流,如DataInputStream、DataOutputStream或PrintStream类对象,对于文本方式流对象,可以采用InputStreamReader和OutputStreamWriter、PrintWirter等处理。
服务器端程序设计
在服务器端,利用ServerSocket类的构造函数ServerSocket(int port)创建一个ServerSocket类的对象,port参数传递端口,这个端口就是服务器监听连接请求的端口,如果在这时出现错误将抛出IOException异常对象,否则将创建ServerSocket对象并开始准备接收连接请求。
服务程序从调用ServerSocket的accept()方法开始,直到连接建立。在建立连接后,accept()返回一个最近创建的Socket对象,该Socket对象绑定了客户程序的IP地址或端口号。
客户端程序设计
当客户程序需要与服务器程序通信时,需在客户机创建一个Socket对象。Socket类有构造函数Socket(InetAddress addr,int port)和Socket(String host,intport),两个构造函数都创建了一个基于Socket的连接服务器端流套接字的流套接字。对于第一个InetAddress子类对象通过addr参数获得服务器主机的IP地址,对于第二个函数host参数包被分配到InetAddress对象中,如果没有IP地址与host参数相一致,那么将抛出UnknownHostException异常对象。两个函数都通过参数port获得服务器的端口号。假设已经建立连接了,网络API将在客户端基于Socket的流套接字中捆绑客户程序的IP地址和任意一个端口号,否则两个函数都会抛出一个IOException对象。
如果创建了一个Socket对象,那么它可通过get-InputStream()方法从服务程序获得输入流读传送来的信息,也可通过调用getOutputStream()方法获得输出流来发送消息。在读写活动完成之后,客户程序调用close()方法关闭流和流套接字。
例实现服务端与客户端简单的通信;
服务器端代码:
- public class Server1 {
- public static void main(String[] args) {
- Scanner input = new Scanner(System.in);
- try {
- @SuppressWarnings("resource")
//创建一个ServerSocket在端口4700监听客户请求- ServerSocket server = new ServerSocket(9880);
- while (true){
//使用accept()阻塞等待客户请求,有客户
//请求到来则产生一个Socket对象,并继续执行- Socket client = server.accept();
- System.out.println("来自"+ client.getInetAddress().getHostAddress()+"的客户端已连接成功!");
- if(client != null){
- //开始对话
- OutputStream os = client.getOutputStream();//从 client(Socket对象) 获取一个输出字节流
//由Socket对象得到输出流,并构造PrintWriter对象- PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)));
- System.out.print("我说:");
- String serverMsg = input.nextLine();
- pw.println(serverMsg); //发送 serverMsg 给客户端
- pw.flush();//刷新输出流,使Client马上收到该字符串
- //服务端读取客户端发送来的信息
- InputStream is = client.getInputStream(); //获取来自客户端的字节流
- //由Socket对象得到输入流,并构造相应的BufferedReader对象
- BufferedReader reader = new BufferedReader(new InputStreamReader(is));
- String msg = reader.readLine(); //每次读取一行信息
- System.out.print("客户端说:"+msg);
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- input.close();
- }
- }
- 客户端代码:
- public class Client1 {
- public static void main(String[] args) {
- @SuppressWarnings("resource")
- Scanner input = new Scanner(System.in);
- try {
- @SuppressWarnings("resource")
//向本机的9880端口发出客户请求- Socket client = new Socket("127.0.0.1",9880);
- //通过socket获得输入流
- InputStream is = client.getInputStream();
//由系统标准输入设备构造BufferedReader对象- BufferedReader reader= new BufferedReader(new InputStreamReader(is));
- //读取输入流
- String msg = reader.readLine();
- //打印来自服务端的信息在
//系统标准输出上打印读入的字符串- System.out.println("服务端说:"+msg);
- //向服务端发送消息
- System.out.print("我说:");
//输入要向服务端发送的信息- String clientMsg = input.nextLine();
- //获得输出字节流
- OutputStream os = client.getOutputStream();
//将输出字节流转换为字节流加缓冲(缓冲后的为字符流)构造打印流对象- PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)));
- pw.println(clientMsg); //将从系统标准输入读入的字符串输出到Server
- pw.flush();//刷新输出流,使Server马上收到该字符串
- } catch (UnknownHostException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
Output:
存在问题:
双方通信须服务端先发起对话,并且是一问一答的形式;
客户端向服务端发送完最后一句话后结束程序。
改进方法:线程操作。分别将读取和发送信息装载在两个线程中,使读取和发送可以独立进行。
使双方可以自由对话
改进代码:
- 读线程
- public class ReaderThread extends Thread {
- private Socket socket ; //传入 socket
- public ReaderThread(String name ,Socket socket){
- super(name);
- this.socket = socket;
- start();
- }
- public void run(){
- InputStream is;
- try {
- is = socket.getInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(is));
- boolean flag = true;
- do{
- String msg = reader.readLine();
- System.out.println("对方说:" + msg);
- flag = "byebye".equals(msg)?false:true;//当 msg 为 byebye 时 flag 为 false,线程结束
- }while(flag);
- } catch (IOException e) {
- e.printStackTrace();
- }finally{
- try {
- socket.shutdownInput(); //关闭输入流
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- 写线程
- public class WriterThread extends Thread {
- private Socket socket;
- public WriterThread(String name,Socket socket){
- super(name);
- this.socket = socket;
- start();
- }
- public void run(){
- @SuppressWarnings("resource")
- Scanner input = new Scanner(System.in);
- try {
- OutputStream os = socket.getOutputStream();
- PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)));
- boolean flag = true;
- do{
- System.out.print("我说:");
- String servermsg = input.nextLine();
- pw.println(servermsg);
- pw.flush();
- flag = "byebye".equals(servermsg)?false:true;
- }while(flag);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }finally{
- try {
- socket.shutdownOutput();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- 服务器端
- public class Server1 {
- public static void main(String[] args) {
- Scanner input = new Scanner(System.in);
- try {
- @SuppressWarnings("resource")
- ServerSocket server = new ServerSocket(9880);
- while (true){
- Socket client = server.accept();
- System.out.println("来自"+ client.getInetAddress().getHostAddress()+"的客户端已连接成功!");
- if(client != null){
- //开始对话
- new WriterThread("服务端",client);
- new ReaderThread("服务端",client);
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- input.close();
- }
- }
- 客户端
- public class Client1 {
- public static void main(String[] args) {
- try {
- Socket client = new Socket("127.0.0.1",9880);
- new ReaderThread("客户端",client);
- new WriterThread("客户端",client);
- } catch (UnknownHostException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
运用线程操作,将用户键盘输入的内容装载在 WriterThread 中,将接收到的消息装载在 ReadThread 中,在服务器端和客户端分别实例化这两个线程,需要注意的是,在服务器端实例化的线程名称设置为 服务器 ,客户端为 客户端 ,但两个线程的套接字均为Socket的对象 client ,因为 ServerSocket 一般仅用于设置端口号和监听,真正进行通信的是服务器端的Socket与客户端的Socket,在ServerSocket 进行accept之后,就将主动权转让了。
缓冲区读者从输入流读取数据,且这个输入流是系统默认输入流,也就是用户在控制台的输入,用户在控制台的输入的东西会先到缓冲区,如果不放到缓冲区,一次只能读到1个字符。
while循环中的reader.readLine()这个方法是从缓冲区读出一行字符放到 msg 这个字符串里面,再判断这个字符串是否与 byebye 相等,相等则结束循环,比较字符串内容相等,必须用字符串的equals()方法,不可用 == 因为 == 是比较地址是否相等。
输出部分,是先声明一个打印流,这个打印流通过实例获取的输出字节流,将输出字节流装入输出缓冲流,,然后实例该缓冲流获得的。有了这个打印流之后,就可以使用 pw.println(servermsg)来对其打印对象输出 servermag 信息,将打印流里的文本信息 发送出去,如果是服务端执行到该代码时,则将打印流中的信息发送到客户端,反之亦然。servermsg 为用户输入的内容,输入可用 Scanner 函数实现。
最后结束时,要关闭这个流,Socket 类有一个方法 shutdownOutput/shutdownInput 分别为关闭输出流和输入流。
输入输出流进行双方通信,而线程则使得双方可以自由通信,即读和写的自由进行。
如有不对之处还望指正,谢谢(●'◡'●)
//由系统标准输入设备构造BufferedReader对象
JavaSE——TCP网络编程(二)的更多相关文章
- java网络编程基础——TCP网络编程二
1.半关闭的Socket 前面的服务器和客户端通信时总是以行为最小数据单位,但是在某些协议里,通信的数据单位可能是多行的,当出现多行数据时就 出现一个问题:Socket输出流如何表示输出数据已经结束. ...
- Linux下TCP网络编程与基于Windows下C#socket编程间通信
一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使s ...
- JAVA TCP网络编程学习笔记
一.JAVA网络编程概述 网络应用程序,就是在已实现网络互联的不同计算机上运行的应用程序,这些程序之间可以相互交换数据.JAVA是优秀的网络编程语言,Java网络编程的类库位于java.net包中.J ...
- Linux网络编程(二)
Linux网络编程(二) 使用多进程实现服务器并发访问. 采用多进程的方式实现服务器的并发访问的经典范例. 程序实现功能: 1.客户端从标准输入读入一行文字,发送到服务器. 2.服务器接收到客户端发来 ...
- 8-2udp和tcp网络编程以及粘包和解决粘包的方法
一 tcp网络编程 server 端 import socket sk=socket.socket() #实例化一个对象 sk.setsockopt(socket.SOL_SOCKET,socket ...
- 简述TCP网络编程本质
基于事件的非阻塞网络编程是编写高性能并发网络服务程序的主流模式,头一次使用这种模式编程需要转换思维模式 .把原来的"主动调用recv()来接收数据,主动调用accept()来接受连接,主动调 ...
- 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
[Linux网络编程]TCP网络编程中connect().listen()和accept()三者之间的关系 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: conn ...
- TCP网络编程
TCP网络编程 与UDP不同的是TCP是通过客服端和服务端的方式来传输数据的.客服端:public class TCPClient { /** * @param args * @th ...
- Java - TCP网络编程
Java - TCP网络编程 Server 逻辑思路: 创建ServerSocket(port),然后服务器的socket就启动了 循环中调用accept(),此方法会堵塞程序,直到发现用户请求,返回 ...
随机推荐
- 少睡与吸烟影响IQ
导读:据英国<每日邮报>报道,根据科学家一项最新研究发现,一晚的糟糕睡眠,对大脑可能产生很大损害,就等同头部遭到了一次严重的撞击.长期睡眠不好会造成智力下降,请看[科学探索]揭秘: ...
- 算法 - 求两个自然数的最小公倍数(C++)
//************************************************************************************************** ...
- iOS 自定义滑动切换TabBar
貌似经常会用到,自己整理收藏起来,方便日后查找备用. 效果如图: 由于制作gif,调整了属性,所以看起来的效果不好.如果用默认配置,生成的gif会很大. 制作gif: 1.使用QuickTimePla ...
- (笔记)Linux内核学习(五)之中断推后处理机制
一 中断 硬件通过中断与操作系统进行通信,通过对硬件驱动程序处注册中断处理程序,快速响应硬件的中断. 硬件中断优先级很高,打断当前正在执行的程序.有两种情况: 硬件中断在中断处理程序中处理 硬件中断延 ...
- 使用Xcode6.1.1打包出现Your account already has a valid iOS Distribution certificate问题
1.问题描述: 使用客户证书在Xcode6.1.1上进行打包测试,出现如下问题,查看网上也很多类似错误且解决办法各异. 2.我的解决办法: 让客户将开发.发布证书重新revoke掉之后重新创新并给到p ...
- CLR VIA
标题 状态 内容 什么是CLR? 什么是托管模块? 托管模块由什么组成? .net代码的执行过程 http://www.cnblogs.com/aaa6818162/p/4726581.ht ...
- 飞思卡尔9S12X系列双核中的协处理器XGATE使用方法
http://adi.chinaaet.com/analog/blogdetail/24482.html
- 简化 Hadoop 2.4.1 Eclpse 插件编译【原创】
昨天折腾hadoop2X的eclipse插件,从https://github.com/winghc/hadoop2x-eclipse-plugin把源码搞下来后,很快搞定出来一个,但是...New H ...
- Linux网络流量实时监控ifstat iftop命令详解
ifstat 介绍 ifstat工具是个网络接口监测工具,比较简单看网络流量 实例 默认使用 #ifstat eth0 eth1 KB /s i ...
- Python strange questions list
sys.setrecursionlimit(1<<64) Line 3: OverflowError: Python int too large to convert to C long ...