Java网络与线程

本文详细介绍了Java中如何进行网络编程,包括通过Socket建立客户端与服务器的连接,使用BufferedReader和PrintWriter进行数据传输,以及编写简单客户端和服务器程序。此外,还探讨了多线程的概念,如何启动新线程,实现Runnable接口创建任务,以及线程同步和并发控制的基本原理。最后,通过一个简单的聊天软件程序实例展示了网络通信与多线程的应用。
摘要由CSDN通过智能技术生成

网络

客户端要解决的三个问题

  1. 建立客户端与服务器之间的初始连接:用户通过socket连接来连接服务器。
  2. 用户送出信息给服务器。
  3. 用户从服务器接受信息。

建立Socket连接

Socket是个代表两台机器之间网络连接的对象(java.net.Socket)。要创建Socket连接要知道两项关于服务器的信息:IP地址和端口号。
Socket连接的建立代表两台机器之间存有对方的信息,包括网络地址和TCP的端口号。

使用BufferedReader从Socket上读取数据

用串流来通过Socket连接来沟通。

  1. 建立对服务器的Socket连接

    Socket chatSocket = new Socket("127.0.0.1", 5000);
    
  2. 建立连接到Socket上底层输入串流的InputStreamReader

    InputStreamReader stream = new InputStreamReader(chatSocket.getInputStream());
    
  3. 建立BufferedReader来读取

    BufferedReader reader = new BufferedReader(stream);
    String message = read.readline();
    

用PrintWriter写数据到Socket上

  1. 对服务器建立Socket连接

    Socket chatSocket = new Socket("127.0.0.1", 5000);
    
  2. 建立链接到Socket的PrintWriter

    PrintWriter writer = new PrintWriter(chatSocket.getInputStream());
    
  3. 写入数据

    writer.println("message to send");
    writer.print("anther message");
    

简单的客户端程序

import java.io.*;
import java.net.*;

public class DailyAdviceClient {

    public void go() {
        try {
            Socket s = new Socket("127.0.0.1", 4242);

            InputStreamReader streamReader = new InputStreamReader(s.getInputStream());
            BufferedReader reader = new BufferedReader(streamReader);

            String advice = reader.readLine();
            System.out.println("Today you should:" + advice);

            reader.close();
        } catch (IOException ex) {
            ex.getStackTrace();
        }
    }

    public static void main(String[] args) {
        DailyAdviceClient client = new DailyAdviceClient();
        client.go();
    }

}

编写简单的服务器程序

import java.io.*;
import java.net.*;

public class DailyAdviceServer {
    String[] adviceList = { "go to school", "go home", "go to a moive" };

    public void go() {
        try {
            ServerSocket serverSock = new ServerSocket(4242);//ServerSocket会监听客户端对这台机器4242端口上的要求

            //服务器进入循环等待服务客户端的请求
            while (true) {

                Socket sock = serverSock.accept(); //这个方法等待用户的时候停下来闲置,客户链接时返回一个Socket对象(与serverSock不同的端口)以便与客户端通信

                PrintWriter writer = new PrintWriter(sock.getOutputStream());
                String advice = adviceList[(int) (Math.random() * adviceList.length)];
                writer.println(advice);
                writer.close();
            }
        } catch (IOException ex) {
        ex.getStackTrace();
        }
    }
    public static void main(String[] args) {
        DailyAdviceServer server = new DailyAdviceServer();
        server.go();
    }
}

多线程

线程是独立的线程。它代表独立的执行空间。
Thread是Java中用来表示线程的类。要建立线程就要创建Thread。

启动新的线程

  1. 建立Runnable对象(线程的任务)

    Runnable threadJob = new MyRunnable();
    
  2. 建立Thread对象(执行者)并赋值Runnable(任务)

    Thread myThread = new Thread(threadJob);
    

    把Runnable对象传给Thread的构造函数。这会告诉Thread对象要把哪个方法放在执行空间去运行(Runnable的run()方法)

  3. 启动Thread

    myThread.start();
    

实现Runnable接口来建立给thread运行的任务

public class MyRunnable implements Runnable {
    public void run() {//必须要实现的方法,线程的任务
        go();
    }

    public void go() {
        doMore();
    }

    public void doMore() {
        System.out.println("In the thread");
    }

}

public class ThreadTester {

    public static void main(String[] args) {

        MyRunnable threadJob = new MyRunnable();
        Thread myThread = new Thread(threadJob);//将Runnable的实例传给Thread的构造函数

        myThread.start();//线程开始执行

        System.out.println("back in main");

    }

}

小结

  • 以小写t描述的thread是个独立的线程
  • Java中的每个线程都有独立的执行空间
  • 大写T的Thread是java.lang.Thread这个类。他的对象是用来表示线程
  • Thread需要任务,任务是实现过Runnable的实例
  • Runnable接口只有一个方法
  • run()会是新线程所执行的第一项方法
  • 要把Runnable传给Thread的构造函数才能启动新线程
  • 线程在初始化以后还没有调用start()之前处于新建立的状态
  • 调用Thread对象的start()之后,会建立出新的执行空间,它处于可执行状态等待被挑出来执行
  • 当Java虚拟机的调度器选择某个线程之后它就处于执行中的状态,单处理器的机器只能有一个执行中的线程
  • 有时线程会因为某些原因而被堵塞
  • 调度不能保证任何的执行时间和顺序,所以你不能期待它会完全的平均分配执行。

多线程

  • Thread.sleep()这个静态方法可以强制线程进入等待状态到过了设定的时间为止。
  • sleep()方法可能会抛出InterruptedException异常。
  • 可以用setName()方法帮线程命名,通常用来除错。
  • 如果两个以上的线程存取堆上相同的对象可能会出现严重的问题(数据的损毁)。
  • 要让对象在线程上有足够的安全性,就要判断出那些指令不能被分割执行。
  • 使用synchronized这个关键词修饰符可以防止两个线程同时进入同一对象的同一方法。
  • 每个对象都有单一的锁,单一的钥匙。这只会在对象带有同步化放方法时才有实际用途。
  • 线程尝试进入同步化过的方法时必须要取得对象的钥匙,如果已经被别的线程拿走了,那就得等。
  • 对象就算是有多个同步化过的方法,也还是只有一个锁。一旦某个线程进入该对象的同步化方法,其他线程就无法进入该对象的任何同步化方法。

简单的聊天软件程序

客户端

import java.io.*;
import java.net.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class SimpleChatClient {

    JTextArea incoming;
    JTextField outgoing;
    BufferedReader reader;
    PrintWriter writer;
    Socket sock;

    public static void main(String[] args) {
        SimpleChatClient client = new SimpleChatClient();
        client.go();
    }

    public void go() {

        JFrame frame = new JFrame("Chat Client");
        JPanel mainPanel = new JPanel();
        incoming = new JTextArea(15,50);
        incoming.setLineWrap(true);
        incoming.setWrapStyleWord(true);
        incoming.setEditable(false);

        JScrollPane qScroller = new JScrollPane(incoming);
        qScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        qScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

        outgoing = new JTextField(20);
        JButton sendButton = new JButton("Send");
        sendButton.addActionListener(new SendButtonListener());

        mainPanel.add(qScroller);
        mainPanel.add(outgoing);
        mainPanel.add(sendButton);

        setUpNetworking();

        Thread readerThread = new Thread(new IncomingReader());
        readerThread.start();

        frame.getContentPane().add(BorderLayout.CENTER, mainPanel);
        frame.setSize(400, 500);
        frame.setVisible(true);
    }

    private void setUpNetworking() {

        try {
            sock = new Socket("127.0.0.1", 4242);
            InputStreamReader streamReader = new InputStreamReader(sock.getInputStream());
            reader = new BufferedReader(streamReader);
            writer = new PrintWriter(sock.getOutputStream());
            System.out.println("networking established");
        } catch (IOException ex) {
            ex.printStackTrace();
        }       
    }

    public class SendButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent ev) {
            try {
                writer.println(outgoing.getText());
                writer.flush();//强制将输出流缓冲区的数据送出
            } catch (Exception ex) {
                ex.printStackTrace();
            }

            outgoing.setText("");
            outgoing.requestFocus();
        }
    }

    public class IncomingReader implements Runnable {
        public void run() {//接收消息
            String message;
            try {

                while ((message = reader.readLine()) != null) {
                    System.out.println("Read " + message);
                    incoming.append(message + "\n");
                }
            } catch (Exception ex) {
                ex.getStackTrace();
            }
        }
    }
}

服务器

import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {

    ArrayList clientOutputStreams;

    public class ClientHandler implements Runnable {
        BufferedReader reader;
        Socket sock;

        public ClientHandler(Socket clientSocket) {
            try {
                sock = clientSocket;
                InputStreamReader isReader = new InputStreamReader(sock.getInputStream());
                reader = new BufferedReader(isReader);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        public void run() {
            String message;
            try {
                while ((message = reader.readLine()) != null) {//发送消息
                    System.out.println("Read " + message);
                    tellEveryone(message);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new ChatServer().go();

    }

    public void go() {
        clientOutputStreams = new ArrayList();
        try {
            ServerSocket serverSock = new ServerSocket(4242);

            while (true) {//主线程接收消息,并将消息加入发送队列

                Socket clientSock = serverSock.accept();
                PrintWriter writer = new PrintWriter(clientSock.getOutputStream());
                clientOutputStreams.add(writer);

                Thread t = new Thread(new ClientHandler(clientSock));
                t.start();
                System.out.println("Got a connection");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void tellEveryone(String message) {

        Iterator it = clientOutputStreams.iterator();//迭代器
        while (it.hasNext()) {
            try {
                PrintWriter writer = (PrintWriter) it.next();
                writer.println(message);
                writer.flush();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值