以下為本人的學習筆記
1.網路編程基本概念
1.1 什么是計算機網路
把發布在不同地理區域的計算機與專門的外部設備用通信線路互連成一個規模大、功能強的網路系統,從而使眾多的計算機可以方便地互相傳遞訊息,共享硬體、軟體、資料訊息等資源
1.2 計算機網路的主要功能
-
資源共享
-
資訊傳輸與集中處理
-
均衡負荷與分布處理
-
綜合資訊服務(www/綜合業務數字網路ISDN等)
1.3網路通信協議
要使計算機連成的網路能夠互通資訊,需要對資料傳輸速率、傳輸代碼、代碼結構、傳輸控制步驟、出錯控制等制定一組標準,這一組共同遵守的通信標準就是網路通信協議,不同的計算機之間必須使用相同的通訊協議才能進行通信
網路通信介面
為了使兩個節點之間能夠進行對話,必須在他們之間建立通信工具(即介面),使彼此之間能進行資訊交換,介面包括兩部分:
1)硬體裝置:實作節點之間的資訊傳送(網線....)
2)軟體裝置:規定雙方進行的約定協議
1.4 TCP/IP
TCP/IP;引數控制協議/因特網協議,又叫網路通訊協議,這個協議是Internet最基本的協議、Internet國際互聯網路的基礎,簡單來說,就是由網路層的IP協議和傳輸層的TCP協議組成的,
IP地址:網路中每臺計算機的一個標識號,本地IP:127.0.0.1 或者 localhost
埠號(PORT):埠號的范圍:0-65535之間,0-1023之間的埠數是用于一些知名的網路服務和應用,通過埠號找到具體要通信的軟體

1.5程式開發結構
網路編程主要是指完成C/S程式的開發,程式的開發結構有兩種:
-
C/S(客戶端/服務器)
開發兩套程式,兩套程式需要同時維護,例如:QQ,CS程式一般比較穩定
-
B/S(瀏覽器/服務器)
開發一套程式,客戶端使用瀏覽器進行訪問,例如:各個論壇,BS程式一般穩定性較差,而且安全性較差,
但是,C/S的程式開發在實際的java應用中很少,而且整個java基本上都是以B/S為主
C/S程式主要可以完成以下兩種程式的開發:
-
TCP:(Transmission Control Protocol) 傳輸控制協議,采用三方握手的方式,保證準確的連接操作
-
UDP:(User Datagram Protocol) 資料報協議,發送資料報,例如:手機短信或者是QQ訊息
TCP、UDP的資料幀格式簡單圖例:
![]()
其中協議型別用于區分TCP、UDP
2.網路編程TCP協議
2.1TCP程式概述
TCP是一個可靠的協議,面向連接的協議
實作TCP程式,需要撰寫服務器端和客戶端,Java API為我們提供了java.net包,為實作網路應用程式提供類
ServerSocket:此類實作服務器套接字
Socket:此類實作客戶端套接字(也可以就叫“套接字”)
硬體都有驅動,網路驅動層指的是網卡的驅動
通過Socket來實作網路編程,Socket是網路驅動層提供給應用程式編程的介面和一種訪問機制,
解釋:資料寫到Socket中,Socket就會把資料對接到驅動層,驅動層把資料對接到網卡,再通過網線傳出去
也可以將Socket想象成一個快遞員
2.2 資料發送程序

2.3 資料接收程序

2.4 實作服務器端與客戶端程式
服務器端:
public class ServerSocket extends Object implements Closeable
這個類實作了服務器套接字, 服務器套接字等待通過網路進入的請求, 它根據該請求執行一些操作,然后可能將結果回傳給請求者,
| method | 說明 |
|---|---|
ServerSocket(int port) |
創建系結到指定埠的服務器套接字, |
setSoTimeout(int timeout) |
啟用/禁用 SO_TIMEOUT帶有指定超時,以毫秒為單位, |
getInetAddress() |
回傳此服務器套接字的本地地址, |
accept() |
偵聽要連接到此套接字(socket)并接收它, |
客戶端:
public class Socket extends Object implements Closeable
該類實作客戶端套接字(也稱為“套接字”), 套接字是兩臺機器之間通訊的端點,
| method | 說明 |
|---|---|
Socket(InetAddress address, int port) |
創建流套接字并將其連接到指定IP地址的指定埠號 |
getInputStream() |
回傳此套接字的輸入流, |
getOutputStream() |
回傳此套接字的輸出流, |
setSoTimeout(int timeout) |
啟用/禁用指定超時的 SO_TIMEOUT(以毫秒為單位), |
3.TCP實作ECHO程式
Echo,意為應答,程式的功能是客戶端向服務器發送一個字串,服務器不做任何處理,直接把字串回傳給客戶端,Echo程式是最為基本的客戶/服務器程式,(相當于java的HelloWorld)
public class EchoServerDeom{
public static void main(String[] args){
//1.創建一個服務器端的Socket(1024-65535的埠號)
try{
ServerSocket server = new ServerSocket(port:6666)
System.out.println("服務器已啟動,正在等待客戶端的連接...");
//2.等待客戶端的連接,造成阻塞,如果有客戶端連接成功,立即回傳一個Socket物件
Socket socket = server.accpet();
System.out.println("客戶端連接成功:"+server.getInetAddress().getHostAddress());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream());
//通過輸入流讀取網路資料,如果沒有資料,那么會阻塞
String info = br.readLine();
System.out.println(info);
//獲取輸出流,向客戶端回傳訊息
PrintStream ps = new PrintStream (new BufferedOutputStream(socket.getOutputStream()));
ps.println("echo"+info);
ps.flush();
//關閉
ps.close();
br.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
?
public class EchoClientDeom{
public static void main(String[] args){
//1.創建一個Socket物件,指定要連接的服務器
try{
Socket socket = new Socket("localhost",6666);//主機名+埠號
//獲取socket的輸入輸出流
PrintStream ps = new PrintStream(new BufferedOutputStream(socket.getOutPutStream()));
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
ps.println("hello,my name is bin");
ps.flush();
//讀取服務器端回傳的資料
String info = br.readLine();
System.out.println(info);
ps.close();
br.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
兩個物件創建成功后面就是流的操作
4.服務器與多客戶端通信
要想服務器同時支持多個客戶端的連接,就必須加入多執行緒的處理機制,將每一個連接的客戶端都創建一個新的執行緒物件(用回圈)
服務器端通過加入執行緒池來處理多個客戶端請求,簡單的設定執行緒數可以與CPU核數匹配,過多的執行緒數空閑會消耗服務器為資源
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//服務器,用回圈處理多個客戶端
public class ServerSocketDemo2 {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(6666);
System.out.println("服務器已啟動,等待客戶端連接...");
ExecutorService es = Executors.newFixedThreadPool(3);
while (true){
Socket socket = server.accept();
System.out.println(socket.getInetAddress().getHostAddress());
es.execute(new UserThread(socket));//一個執行緒處理一個客戶端,將一個客戶端socket作為執行緒的一個引數
}
?
} catch (IOException e) {
e.printStackTrace();
}
}
}
//用來處理客戶端請求的執行緒
class UserThread implements Runnable{
private Socket socket;
UserThread(Socket socket){
this.socket = socket;
}
public void run(){
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
?
String info = br.readLine();
System.out.println(info);
?
pw.println("echo:"+info);
pw.flush();
br.close();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
//客戶端
public class MutilClientDemo {
public static void main(String[] args) {
try {
Scanner input = new Scanner(System.in);
//創建一個socket物件,指定要連接的服務器
Socket socket = new Socket("localhost", 6666);
?
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream ps = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
?
System.out.println("請輸入一段話:");
String info = input.nextLine();
ps.println(info);
ps.flush();
?
//讀取服務器回傳的資料
info = br.readLine();
System.out.println(info);
?
br.close();
ps.close();
?
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.多客戶端之間的通信
服務器可以與多個客戶端實作通信了,那我們真正的目的就是要實作多個客戶端之間的通信,使用TCP協議實作的方案是:客戶端的資料包通過服務器中轉,發送到另一個客戶端,如下圖所示:

多客戶端間通信案例:
服務器通過訊息型別來判斷發送訊息的客戶端是什么意圖,訊息型別是我們自定義的標記
圖解分析:
服務器端:
/*服務器作為一個中轉,轉發訊息給多個客戶端之間*/
public class Server {
public static void main(String[] args) {
//2.創建集合,保存客戶端處理的執行緒,用來找其他指定執行緒
Vector<UserThread> vector = new Vector<UserThread>();
//3.創建執行緒池
ExecutorService es = Executors.newFixedThreadPool(5);
//1.創建服務器端的Socket
try {
ServerSocket server = new ServerSocket(7777);
System.out.println("服務器已啟動,正在等待連接...");
while (true){
//4.接收一個客戶端的socket
Socket socket = server.accept();
UserThread user = new UserThread(socket,vector);
//5.執行緒執行一個客戶端
es.execute(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//客戶端處理的執行緒
class UserThread implements Runnable{
private String name ;//客戶端的用戶名稱(唯一)
private Socket socket;
private Vector<UserThread> vector;//客戶端處理執行緒的集合
private ObjectInputStream ois;
private ObjectOutputStream oos;
private boolean flag = true;
?
public UserThread(Socket socket, Vector<UserThread> vector) {
this.socket = socket;
this.vector = vector;
vector.add(this);
}
@Override
public void run() {
try {
//6.輸出客戶端socket主機地址
System.out.println("客戶端"+socket.getInetAddress().getHostAddress());
//7.構建序列化輸入輸出物件流,
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
?
while (flag){
//8.讀取訊息物件
Message msg = (Message)ois.readObject();
//9.判斷訊息型別
int type = msg.getType();
switch (type){
//發送訊息的型別
case MessageType.TYPE_SEND:
String to = msg.getTo();
UserThread ut ;
int size = vector.size();
//遍歷執行緒集合里的執行緒名字,符合就把訊息writ給該執行緒
for (int i = 0; i < size; i++) {
ut = vector.get(i);//取一個變數裝這個執行緒
if (to.equals(ut.name) && ut != this){
ut.oos.writeObject(msg);//把該訊息發給該執行緒
break;
}
}
?
break;
//登錄訊息型別
case MessageType.TYPE_LOGIN:
//獲取發送者名字
name = msg.getFrom();
msg.setInfo("歡迎你:");
//輸出客戶端歡迎資料
oos.writeObject(msg);
break;
}
}
//10.關閉流
ois.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
客戶端:
public class Client {
public static void main(String[] args) {
?
Scanner input = new Scanner(System.in);
ExecutorService es = Executors.newSingleThreadExecutor();
try {
//1.創建一個客戶端的socket
Socket socket = new Socket("localhost",7777);
System.out.println("服務器連接成功...");
//2.構建序列化輸入輸出物件流
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//3.向服務器發送登錄訊息
System.out.println("請輸入名稱:");
String name = input.nextLine();
Message msg = new Message(name,null,MessageType.TYPE_LOGIN,null);
oos.writeObject(msg);
msg = (Message)ois.readObject();
System.out.println(msg.getInfo()+msg.getFrom());
//啟動(不斷)讀取訊息的執行緒
es.execute(new ReadInfoThread(ois));
?
//使用主執行緒來實作發送訊息
boolean flag = true;
while(flag){
msg = new Message();
System.out.println("To:");
msg.setTo(input.nextLine());
msg.setFrom(name);
msg.setType(MessageType.TYPE_SEND);
System.out.println("Info:");
msg.setInfo(input.nextLine());
oos.writeObject(msg);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//讀取訊息
class ReadInfoThread implements Runnable{
?
private ObjectInputStream in;
private boolean flag = true;
?
public ReadInfoThread(ObjectInputStream in) {
this.in = in;
}
public void setFlag(boolean flag){
this.flag= flag;
}
@Override
public void run() {
try {
//不斷地讀取訊息
while (flag){
?
Message message = (Message)in.readObject();
System.out.println("["+message.getFrom()+"]對我說:"+message.getInfo());
}
if (in!=null){
in.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
import java.io.Serializable;
?
/*訊息包*/
public class Message implements Serializable {//序列化物件實作Serializable介面
?
private String from;//發送者
private String to;//接收者
private int type;//訊息型別
private String info;//訊息
?
public Message() {
}
public Message(String from, String to, int type, String info) {
this.from = from;
this.to = to;
this.type = type;
this.info = info;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Message{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", type=" + type +
", info='" + info + '\'' +
'}';
}
}
public final class MessageType {
?
public static final int TYPE_LOGIN = 0x1;//登錄訊息型別
public static final int TYPE_SEND = 0x2;//發送訊息的型別
}
6.網路編程UDP協議
1.UDP協議概述
UCP是User Datagram Protocol的簡稱,是一種無連接的協議,每個資料報都是一個獨立的資訊,包括完整的源地址或目的地址,它在網路上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的,每個被傳輸的資料報必須限定在64KB之內,
主要使用以下兩個類:
DatagramPacket:此類表示資料報包
DatagramSocket:此類表示用來發送和接收資料報包的套接字
UDP是無連接的,減少了開銷和發送資料之前的延遲,不保證可靠,UDP的報頭長度要小于TCP的包頭長度,例如QQ檔案傳輸,pplive等都是使用UDP協議
服務器端:
public class UDPServerDeom{
public static void main(String[] args){
String info = "good good 天天";
byte[] bytes = info.getBytes();
try{
DatagramPacket dp = new DatagramPacket(bytes,/**資料報包資料*/
0,/**0表示第0個位置,分組資料偏移量*/
bytes.length,/**陣列長度*/
InetAddress.getByName("127.0.0.1"),/**目的地址*/
8000/**目的埠號*/);
//本程式的埠號
DatagramSocket socket = new DatagramSocket(9000);
socket.send(dp);
} catch(UnknownHostException e){
e.printStackTrace();
} catch( IOException e){
e.printStackTrace();
}
}
}
客戶端:客戶端要先啟動,在receive()阻塞,等待server發資料報
public class UDPClientDeom{
public static void main(String[] args){
byte[] bytes = new byte[1024];
//封裝成資料報包
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
try{
DatagramSocket socket = new DatagramSocket(8000);
System.out.println("正在接收資料中...");
socket.receive(dp);//接收資料,會造成阻塞
String s = new String(dp.getData(),0,dp.getLength());//從data中取,從0 到dp.getLength()
System.out.println(s);
socket.close();
} catch(UnknownHostException e){
e.printStackTrace();
} catch( IOException e){
e.printStackTrace();
}
}
}
7.URL
URL概述
URL(uniform resource location)類URL代表一個統一資源定位符,是互聯網上標準資源的地址,它是指向互聯網“資源”的指標,互聯網的每個檔案都有唯一的一個URL,抽象類URLConnection是所有類的超類,它代表應用程式和URL之間的通信鏈接
public class URLDeom{
public static void main(String[] args){
try{
URL url = new URL("https://pic.cnblogs.com/avatar/2988753/20221012224248.png");//資源路徑
HttpURLConnection conn = (HttpURLConnection)url.openConnection();//打開鏈接,因為圖片地址是http協議,可以用HttpURLConnection
BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("D:\\xx\\wukong.jpg"));
byte[] bytes = new byte[1024];
int len = -1 ;
while((len = in.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
in.close();
out.close();
System.out.println("下載成功... ");
} catch(MalformedURLException e){
e.printStackTrace();
} catch( IOException e){
e.printStackTrace();
}
}
}
?
?
8.MINA框架
-
什么是MINA?一句話就是:一個簡潔易用的基于TCP/IP通信的java框架
-
下載地址:http://mina.apache.org/downloads-mina.html 下載zip包
-
一個簡單的網路程式需要的最少jar包:mina-core-2.0.16.jar、slf4j-api-1.7.21.jar
-
開發一個MINA應用,簡單的說,就是①創建連接,②設定過濾規則,③撰寫自己的訊息處理器
-
Mina框架可以幫助我們快速開發高性能、高擴展的網路通信應用,Mian提供了事件驅動、異步操作的編程模型,默認使用NIO作為底層支持
示例:
public class Server{
public static void main(String[] args){
//創建一個非阻塞的Server端Socket,用NIO
?
SocketAcceptor acceptor= new NioSocketAcceptor();//創建接收資料的過濾器
?
DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();//chain鏈
//設定這個過濾器將一行一行(/r/n)的讀取資料
chain.addLast("myChin",new ProtocolCodecFilter(new TextLineCodecFactory()));
//如,設定服務器端的訊息處理器:一個MainaServerHandler物件
acceptor.setHandler(new MinaServerHandler());
int bindPort = 9999;//服務器埠號
try{
//系結埠,啟動服務器
//立即回傳
acceptor.bind(new InetSocketAddress(bindPort));
}catch(IOException e){
e.printStackTrace();
}
System.out.println("Mina Server is running: = "+bindPort);
}
}
/**
服務器端的訊息處理器
*/
public class MinaServerHandler extends IoHandleAdapter{
//一次會話被打開
public void sessionOpened(IoSession session) throws Exception{
super.sessionOpened(session);
System.out.println("welcome client"+session.getRemoteAddress());
}
//會話被關閉
public void sessionClosed(IoSession session) throws Exception{
super.sessionClosed(session);
System.out.println("client closed");
}
//接收訊息
public void messageReceived(IoSession session,Object message) throws Exception{
super.messageReceived(session,message);
String msg = (String)message;//接收到的訊息物件
System.out.println("收到客戶端發來的訊息: "+msg);
//向客戶端發送訊息物件
session.write("echo:"+msg);
}
}
}
使用telnet測驗:telnet localhost 9999
public class Client{
public static void main(String[] args){
NioSocketConnector connector = new NioSocketConnector();//創建接收資料的過濾器
DefaultIoFilterChainBuilder chain = connector.getFilterChain();
//設定這個過濾器將一行一行(/r/n)的讀取資料
chain.addLast("myChin",new ProtocolCodecFilter(new TextLineCodecFactory()));
//如,設定服務器端的訊息處理器:一個SamplMainaServerHandler物件
connector.setHandler(new MinaServerHandler());
connector.setConnectTimeoutMillis(30);//Set connect timeout.
//連接到服務器:
ConnectFuture cf = connector.connect(new InsetSocketAddress("localhost",9999));
//等待連接成功,立即回傳
cf.awaitUninterruptibley();
Scanner input = new Scanner(System.in);
while(true){
System.out.println("請輸入:");
String info = input.nextLine();
//發送訊息
cf.getSession().write(info);
}
//等待服務器連接關閉,結束長連接
//cf.getSession().getCloseFuture().awaitUninterruptibly();//會阻塞
//connector.dispose(); //釋放
}
}
public class SampleMinaServerHandler extends IoHandleAdapter{
//當一個客戶端連接進入時
public void sessionOpened(IoSession session) throws Exception{
System.out.println("incomming client"+session.getRemoteAddress());
super.sessionOpened(session);
?
session.writ("我來啦");
?
}
//當一個客戶端關閉時
public void sessionClosed(IoSession session) {
super.sessionClosed(session);
?
System.out.println("client closed");
}
//當一個客戶端發送的訊息到達時:
public void messageReceived(IoSession session,Object message) throws Exception{
super.messageReceived(session,message);
?
//我們已設定了服務器決議訊息的規則是一行一行讀取,這里就可轉為String;
String msg = (String)message;
System.out.println("收到服務器端發來的訊息: "+msg);
//測驗將訊息會送給客戶端
session.write(msg);
}
}
使用Mina直接傳送物件
1.public class User info implements java.io.Serializable
2.服務器,客戶端都設定以物件為單位
//設定這個過濾器將以物件為單位讀取資料
ProtocolCodecFilter filter = new ProtocolCodecFilter(new ObjectSerializationCodecFactory());
chain.addLast("objectFilter",filter);
3.接收物件
public void messageReceived(IoSession session,Object message) throws Exception{
//我們已設定了服務器決議訊息的規則一個Userinfo物件為單位傳輸:
Userinfo us = (Userinfo)message;
}
public class Message implements Serializable{
private String from ;
private String to;
private String type;
private String info;
......
}
■免責申明
⒈ 本站是純粹個人學習網站,與朋友交流共賞,不存在任何商業目的,
⒉ 本站利用了部分網路資源,著作權歸原作者及網站所有,如果您對本站所載文章及作品著作權的歸屬存有異議,請立即通知我們,我們將在第一時間予以洗掉,同時向你表示歉意!
2022-10-14 17:33:19
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/514248.html
標籤:其他
上一篇:go入坑記錄 檔案斷點重傳
下一篇:配置環境變數
