一、网络编程是什么?
在网络通信协议下,不同计算机上运行的程序,进行数据传输。
- 应用场景:即时通讯、网游对战、金融证券、国际贸易、邮件、等等。
不管是什么场景,都是计算机与计算机之间通过网络进行数据传输。
- 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监听端口port
serverSocket = new ServerSocket(port);
//设置等待时间:10000毫秒没有监听到Socket连接该端口就报错
serverSocket.setSoTimeout(10000);
}
public void run(){
try{
//ServerSocket等待连接,链接成功就创建一个与客户端对等通信的socket
Socket 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");
}
}
}