由简入繁阐述socket 网络编程

来源:互联网 发布:心理测试 软件 编辑:程序博客网 时间:2024/06/18 02:30

前言

本文从最简单的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编程详细介绍