HeadFirstJava学习心得——网络编程

编写简单的服务器应用程序

编写服务器应用程序需要用到一对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("请输入你要发送的信息!", 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端口监听
	}

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值