网络编程(二)TCP网络编程
1、概述
在TCP通信协议下,能实现两台计算机之间的数据交互,并且它们要严格区分客户端(Client)与服务端(Server)
客户端和服务端通信的步骤: (1)服务端先进行启动,并占用一个指定的端口号,等待客户端的连接。
(2)客户端主动发起服务端的连接,在连接成功之后,就可以进行数据发送。
服务端不能主动连接客户端,必须由客户端先行发起连接才行
在java中,对于这样基于TCP协议下连接通信的客户端和服务端,分别进行了抽象:
java.net.Socket类表示客户端
java.net.ServerSocket类表示服务端
使用Socket和ServerSocket进行的编程,也称为套接字编程
客户端
java.net.Socket类表示客户端
public class Socket{
public Socket(String host, int port){
}
public InputStream getInputStream() throws IOException {
}
public OutputStream getOutputStream() throws IOException {
}
public synchronized void close() throws IOException {
}
public void shutdownOutput() throws IOException{
}
public void shutdownInput() throws IOException{
}
}
服务器端
java.net.ServerSocket类表示服务端
public class ServerSocket{
public ServerSocket(int port) throws IOException {
}
public Socket accept() throws IOException {
}
}
2、客户端与服务器端交互
普通交互
测试一:客户端和服务端进行连接
客户端 步骤: (1)IP地址和端口号 (2)声明Socket (3)创键Socket对象 (4)中间操作 (5)关闭资源
import java.io.IOException;
import java.net.Socket;
public class ClientTCP {
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 8888;
Socket socket = null;
try {
socket = new Socket(host, port);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器端: 步骤: (1)声明端口号 (2)声明ServerSocket和Socket (3)创建服务器对象,传入端口号 (4)accept方法阻塞线程,等待客户端的连接 (5)中间操作 (6)关闭资源
(2)(3)步骤可以合并在一起写,如, ServerSocket server = new ServerSocket(); 但此时要注意如果使用try。。catch。。。捕获异常的话,关闭资源时就比较麻烦,因为对于Socket和ServerSocket的声明都在try。。。catch。。括号里面,使用范围也就只在这对括号里面,可以选择改为抛出异常
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTCP {
public static void main(String[] args) {
int port = 8888;
ServerSocket server = null;
Socket socket = null;
try {
server = new ServerSocket(port);
System.out.println("服务器启动,监听端口" + port + "等待客户端的连接");
socket = server.accept();
System.out.println("服务器接收到客户端的连接:" + socket);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意:先启动服务器端,再启动客户端
运行服务器端后 再运行客户端:
这里的输出窗口都是服务器端的,客户端的窗口没有输出
测试二:连接出成功后,客户端向服务器发送信息,服务器接收信息
客户端
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientTCP {
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 8888;
String encoding = "UTF-8";
Socket socket = null;
PrintWriter pw = null;
try {
socket = new Socket(host,port);
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
pw.println("你好...我是客户端Client!");
pw.flush();
}catch(IOException e){
e.printStackTrace();
}finally {
pw.close();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务器端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTCP {
public static void main(String[] args) {
int port = 8888;
ServerSocket server = null;
Socket socket = null;
String encoding = "UTF-8";
BufferedReader br = null;
try {
server = new ServerSocket(port);
System.out.println("等待来自客户端的连接...");
socket = server.accept();
System.out.println("连接到客户端:" + socket);
br = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
String readLine = br.readLine();
System.out.println("服务器接收到来自客户端的信息:" + readLine);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br!=null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器端窗口显示如下:
测试三:连接成功后,客户端向服务器发送信息,服务器接收信息,并且写回数据给客户端,客户端再接收
客户端代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientTCP {
public static void main(String[] args) {
int port = 8888;
String host = "127.0.0.1";
String encoding = "UTF-8";
Socket socket = null;
PrintWriter pw = null;
BufferedReader br = null;
try {
socket = new Socket(host,port);
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
pw.println("你好...这条消息来自客户端...");
pw.flush();
br = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
String line = br.readLine();
System.out.println("接收到服务器端回应的消息:" + line);
} catch(Exception e) {
e.printStackTrace();
}finally {
pw.close();
try {
br.close();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务器端代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTCP {
public static void main(String[] args) {
int port = 8888;
String encoding = "UTF-8";
Socket socket = null;
ServerSocket server = null;
BufferedReader br = null;
PrintWriter pw = null;
try {
server = new ServerSocket(port);
System.out.println("服务器端等待客户端...");
socket = server.accept();
System.out.println("服务器端收到来自客户端的连接请求...");
br = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
String line = br.readLine();
System.out.println("服务器端收到来自客户端的信息:" + line);
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
pw.println("你好客户端,这条信息来自服务器端!");
pw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(pw!= null) {
pw.close();
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server != null){
try{
server.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结果: (1)启动服务器端
(2)启动客户端 在这期间,客户端向服务器端发送了消息(再去看服务器端窗口) 并且服务器端也向客户端发送了消息(打开客户端窗口)
测试四:客户端可以从控制台接收用户的输入内容,然后再写给服务器,然后再接收服务器写回来的数据
客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientTCP {
public static void main(String[] args) {
int port = 8888;
String host = "127.0.0.1";
String encoding = "UTF-8";
Socket socket = null;
BufferedReader bir = null;
PrintWriter pw = null;
try {
socket = new Socket(host,port);
bir = new BufferedReader(new InputStreamReader(System.in,encoding));
String fromConsole = bir.readLine();
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
pw.println(fromConsole);
pw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bir!=null) {
try {
bir.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(pw != null) {
pw.close();
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTCP {
public static void main(String[] args) {
int port = 8888;
String encoding = "UTF-8";
Socket socket = null;
ServerSocket server = null;
BufferedReader br = null;
PrintWriter pw = null;
try {
server = new ServerSocket(port);
System.out.println("服务启动...监听端口" + port + ",等待客户端的连接");
socket = server.accept();
System.out.println("连接到了客户端:" + socket);
br = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
String fromClient = br.readLine();
System.out.println("服务器接收到客户端的信息:" + fromClient);
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
pw.println("欢迎访问本服务器!");
pw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(pw != null) {
pw.close();
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server != null){
try{
server.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结果如下: (1)启动服务器端 (2)启动客户端 此时服务器端收到了客户端发送过来建立连接的信息 接下来在客户端窗口输入要发给服务端的信息 服务器端收到了来自客户端控制台发送的信息
进阶交互
测试一:服务端启动后,接收一个客户端的多次连接(一个客户端,一个服务器,客户端不断发信息给服务端)
。。。就是测试4加上了个循环实现。。。
客户端
public static void main(String[] args) {
int port = 8888;
String host = "127.0.0.1";
String encoding = "UTF-8";
Socket socket = null;
BufferedReader brConsole = null;
String fromConsole = null;
PrintWriter pw = null;
BufferedReader brServer = null;
String fromClient = null;
try {
socket = new Socket(host,port);
brConsole = new BufferedReader(new InputStreamReader(System.in,encoding));
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
brServer = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
while(true) {
fromConsole = brConsole.readLine();
pw.println(fromConsole);
pw.flush();
fromClient = brServer.readLine();
System.out.println("客户端接收到服务器写回的是内容为:" + fromClient);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
pw.close();
if(brServer != null) {
try {
brServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务器端
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
String encoding = "UTF-8";
BufferedReader br = null;
PrintWriter pw = null;
OutputStreamWriter osw = null;
String line = null;
try {
server = new ServerSocket(8888);
System.out.println("服务器启动...");
socket = server.accept();
System.out.println("连接到客户端:" + socket );
br = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
while(true) {
line = br.readLine();
System.out.println("服务器端接收到客户端信息内容为:" + line);
pw.println("欢迎访问本服务器!");
pw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(pw != null) {
pw.close();
}
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server!=null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
结果演示: (1)启动服务器端 (2)启动客户端 在客户端窗口输入要传给服务器端的信息 可以继续输入: 在服务端窗口会看到这样的显示:
测试二:服务器端同时接收多个不同客户端的连接
总体思路: 一个服务器,多个客户端,要想多个客户端可以做到向同一个服务器建立连接和发送消息,那么服务器端就必须用到多线程,当一个客户端向服务器发送连接请求时,创建一个线程用来处理这个客户端,另一个客户端向服务器向同一服务器发送请求,服务器再创建一个线程用来处理这个客户端,循环往复,服务器不断创建线程来处理多个客户端。。。
客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientTCP {
public static void main(String[] args) {
int port = 8888;
String host = "127.0.0.1";
String encoding = "UTF-8";
Socket socket = null;
BufferedReader brConsole = null;
String fromConsole = null;
PrintWriter pw = null;
BufferedReader brServer = null;
String fromClient = null;
try {
socket = new Socket(host,port);
brConsole = new BufferedReader(new InputStreamReader(System.in,encoding));
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
brServer = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
while(true) {
fromConsole = brConsole.readLine();
pw.println(fromConsole);
pw.flush();
fromClient = brServer.readLine();
System.out.println("客户端接收到服务器写回的是内容为:" + fromClient);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
pw.close();
if(brServer != null) {
try {
brServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器端:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTCP {
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
try {
server = new ServerSocket(8888);
System.out.println("服务器启动...");
while(true) {
socket = server.accept();
System.out.println("连接到客户端:" + socket );
MyThread t = new MyThread(socket);
t.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(server!=null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
创建线程类,用于服务器处理多个客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class MyThread extends Thread{
Socket socket = null;
ServerSocket server = null;
String encoding = "UTF-8";
BufferedReader br = null;
PrintWriter pw = null;
String line = null;
public MyThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
while(true) {
line = br.readLine();
System.out.println("服务器端接收到客户端信息内容为:" + line);
pw.println("欢迎访问本服务器!");
pw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(br!= null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(pw != null) {
pw.close();
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
效果演示: (1)启动服务器端 (2)启动一个客户端(假设标号为1) (3)客户端1向服务端发送消息 客户端发送信息:这里是客户端1发送过来的信息 (在这期间收到了服务器端的回应) 客户端窗口显示 此时服务器端收到了客户端1发送过来的信息,并回应了客户端 服务器端窗口显示
(4)再启动一个客户端(就是再次运行下客户端代码,即为再启动一个客户端。。。)(假设标号为2)
客户端2向服务器端发送信息:这里是客户端2发送过来的信息(此期间也收到了服务器端的回应) 服务器端的反映如下: 你还可以继续添加客户端,继续向服务器发送消息。。。
总结: 如果像这样使用多线程来处理的话会造成资源浪费,至于其他方法我目前暂时还未涉及到,好像线程池还可以处理,但似乎也不是很好,具体还有啥学到了时再补充。。。(懒)
测试三:客户端不断的读取控制台的数据,然后写给服务器,而不是读取一次就结束
思路: 这个和测试一是类似的。不同的是这里客户端从控制台读取数据的时候要加个表示输入结束的关键词,如end,来表示输入结束,客户端每次将回车接收到的内容传给服务器端
客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientTCP {
public static void main(String[] args) {
int port = 8888;
String host = "127.0.0.1";
String encoding = "UTF-8";
Socket socket = null;
BufferedReader brConsole = null;
String fromConsole = null;
PrintWriter pw = null;
BufferedReader brServer = null;
String fromClient = null;
try {
socket = new Socket(host,port);
brConsole = new BufferedReader(new InputStreamReader(System.in,encoding));
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
brServer = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
while(true) {
fromConsole = brConsole.readLine();
if(!fromConsole.equals("end")) {
pw.println(fromConsole);
}
else{
pw.flush();
fromClient = brServer.readLine();
System.out.println("客户端接收到服务器写回的是内容为:" + fromClient);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
pw.close();
if(brServer != null) {
try {
brServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class ServerTCP {
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
String encoding = "UTF-8";
BufferedReader br = null;
PrintWriter pw = null;
OutputStreamWriter osw = null;
String line = null;
try {
server = new ServerSocket(8888);
System.out.println("服务器启动...");
socket = server.accept();
System.out.println("连接到客户端:" + socket );
br = new BufferedReader(new InputStreamReader(socket.getInputStream(),encoding));
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),encoding));
while(true) {
line = br.readLine();
System.out.println("服务器端接收到客户端信息内容为:\n" + line);
pw.println("欢迎访问本服务器!");
pw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(pw != null) {
pw.close();
}
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server!=null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端窗口输入 服务端窗口 我这里是每次读取一行就输出来。。。估计还有更好一点的方法,就这样吧
码了这么多字,人都麻了,心累,身累,也还得加把劲学习,毕竟竞争太激烈了(o(╥﹏╥)o)
|