由简入繁阐述socket 网络编程

前言

本文从最简单的socket网络编程开始,逐渐深入,扩展到复杂情况下网络通信.

一. 单客户端

一个服务器,一个客户端.

服务器端

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

public class DateServer {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(2017);
        while (true){
            Socket socket = serverSocket.accept();
            OutputStreamWriter outputStreamWriter = 
                        new OutputStreamWriter(socket.getOutputStream()); 
            outputStreamWriter.write(new Date()+"\n");
            outputStreamWriter.close();
            socket.close();
        }
    }
}

客户端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class DateClient {

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 2017);
        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
        System.out.println("The date on the server is: "+
                bufferedReader.readLine());
        socket.close();
    }
}

二. 多客户端

多个客户端与服务器通信,服务器为每个客户端开辟一个线程.

服务端


import java.io.*; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.Date; 

public class DictionaryServer { 
    public static int LISTENING_PORT = 2017; 

    public static void main(String[] args) throws IOException { 
        ServerSocket serverSocket = 
            new ServerSocket(LISTENING_PORT);
     System.out.println("Server started."); 
        while (true) { 
            Socket socket = serverSocket.accept(); 
            DictionaryClientThread dictionaryClientThread = 
                new DictionaryClientThread(socket); 
            dictionaryClientThread.start(); 
        } 
    } 
} 

class DictionaryClientThread extends Thread { 
    private int CLIENT_REQUEST_TIMEOUT = 15*60*1000; // 15 min. 
    private Socket mSocket; 
    private BufferedReader mSocketReader; 
    private PrintWriter mSocketWriter; 

    public DictionaryClientThread(Socket aSocket) 
            throws IOException { 
        mSocket = aSocket; 
        mSocket.setSoTimeout(CLIENT_REQUEST_TIMEOUT); 
        mSocketReader = new BufferedReader( 
            new InputStreamReader(mSocket.getInputStream())); 
        mSocketWriter = new PrintWriter( 
            new OutputStreamWriter(mSocket.getOutputStream())); 
    } 

    public void run() { 
        System.out.println(new Date().toString() + " : " + 
            "Accepted client : " + mSocket.getInetAddress() + 
            ":" + mSocket.getPort()); 
        try { 
            mSocketWriter.println("Dictionary server ready."); 
            mSocketWriter.flush(); 
            while (!isInterrupted()) { 
                String word = mSocketReader.readLine(); 
                if (word == null) 
                    break; // Client closed the socket 
                String translation = getTranslation(word); 
                mSocketWriter.println(translation); 
                mSocketWriter.flush(); 
            } 
        } catch (Exception ex) { 
            ex.printStackTrace(); 
        } 
        System.out.println(new Date().toString() + " : " + 
            "Connection lost : " + mSocket.getInetAddress() + 
            ":" + mSocket.getPort()); 
    } 

    private String getTranslation(String aWord) { 
        if (aWord.equalsIgnoreCase("network")) { 
            return "网络"; 
        } else if (aWord.equalsIgnoreCase("firewall")) { 
            return "防火墙"; 
        } else { 
            return "未知单词"; 
        } 
    } 
}

客户端

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

public class DictionaryClient { 
    private static int SERVER_RESPONSE_TIMEOUT = 60*1000; 
    public static void main(String[] args) throws IOException { 
        Socket socket = new Socket("localhost", 2017); 
        socket.setSoTimeout(SERVER_RESPONSE_TIMEOUT); 
        BufferedReader socketReader = new BufferedReader( 
            new InputStreamReader(socket.getInputStream()) ); 
        PrintWriter socketWriter = 
            new PrintWriter(socket.getOutputStream()); 
        BufferedReader consoleReader = new BufferedReader( 
            new InputStreamReader(System.in) ); 
        String welcomeMessage = socketReader.readLine(); 
        System.out.println(welcomeMessage); 
        try { 
            while (true) { 
                String word = consoleReader.readLine(); 
                socketWriter.println(word); 
                socketWriter.flush(); 
                String translation = socketReader.readLine(); 
                System.out.println(translation); 
            } 
        } finally { 
            socket.close(); 
        } 
    } 
}

三. 群聊模式

服务器同步接受各个客户端的消息并且将消息分发至各个客户端.

这里写图片描述

服务器端

/** 
 * 
 * Nakov Chat Server is multithreaded chat server. It accepts 
 * multiple clients simultaneously and serves them. Clients are 
 * able to send messages to the server. When some client sends 
 * a message to the server, the message is dispatched to all 
 * the clients connected to the server. 
 * 
 * The server consists of two components - "server core" and 
 * "client handlers". 
 * 
 * The "server core" consists of two threads: 
 *   - NakovChatServer - accepts client connections, creates 
 * client threads to handle them and starts these threads 
 *   - ServerDispatcher - waits for messages and when some 
 * message arrive sends it to all the clients connected to 
 * the server 
 * 
 * The "client handlers" consist of two threads: 
 *   - ClientListener - listens for message arrivals from the 
 * socket and forwards them to the ServerDispatcher thread 
 *   - ClientSender - sends messages to the client 
 * 
 * For each accepted client, a ClientListener and ClientSender 
 * threads are created and started. A Client object is also 
 * created to maintain the information about the client and is 
 * added to the ServerDispatcher's clients list. When some 
 * client is disconnected, is it removed from the clients list 
 * and both its ClientListener and ClientSender threads are 
 * interrupted. 
 */ 

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

/** 
 * NakovChatServer class is the entry point for the server. 
 * It opens a server socket, starts the dispatcher thread and 
 * infinitely accepts client connections, creates threads for 
 * handling them and starts these threads. 
 */ 
public class NakovChatServer { 
    public static final int LISTENING_PORT = 2017; 
    public static String KEEP_ALIVE_MESSAGE = "!keep-alive"; 
    public static int CLIENT_READ_TIMEOUT = 5*60*1000; 
    private static ServerSocket mServerSocket; 

    private static ServerDispatcher mServerDispatcher; 

    public static void main(String[] args) { 
        // Start listening on the server socket 
        bindServerSocket(); 

        // Start the ServerDispatcher thread 
        mServerDispatcher = new ServerDispatcher(); 
        mServerDispatcher.start(); 

        // Infinitely accept and handle client connections 
        handleClientConnections(); 
    } 

    private static void bindServerSocket() { 
        try { 
            mServerSocket = new ServerSocket(LISTENING_PORT); 
            System.out.println("NakovChatServer started on " + 
                "port " + LISTENING_PORT); 
        } catch (IOException ioe) { 
            System.err.println("Can not start listening on " + 
                "port " + LISTENING_PORT); 
            ioe.printStackTrace(); 
            System.exit(-1); 
        } 
    } 

    private static void handleClientConnections() { 
        while (true) { 
            try { 
                Socket socket = mServerSocket.accept(); 
                Client client = new Client(); 
                client.mSocket = socket; 
                ClientListenerr clientListener = new 
                        ClientListenerr(client, mServerDispatcher); 
                ClientSender clientSender = 
                    new ClientSender(client, mServerDispatcher); 
                client.mClientListener = clientListener; 
                clientListener.start(); 
                client.mClientSender = clientSender; 
                clientSender.start(); 
                mServerDispatcher.addClient(client); 
            } catch (IOException ioe) { 
                ioe.printStackTrace(); 
            } 
        } 
    } 
} 


/** 
 * ServerDispatcher class is purposed to listen for messages 
 * received from the clients and to dispatch them to all the 
 * clients connected to the chat server. 
 */ 
class ServerDispatcher extends Thread { 
    private Vector mMessageQueue = new Vector(); 
    private Vector mClients = new Vector(); 

    /** 
     * Adds given client to the server's client list. 
     */ 
    public synchronized void addClient(Client aClient) { 
        mClients.add(aClient); 
    } 

    /** 
     * Deletes given client from the server's client list if 
     * the client is in the list. 
     */ 
    public synchronized void deleteClient(Client aClient) { 
        int clientIndex = mClients.indexOf(aClient); 
        if (clientIndex != -1) 
            mClients.removeElementAt(clientIndex); 
    } 

    /** 
     * Adds given message to the dispatcher's message queue and 
     * notifies this thread to wake up the message queue reader 
     * (getNextMessageFromQueue method). dispatchMessage method 
     * is called by other threads (ClientListener) when a 
     * message is arrived. 
     */ 
    public synchronized void dispatchMessage( 
            Client aClient, String aMessage) { 
        Socket socket = aClient.mSocket; 
        String senderIP = 
            socket.getInetAddress().getHostAddress(); 
        String senderPort = "" + socket.getPort(); 
        aMessage = senderIP + ":" + senderPort + 
            " : " + aMessage; 
        mMessageQueue.add(aMessage); 
        notify(); 
    } 

    /** 
     * @return and deletes the next message from the message 
     * queue. If there is no messages in the queue, falls in 
     * sleep until notified by dispatchMessage method. 
     */ 
    private synchronized String getNextMessageFromQueue() 
    throws InterruptedException { 
        while (mMessageQueue.size()==0) 
            wait(); 
        String message = (String) mMessageQueue.get(0); 
        mMessageQueue.removeElementAt(0); 
        return message; 
    } 

    /** 
     * Sends given message to all clients in the client list. 
     * Actually the message is added to the client sender 
     * thread's message queue and this client sender thread 
     * is notified to process it. 
     */ 
    private void sendMessageToAllClients( 
            String aMessage) { 
        for (int i=0; i<mClients.size(); i++) { 
            Client client = (Client) mClients.get(i); 
            client.mClientSender.sendMessage(aMessage); 
        } 
    } 

    /** 
     * Infinitely reads messages from the queue and dispatches 
     * them to all clients connected to the server. 
     */ 
    public void run() { 
        try { 
            while (true) { 
                String message = getNextMessageFromQueue(); 
                sendMessageToAllClients(message); 
            } 
        } catch (InterruptedException ie) { 
            // Thread interrupted. Stop its execution 
        } 
    } 
} 


/** 
 * Client class contains information about a client, 
 * connected to the server. 
 */ 
class Client { 
    public Socket mSocket = null; 
    public ClientListenerr mClientListener = null; 
    public ClientSender mClientSender = null; 
} 


/** 
 * ClientListener class listens for client messages and 
 * forwards them to ServerDispatcher. 
 */ 
class ClientListenerr extends Thread { 
    private ServerDispatcher mServerDispatcher; 
    private Client mClient; 
    private BufferedReader mSocketReader; 

    public ClientListenerr(Client aClient, ServerDispatcher 
            aSrvDispatcher) throws IOException { 
        mClient = aClient; 
        mServerDispatcher = aSrvDispatcher; 
        Socket socket = aClient.mSocket; 
        socket.setSoTimeout( 
            NakovChatServer.CLIENT_READ_TIMEOUT); 
        mSocketReader = new BufferedReader( 
            new InputStreamReader(socket.getInputStream()) ); 
    } 

    /** 
     * Until interrupted, reads messages from the client 
     * socket, forwards them to the server dispatcher's 
     * queue and notifies the server dispatcher. 
     */ 
    public void run() { 
        try { 
            while (!isInterrupted()) { 
                try { 
                    String message = mSocketReader.readLine(); 
                    if (message == null) 
                        break; 
                    mServerDispatcher.dispatchMessage( 
                        mClient, message); 
                } catch (SocketTimeoutException ste) { 
                    mClient.mClientSender.sendKeepAlive(); 
                } 
            } 
        } catch (IOException ioex) { 
            // Problem reading from socket (broken connection) 
        } 

        // Communication is broken. Interrupt both listener and 
        // sender threads 
        mClient.mClientSender.interrupt(); 
        mServerDispatcher.deleteClient(mClient); 
    } 
} 

/** 
 * Sends messages to the client. Messages waiting to be sent 
 * are stored in a message queue. When the queue is empty, 
 * ClientSender falls in sleep until a new message is arrived 
 * in the queue. When the queue is not empty, ClientSender 
 * sends the messages from the queue to the client socket. 
 */ 
class ClientSender extends Thread { 
    private Vector mMessageQueue = new Vector(); 

    private ServerDispatcher mServerDispatcher; 
    private Client mClient; 
    private PrintWriter mOut; 

    public ClientSender(Client aClient, ServerDispatcher 
            aServerDispatcher) throws IOException { 
        mClient = aClient; 
        mServerDispatcher = aServerDispatcher; 
        Socket socket = aClient.mSocket; 
        mOut = new PrintWriter( 
            new OutputStreamWriter(socket.getOutputStream()) ); 
    } 

    /** 
     * Adds given message to the message queue and notifies 
     * this thread (actually getNextMessageFromQueue method) 
     * that a message is arrived. sendMessage is always called 
     * by other threads (ServerDispatcher). 
     */ 
    public synchronized void sendMessage(String aMessage) { 
        mMessageQueue.add(aMessage); 
        notify(); 
    } 

    /** 
     * Sends a keep-alive message to the client to check if 
     * it is still alive. This method is called when the client 
     * is inactive too long to prevent serving dead clients. 
     */ 
    public void sendKeepAlive() { 
        sendMessage(NakovChatServer.KEEP_ALIVE_MESSAGE); 
    } 

    /** 
     * @return and deletes the next message from the message 
     * queue. If the queue is empty, falls in sleep until 
     * notified for message arrival by sendMessage method. 
     */ 
    private synchronized String getNextMessageFromQueue() 
            throws InterruptedException { 
        while (mMessageQueue.size()==0) 
            wait(); 
        String message = (String) mMessageQueue.get(0); 
        mMessageQueue.removeElementAt(0); 
        return message; 
    } 

    /** 
     * Sends given message to the client's socket. 
     */ 
    private void sendMessageToClient(String aMessage) { 
        mOut.println(aMessage); 
        mOut.flush(); 
    } 

    /** 
     * Until interrupted, reads messages from the message queue 
     * and sends them to the client's socket. 
     */ 
    public void run() { 
        try { 
            while (!isInterrupted()) { 
                String message = getNextMessageFromQueue(); 
                sendMessageToClient(message); 
            } 
        } catch (Exception e) { 
            // Commuication problem 
        } 

        // Communication is broken. Interrupt both listener 
        // and sender threads 
        mClient.mClientListener.interrupt(); 
        mServerDispatcher.deleteClient(mClient); 
    } 
}

客户端


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

/** 
 * NakovChatClient is a client for Nakov Chat Server. After 
 * creating a socket connection to the chat server it starts 
 * two threads. The first one listens for data comming from 
 * the socket and transmits it to the console and the second 
 * one listens for data comming from the console and transmits 
 * it to the socket. After creating the two threads the main 
 * program's thread finishes its execution, but the two data 
 * transmitting threads stay running as long as the socket 
 * connection is not closed. When the socket connection is 
 * closed, the thread that reads it terminates the program 
 * execution. Keep-alive messages are ignored when received. 
 */ 
public class NakovChatClient { 
    public static final String SERVER_HOSTNAME = "localhost"; 
    public static String KEEP_ALIVE_MESSAGE = "!keep-alive"; 
    public static final int SERVER_PORT = 2017; 

    private static BufferedReader mSocketReader; 
    private static PrintWriter mSocketWriter; 

    public static void main(String[] args) { 
        // Connect to the chat server 
        try { 
            Socket socket = 
                new Socket(SERVER_HOSTNAME, SERVER_PORT); 
            mSocketReader = new BufferedReader(new 
                InputStreamReader(socket.getInputStream())); 
            mSocketWriter = new PrintWriter(new 
                OutputStreamWriter(socket.getOutputStream())); 
            System.out.println("Connected to server " + 
                    SERVER_HOSTNAME + ":" + SERVER_PORT); 
        } catch (IOException ioe) { 
            System.err.println("Can not connect to " + 
                SERVER_HOSTNAME + ":" + SERVER_PORT); 
            ioe.printStackTrace(); 
            System.exit(-1); 
        } 

        // Start socket --> console transmitter thread 
        PrintWriter consoleWriter = new PrintWriter(System.out); 
        TextDataTransmitter socketToConsoleTransmitter = new 
            TextDataTransmitter(mSocketReader, consoleWriter); 
        socketToConsoleTransmitter.setDaemon(false); 
        socketToConsoleTransmitter.start(); 

        // Start console --> socket transmitter thread 
        BufferedReader consoleReader = new BufferedReader( 
            new InputStreamReader(System.in)); 
        TextDataTransmitter consoleToSocketTransmitter = new 
            TextDataTransmitter(consoleReader, mSocketWriter); 
        consoleToSocketTransmitter.setDaemon(false); 
        consoleToSocketTransmitter.start(); 
    } 
} 

/** 
 * Transmits text data from the given reader to given writer 
 * and runs as a separete thread. 
 */ 
class TextDataTransmitter extends Thread { 
    private BufferedReader mReader; 
    private PrintWriter mWriter; 

    public TextDataTransmitter(BufferedReader aReader, 
            PrintWriter aWriter) { 
        mReader = aReader; 
        mWriter = aWriter; 
    } 

    /** 
     * Until interrupted reads a text line from the reader 
     * and sends it to the writer. 
     */ 
    public void run() { 
        try { 
            while (!isInterrupted()) { 
                String data = mReader.readLine(); 
                if (! data.equals(NakovChatClient. 
                        KEEP_ALIVE_MESSAGE)) { 
                    mWriter.println(data); 
                    mWriter.flush(); 
                } 
            } 
        } catch (IOException ioe) { 
            System.err.println("Lost connection to server."); 
            System.exit(-1); 
        } 
    } 
}

四. 文件传输

客户端向服务器端传送文件,服务端可获取文件名用于保存,获取文件大小计算传输进度.

服务器端


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

public class FileServer {
    public static int LISTENING_PORT = 2017; 

     public static void main(String[] args) throws IOException { 
            ServerSocket serverSocket = 
                new ServerSocket(LISTENING_PORT);
         System.out.println("Server started."); 
            while (true) { 
                Socket socket = serverSocket.accept(); 
                FileThread fileThread = 
                    new FileThread(socket); 
                fileThread.start(); 
            } 
    } 
} 

class FileThread extends Thread{
    private int CLIENT_REQUEST_TIMEOUT = 15*60*1000; // 15 min. 
    private Socket fSocket; 
    private DataInputStream dataInputStream;
    private FileOutputStream fileOutputStream;
    private String fileName;
    private long fileLength;


    public FileThread(Socket socket) throws IOException {
        fSocket = socket; 
        fSocket.setSoTimeout(CLIENT_REQUEST_TIMEOUT); 

        dataInputStream = new DataInputStream(fSocket.getInputStream());
        //文件名和长度
        fileName = dataInputStream.readUTF();
        fileLength = dataInputStream.readLong();
        fileOutputStream = new FileOutputStream(new File("E:/"+fileName));

    }

    @Override
    public void run(){
        System.out.println(new Date().toString() + " : " + 
                "Accepted client : " + fSocket.getInetAddress() + 
                ":" + fSocket.getPort());

        try {
            byte[] sendBytes = new byte[1024];
            int transLen = 0;  //当前传输文件大小
            System.out.println("--开始接收文件<" + fileName +">,文件大小为<" + fileLength +">--");
            while (!isInterrupted()) { 
                int read = 0;
                read = dataInputStream.read(sendBytes);
                if (read == -1){
                    break;
                }
                transLen += read;
                System.out.println("接收文件进度" +100 * transLen/fileLength +"%...");
                fileOutputStream.write(sendBytes,0,read);
                fileOutputStream.flush();
            } 
            System.out.println("--接收文件<" + fileName +">成功--");
            fSocket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (dataInputStream != null){
                    dataInputStream.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

}

客户端


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

public class FileClient {
    private static FileInputStream fileInputStream;
    private static DataOutputStream dataOutputStream;
    private static int SERVER_RESPONSE_TIMEOUT = 60*1000; //1h
    public static void main(String[] args) throws IOException { 
        Socket socket = new Socket("localhost", 2017); 
        socket.setSoTimeout(SERVER_RESPONSE_TIMEOUT); 
        try { 
           File file = new File("D:/test.doc"); 
           fileInputStream = new FileInputStream(file);
           dataOutputStream = new DataOutputStream(socket.getOutputStream());

           dataOutputStream.writeUTF(file.getName());
           dataOutputStream.flush();
           dataOutputStream.writeLong(file.length());
           dataOutputStream.flush();

           //传输文件
           byte[] sendBytes = new byte[1024];
           int read = 0;

           while((read = fileInputStream.read(sendBytes,0, sendBytes.length)) >0){
               dataOutputStream.write(sendBytes,0,read);
               dataOutputStream.flush();
           }
        }catch (Exception e) {
            e.printStackTrace();
        } finally { 
            try {
                if (dataOutputStream != null){
                    dataOutputStream.close();
                }
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
            socket.close(); 
        } 
    } 
}

总结

本文涵盖的主要内容有 单客户端,多客户端,文件传输,群聊模式,从socket单线程着手,逐渐深入,通过实验探索了socket 编程的美妙之处.

至繁归于至简,复杂的东西简单化了,也就清晰明了了.

参考文献

  1. 循序渐进Java Socket网络编程(多客户端、信息共享、文件传输)
  2. Internet-Programming-with-Java-Book
  3. Client-Server-Client communication using Sockets
  4. python socket编程详细介绍
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值