一、网络编程是什么?
在网络通信协议下,不同计算机上运行的程序,进行数据传输。
- 应用场景:即时通讯、网游对战、金融证券、国际贸易、邮件、等等。
不管是什么场景,都是计算机与计算机之间通过网络进行数据传输。
- Java中可以使用
java.net
包下的技术轻松开发出常见的网络应用程序。
二、常见的软件架构?
-
常见的软件架构有哪些?
CS/BS。 即Client/Server 和 Browser/Server模式
-
通信的软件架构CS/BS的各有什么优缺点和区别?
CS:客户端服务端模式都需要开发客户端
BS:浏览器服务器模式不需要开发客户端
CS:适合定制专业化的办公类软件:IDEA、网游
BS:适合移动互联网应用,可以在任何地方随时访问系统
三、网络编程三要素?
- IP:设备在网络中的地址,是唯一的标识
- 端口号:应用程序在设备中唯一标识
- 协议:数据在网络中传输的规则,常见的协议游UDP、TCP、HTTP、HTTPS、FTP
四、Socket编程(Java)
Socket
(套接字)使用TCP提供了两台计算机之间的通信机制。客户端程序创建一个套接字,并尝试连接服务器的套接字。当连接建立的时候,服务器会创建一个Socket对象。客户端和服务器可以通过对Socket对象写入和读取来进行通信。
java.net.Socket
类代表一个套接字,并且java.net.ServerSocket
类为服务器程序提供了一种监听客户端,并与他们建立连接的机制。
以下步骤在两台计算机之间使用Socket建立TCP连接出现:
- 服务器实例化一个
ServerSocket
对象,表示通过服务器端口通信。(ServerSocket本质就是监听端口等待Socket对象连接) - 服务器调用
ServerSocket
类的accept()
方法,该方法将一直等待,直到一个客户端连接到服务器上给定的端口。 - 服务器
ServerSocket
监听等待连接的过程中,客户端创建一个Socket
对象,并指定该Socket要连接到的服务器的名称和端口。 Socket
类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。- 在服务器端,
accept()
方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
- 连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个
输出流
和一个输入流
,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。- TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送。
五、ServerSocket类的方法
服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。
ServerSocket 类有四个构造方法:
序号 | 方法描述 |
---|---|
1 | public ServerSocket(int port) throws IOException 创建监听特定端口的服务器套接字。 |
2 | public ServerSocket(int port, int backlog) throws IOException 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
3 | public ServerSocket(int port, int backlog, InetAddress address) throws IOException 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
4 | public ServerSocket() throws IOException 创建非绑定服务器套接字。 |
创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。
这里有一些 ServerSocket 类的常用方法:
序号 | 方法描述 |
---|---|
1 | public int getLocalPort() 返回此套接字在其上侦听的端口。 |
2 | public Socket accept() throws IOException 侦听并接受到此套接字的连接。 |
3 | public void setSoTimeout(int timeout) 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 |
4 | public void bind(SocketAddress host, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 |
六、Socket 类的方法
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。
Socket 类有五个构造方法.
序号 | 方法描述 |
---|---|
1 | public Socket(String host, int port) throws UnknownHostException, IOException. 创建一个流套接字并将其连接到指定主机上的指定端口号。 |
2 | public Socket(InetAddress host, int port) throws IOException 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
3 | public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. 创建一个套接字并将其连接到指定远程主机上的指定远程端口。 |
4 | public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
5 | public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字 |
当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。
下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。
序号 | 方法描述 |
---|---|
1 | public void connect(SocketAddress host, int timeout) throws IOException 将此套接字连接到服务器,并指定一个超时值。 |
2 | public InetAddress getInetAddress() 返回套接字连接的地址。 |
3 | public int getPort() 返回此套接字连接到的远程端口。 |
4 | public int getLocalPort() 返回此套接字绑定到的本地端口。 |
5 | public SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点的地址,如果未连接则返回 null。 |
6 | public InputStream getInputStream() throws IOException 返回此套接字的输入流。 |
7 | public OutputStream getOutputStream() throws IOException 返回此套接字的输出流。 |
8 | public void close() throws IOException 关闭此套接字。 |
例子(一):Socket 实例
1. 服务端
如下的 MySocketServer 是一个服务端程序,该程序通过 socket 连接到服务器并发送一个请求,然后等待一个响应。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;public class MySocketServer extends Thread{private ServerSocket serverSocket;public MySocketServer(int port)throws IOException {//创建ServerSocket监听端口portserverSocket = new ServerSocket(port);//设置等待时间:10000毫秒没有监听到Socket连接该端口就报错serverSocket.setSoTimeout(10000);}public void run(){try{//ServerSocket等待连接,链接成功就创建一个与客户端对等通信的socketSocket server = serverSocket.accept();System.out.println("客户"+server.getLocalAddress()+"连接成功");//获取Socket的输入流输出流DataInputStream inputStream = new DataInputStream(server.getInputStream());DataOutputStream outputStream = new DataOutputStream(server.getOutputStream());while(true){//从流中获取信息String msg = inputStream.readUTF();//当客户端传递的信息为ends时,结束。if(msg.equals("ends")) break;//服务端显示流中的信息System.out.println(msg);}server.close();}catch(Exception ex){ex.printStackTrace();}}public static void main(String[] args){Scanner scanner = new Scanner(System.in);try{System.out.println("请输入服务端绑定端口:");//运行线程让服务端异步运行这样可以让主程序继续干自己的事Thread t = new MySocketServer(scanner.nextInt());t.run();}catch(Exception ex){ex.printStackTrace();}}
}
2. 客户端
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class MySocketClient{public static void main(String[] args){try{Scanner keyboard = new Scanner(System.in);System.out.println("请输入连接主机的IP地址:");String host = keyboard.nextLine();System.out.println("输入主机"+host+" 的端口号:");int port = keyboard.nextInt();//创建Socket并尝试连接 IP=host && 端口=port的服务端Socket client = new Socket(host,port);InputStream in = client.getInputStream();OutputStream out = client.getOutputStream();DataInputStream inputStream = new DataInputStream(in);DataOutputStream outputStream = new DataOutputStream(out);while(true){String msg = keyboard.nextLine();outputStream.writeUTF(msg);//当输入了ends时,客户端关闭socket,服务端也关闭socket,二者结束通信。if(msg.equals("ends"))break;}client.close();}catch(Exception ex){ex.printStackTrace();};}
}
3. 测试结果
例子(二):聊天室
1. 服务端
- 服务端使用while循环的添加Socket用户
- 每个用户有自己独立的线程(异步性:使各个用户可以同时输出输入的同时服务端能够继续监听端口)
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;public class ChatRoomServer{private static ServerSocket serverSocket;private static ArrayList<Socket> clientList = new ArrayList<Socket>();public static void main(String[] args){try{serverSocket = new ServerSocket(8888);while(true){Socket client = serverSocket.accept();clientList.add(client);//TODO:开启客户端线程,进行异步聊天ClientThread ct = new ClientThread(client,clientList);ct.start();}}catch(Exception ex){ex.printStackTrace();}finally {try{if(serverSocket != null)serverSocket.close();}catch(Exception ex){ex.printStackTrace();}}}
}class ClientThread extends Thread{private Socket client = null;private ArrayList<Socket> clientList;public ClientThread(Socket s,ArrayList<Socket>ss){client = s;clientList = ss;}public void run(){DataInputStream input = null;DataOutputStream output = null;try{input = new DataInputStream(client.getInputStream());String rec = null;String send = null;while(true){if(!client.isClosed()){rec = input.readUTF();System.out.println("服务端接收到数据:"+rec);clientList.trimToSize();String[] param = rec.split("&");//将输入进行一些封装if("$start$".equals(param[1])){send = param[0] + "进入聊天室";}else{send = param[0] + "说: " + param[1];}//将非取消信号的数据发送出去if(!("$ends$".equals(param[1]))){for(Socket socket : clientList){if(!socket.isClosed()){output = new DataOutputStream(socket.getOutputStream());output.writeUTF(send);}}}else{for(Socket socket : clientList){if(socket!= client && !socket.isClosed()){output = new DataOutputStream(socket.getOutputStream());output.writeUTF(param[0]+"已退出聊天室");}}output = new DataOutputStream(client.getOutputStream());output.writeUTF("$ends$");client.close();input.close();output.close();}}}}catch(Exception ex){ex.printStackTrace();}}
}
2. 客户端
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;public class ChatRoomClient {public static final String ip = "127.0.0.1";public static final int port = 8888;public Socket socket = null;public DataInputStream input = null;public DataOutputStream output = null;public Scanner keyboard = new Scanner(System.in);public String send;public String name;public void start(){try{System.out.println(" ################ 欢迎进入Socket聊天室 ################ ");System.out.println("输入您在聊天室的昵称: ");name = keyboard.nextLine();socket = new Socket(ip,port);input = new DataInputStream(socket.getInputStream());output = new DataOutputStream(socket.getOutputStream());send = name + "&$start$";System.out.println(" ################ 进入聊天室成功 ################ ");System.out.println("如需退出聊天室,输入'$ends$'即可....");output.writeUTF(send);//TODO: 编写聊天的线程MsgThread mt = new MsgThread(output,name,input);mt.start();while(true){String rec = input.readUTF();if("$ends$".equals(rec)){System.out.println(" ################ 退出聊天室成功 ################ ");input.close();output.close();socket.close();System.exit(0);}else{System.out.println(rec);}}}catch(Exception ex){ex.printStackTrace();}finally {try{if(socket!= null){socket.close();input.close();output.close();}}catch (Exception ex){ex.printStackTrace();}}}public static void main(String[] args){ChatRoomClient client = new ChatRoomClient();client.start();}
}
class MsgThread extends Thread{private DataInputStream input;private DataOutputStream output;private Scanner keyboard = new Scanner(System.in);public static String name;public MsgThread(DataOutputStream o,String n,DataInputStream i){output = o;input = i;name = n;}public void run(){ChatRoomClient client = new ChatRoomClient();try{while(true){String send = name+"&" + keyboard.nextLine();output.writeUTF(send);}}catch(Exception ex){ex.printStackTrace();}finally {System.out.println("sfef");}}
}