由简入繁阐述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 编程的美妙之处.
至繁归于至简,复杂的东西简单化了,也就清晰明了了.
参考文献
- 循序渐进Java Socket网络编程(多客户端、信息共享、文件传输)
- Internet-Programming-with-Java-Book
- Client-Server-Client communication using Sockets
- python socket编程详细介绍
阅读全文
0 0
- 由简入繁阐述socket 网络编程
- 由简入繁阐述单例模式
- 网络编程---Socket编程
- 网络编程:Socket编程
- 网络编程(Socket编程)
- Linux网络编程之socket:由一个进程发起多个连接
- 网络socket编程指南
- 网络socket编程指南
- 网络Socket编程
- 网络socket编程指南
- 网络socket编程指南
- 网络socket编程指南
- 网络socket编程指南
- 网络编程 socket
- 网络编程 socket
- 网络socket编程指南
- 网络socket编程指南
- 网络socket编程指南
- C# 访问修饰符internal的访问范围误区释疑
- 文件复制
- easyUI tree 树形json的递归转换
- PS技能
- Ubuntu 16.04安装java
- 由简入繁阐述socket 网络编程
- linux体系结构+linux内核结构+linux内核目录结构
- Largest Number
- 剑指offer——合并两个排序的链表__
- 搬运工整理之HoloLens你的一个HoloLens应用程序 02
- lucene操作
- TensorFlow在Windows环境下的搭建
- codeforces811C——Vladik and Memorable Trip(动态规划)
- spring 事物监听机制,同步异步处理