HeadFirstJava学习心得——网络编程
2015-01-11 14:37
507 查看
编写简单的服务器应用程序
编写服务器应用程序需要用到一对Socket。他们是一个会等待用户请求(当用户创建Socket是)的ServerSocket和与用户进行通信用的Socket。工作方式:
1. 服务器应用程序对特定端口创建出ServerSocket。
ServerSocket serverSocket = new ServerSocket(8080);
2. 客户端对服务器应用程序建立Socket连接。
Socket socket = new Socket("127.0.0.1", 8080);
3. 服务器创建出与客户端通信的新的Socket。
Socket socket = serverSocket.accept();
下面是一个简单的聊天室应用程序(这个版本只完成了客户端向服务端发送数据)
服务端:import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class DailyAdviceServer { String[] adviceList = { "愿得一人心,白首不相离。", "天长地久有时尽,此恨绵绵无绝期。", "东边日出西边雨,道是无晴却有晴。", "曾经沧海难为水,除却巫山不是云。", "两情若是久长时,又岂在朝朝暮暮。", "庄生晓梦迷蝴蝶,望帝春心托杜鹃。" }; public static void main(String[] args) { new DailyAdviceServer().go(); } private void go() { try { ServerSocket serverSocket = new ServerSocket(8888);// ServerSocket会监听客户端对这台机器在8888端口上的请求 System.out.println("服务器启动。。。"); /* 服务器进入无限循环监听客户端请求 */ while (true) { Socket socket = serverSocket.accept();// 该方法会停下来,直到满足要求才会继续 /* 向客户端发送数据 */ PrintWriter printWriter = new PrintWriter( socket.getOutputStream());// 使用Socket连接来送出真言信息 String advice = getAdvice(); printWriter.println(advice); System.out.println("欢迎信息:" + advice); /* 接收客户端数据 */ BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(socket.getInputStream())); String info = null; while ((info = bufferedReader.readLine()) != null) { System.out.println("客户端数据:" + info); } } } catch (IOException e) { System.out.println("服务器错误!"); System.exit(-1); } } private String getAdvice() { return adviceList[new Random().nextInt(adviceList.length)]; // 随即返回字符串数组中的一首诗 } }客户端:
import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; public class SimpleClient { private PrintWriter printWriter; private JTextField textField; public static void main(String[] args) { new SimpleClient().go(); } private void go() { /* 图形用户界面的绘制 */ JFrame frame = new JFrame("一个简单的客户端程序"); JPanel panel = new JPanel(); textField = new JTextField("请输入需要发送的信息"); JButton button = new JButton("发送"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { printWriter.println(textField.getText()); printWriter.flush(); textField.setText(""); // 每次发送之后清空输入 textField.requestFocus();//请求焦点 } }); panel.add(textField); panel.add(button); frame.getContentPane().add(panel, BorderLayout.CENTER); frame.setVisible(true); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setUpNetwork(); } private void setUpNetwork() { try { Socket socket = new Socket(InetAddress.getLocalHost(), 8888); printWriter = new PrintWriter(socket.getOutputStream()); System.out.println("连接已建立!"); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }运行结果:
以上代码有一个很明显的bug,即服务端只能接收一个客户端的输入。可以引入线程来解决。
更好的版本:
服务器:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Iterator; public class VerySimpleChatServer { ArrayList clientOutputStreams; public class ClientHandler implements Runnable { BufferedReader bufferedReader; Socket socket; public ClientHandler(Socket socket) { this.socket = socket; try { bufferedReader = new BufferedReader(new InputStreamReader( socket.getInputStream())); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { String message; try { while ((message = bufferedReader.readLine()) != null) { System.out.println("客户端发送数据:" + message); tellEveryone(message); } } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { new VerySimpleChatServer().go(); } private void go() { clientOutputStreams = new ArrayList(); try { ServerSocket serverSocket = new ServerSocket(8888); while (true) { Socket clientSocket = serverSocket.accept(); PrintWriter printWriter = new PrintWriter( clientSocket.getOutputStream()); clientOutputStreams.add(printWriter); Thread t = new Thread(new ClientHandler(clientSocket)); t.start(); System.out.println("已连接。。"); } } catch (IOException e) { e.printStackTrace(); } } private void tellEveryone(String message) { Iterator iterator = clientOutputStreams.iterator(); while (iterator.hasNext()) { PrintWriter printWriter = (PrintWriter) iterator.next(); printWriter.println(message); printWriter.flush(); } } }客户端:
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.ScrollPaneConstants; public class SimpleChatClient { JTextField out; PrintWriter printWriter; JTextArea in; public BufferedReader bufferedReader; public static void main(String[] args) { new SimpleChatClient().go(); } private void go() { /* 界面和事件 */ JFrame frame = new JFrame("聊天室-客户端"); JPanel panel = new JPanel(); in = new JTextArea(10, 50); in.setLineWrap(true); in.setWrapStyleWord(true); // 单词空白处换行 in.setEditable(false); JScrollPane scrollPane = new JScrollPane(in); scrollPane .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); out = new JTextField("请输入你要 4000 发送的信息!", 50); JButton button = new JButton("发送"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { printWriter.println(out.getText()); printWriter.flush(); // 这个是必须要有的 out.setText(""); out.requestFocus(); } }); panel.add(out); panel.add(button); panel.add(new JLabel("服务器端信息:")); panel.add(scrollPane); setUpNetWork(); /* 启动新的线程,以内部类作为任务,此任务是读取服务端的socket串流,显示在文本域 */ Thread readerThread = new Thread(new IncomingReader()); readerThread.start(); frame.getContentPane().add(panel); frame.setVisible(true); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private void setUpNetWork() { Socket socket; try { socket = new Socket(InetAddress.getLocalHost(), 8888); bufferedReader = new BufferedReader(new InputStreamReader( socket.getInputStream())); printWriter = new PrintWriter(socket.getOutputStream()); System.out.println("建立连接。。。"); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public class IncomingReader implements Runnable { @Override public void run() { String message; try { while ((message = bufferedReader.readLine()) != null) { System.out.println("服务器信息:" + message); in.append(message + "\n"); } } catch (IOException e) { e.printStackTrace(); } } } }运行结果:
基于UDP的聊天室的实现。
对于UDP编程,java提供了2个类给予支持。他们分别是DatagramSocket和DatagramPacket。UDP是面向无连接的,发送端和接收端哪个先启动并没有区别。在发送端在构造DatagramPacket(数据包)的时候需要为其指定目的主机和目标端口。在接收方我们只需要在初始化DatagramSocket的时候指定监听端口(发送端需要发送的端口)即可。然后通过此DatagramSocket的receive方法取得数据包,用数据包中的getData方法取得内容。发送端Send.java
public class Send implements Runnable { DatagramSocket ds; int descport; public Send(DatagramSocket ds, int descport) { System.out.println("发送端已经启动。。。。。。。。。。。。。。。"); this.ds = ds; this.descport = descport; } @SuppressWarnings("resource") @Override public void run() { while (true) { System.out.println("请输入要发送的数据:"); Scanner scanner = new Scanner(System.in); try { while (scanner.hasNext()) { System.out.println("请输入要发送的数据:"); String line = scanner.nextLine(); byte[] buf = line.getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), descport); // 数据报的终点是127.0.0.1:8888 ds.send(dp); if ("quit".equals(line)) return; scanner = new Scanner(System.in); } } catch (IOException e) { throw new RuntimeException("发送失败!"); } } } public static void main(String[] args) throws SocketException { // 对于发送方来说,从哪个端口发出数据报并不重要,重要的是数据报要发送到哪里 new Thread(new Send(new DatagramSocket(1111), 8888)).start(); // 发送端127.0.0.1:1111,目的127.0.0.1:8888 } }
接收端Receive.java
public class Receive implements Runnable { DatagramSocket ds; public Receive(DatagramSocket ds) { System.out.println("接收端已经启动。。。。。。。。。。。。。"); this.ds = ds; } @Override public void run() { while (true) { byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf, buf.length); try { ds.receive(dp); String data = new String(dp.getData(), 0, dp.getLength()); if ("quit".equals(data)) { return; } System.out.println("收到来自:" + dp.getSocketAddress() + "的数据:" + data); } catch (IOException e) { throw new RuntimeException("接收失败!"); } } } public static void main(String[] args) throws SocketException { // 对于接收端来说,只需要绑定接收端口即可,不需要关系数据的来源 new Thread(new Receive(new DatagramSocket(8888))).start(); // 接收端在127.0.0.1:8888端口监听 } }
相关文章推荐
- HeadFirstJava学习心得——随机字符串的产生
- HeadFirstJava学习心得——javaGUI编程
- HeadFirstJava学习——对象的序列化和文件的保存
- <Head First Java>学习笔记--第五章:编写程序
- JavaHead First design pattern 学习感想
- <Head First Java>学习笔记--第一章:基本概念
- Head First Java (第二版)学习记录 4 - 方法操作实例变量
- Head First Java【学习经验分享】
- Head First Java (第二版)学习记录 6 - 解决bug (ArrayList 介绍)
- <Head First Java>学习笔记--第三章:primitive主数据类型和引用
- Head First Java (第二版)学习记录 2 - 对象
- Head First Java (第二版)学习记录 3 - primitive主数据类型和引用
- HeadFirstJava学习——数字和日期格式化
- <Head First Java>学习笔记--第四章:方法操作实例变量 对象的行为
- headFirst java学习笔记之一:基础知识部分(5.7)
- 《Head First Java》学习整理
- 《Head First Java》学习笔记 (八)接口与多态
- Head First Java (第二版)学习记录 5 - 编写程序 DotCom 初级游戏
- headfirstjava 学习笔记(chapter1-5)
- <Head First Java>学习笔记--第六章:认识Java的API