1. 程式人生 > >網路程式設計之TCP

網路程式設計之TCP

1)建立連線,形成傳輸的資料通道

2)在連線中進行大資料量傳輸

3)通過三次握手完成連線,是可靠的協議

4)必須連線,效率稍低

2TCP分客戶端和服務端

1)客戶端對應的物件:Socket

2)服務端對應的物件:ServerSocket

2.1、客戶端

  通過查閱Socket物件,發現在該物件建立時,就可以去連線指定主機,因為TCP時面向連線的,所以在建立Socket服務時,就要有服務端存在,並連線成功,形成通路後,在該通道進行資料傳輸。

思路:

1)建立Socket服務,並制定要連線的主機和埠號

一旦Socket物件建立,就形成Socket流,包含輸入流和輸出流,

Socket有以下方法:

InputStream getInputStream():返回次套接字的輸入流

OutputStream getOutputStream():返回套接字的輸出流

2)為了傳送資料,應獲取Socket流中俄輸出流

3)關閉資源

例:

package com.heima.net;

import java.io.OutputStream;

import java.net.InetAddress;

import java.net.Socket;

publicclass TCPDemo1 {

publicstaticvoid main(String[] args)throws

 Exception {

//建立Socket服務

Socket s=new Socket(InetAddress.getByName("localhost"),10001);

//傳送資料,應獲取Socket流中輸出流

        OutputStream out=s.getOutputStream();

//寫資料

        out.write("hello heima".getBytes());

//關閉資源

        s.close();

}

}

2.2、服務端ServerSocket

思路

1)建立服務端Socket服務,ServerSocket,並監聽一個埠

2)獲取連線過來的客戶端物件,通過

ServerSocketaccept方法,沒有連線就會等,是阻塞式的

3)客戶端如果發過來資料,那麼服務端要使用對應的客戶端物件,並獲取到該客戶端物件的讀取流來讀取發過來的資料

4)關閉流

程式碼如下:

package com.heima.net;

import java.io.InputStream;

import java.net.ServerSocket;

import java.net.Socket;

publicclass TCPServer {

publicstaticvoid main(String[] args)throws Exception {

//建立ServerSocket服務

ServerSocket ss=new ServerSocket(10001);

//通過accept方法獲取連線過來的客戶端物件

Socket s=ss.accept();

//獲取ip

String ip=s.getInetAddress().getHostAddress();

//獲取客戶端發過來的資料

InputStream in=s.getInputStream();

//讀取資料到控制檯

byte[]buf=newbyte[1024];

int len=0;

while((len=in.read(buf))!=-1){

System.out.println(new String(buf,0,len));

}

//關閉資源

        s.close();

}

}

應用一:建立一個文字轉換伺服器

  客戶端給伺服器傳送文字,服務端會將文字轉換成大寫在返回該客戶端,而且客戶端能夠不斷的進行文字轉換。

package com.heima.net;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

publicclass TCPDemo1 {

publicstaticvoid main(String[] args)throws Exception {

}

}

//客戶端

class Send{

publicstaticvoid main(String[] args)throws Exception {

//建立一個Socket服務

Socket s=new Socket(InetAddress.getByName("localhost"),10009);

//建立一個鍵盤輸入流

BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));

//建立一個Socket輸出流

BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//建立一個Socket輸入流

BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

//建立一個字串

String line=null;

while((line=bufr.readLine())!=null){

//把從鍵盤讀取的資料寫到Socket輸出流中

bufOut.write(line);

//換行

bufOut.newLine();

//重新整理

bufOut.flush();

//同時需要接受服務端反饋回來的資訊

String str=bufIn.readLine();

//打印出來

System.out.println("server說:"+str);

}

//關閉資源

bufr.close();

s.close();

}

}

//服務端

class Receive{

publicstaticvoid main(String[] args)throws Exception {

//建立一個ServerSocket服務

ServerSocket ss=new ServerSocket(10009);

//獲取客戶端的Socket服務

Socket s=ss.accept();

//建立一個Socket接受流

BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

//建立一個Socket輸出流

BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//定義一個字串

String line=null;

while((line=bufIn.readLine())!=null){

//把讀取的資料轉換成大些寫出去

bufOut.write(line.toUpperCase());

//換行

bufOut.newLine();

//重新整理

bufOut.flush();

}

//關閉資源

s.close();

}

}

注:如果不進行換行和重新整理,客戶端會停留等待,因為readLine(),只有回車符時才結束,二newLine表示一行的結束。

應用二:複製檔案

package com.heima.net;

import java.io.BufferedReader;

import java.io.File;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

publicclass TCPDemo2 {

}

//客戶端

class Send1{

publicstaticvoid main(String[] args)throws Exception {

//建立Socket服務

Socket s=new Socket(InetAddress.getByName("localhost"),10007);

//建立輸入流

BufferedReader bufr=new BufferedReader(

new FileReader(new File("d:"+File.separator+"clint.txt")));

//建立Socket輸出流

PrintWriter out=new PrintWriter(s.getOutputStream(),true);

//建立一個字串

String line=null;

while((line=bufr.readLine())!=null){

//把從檔案中讀取的資料寫到Socket輸出流中

out.println(line);

}

//關閉客戶端的輸出流,相當於給流種加入一個結束標記

s.shutdownOutput();

//讀取服務端反饋回來的資料

BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));

//讀取服務端反饋回來的資料

String str=in.readLine();

System.out.println("伺服器說:"+str);

//關閉資源

bufr.close();

s.close();

}

}

//服務端

class Receive1{

publicstaticvoid main(String[] args)throws Exception {

//建立ServerSocket服務

ServerSocket ss=new ServerSocket(10007);

//建立一個客戶端的Socket服務

Socket s=ss.accept();

//讀取Socket中資料

BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

//把讀取的資料寫到檔案中

PrintWriter pw=new PrintWriter(new FileWriter(new File("d:"+File.separator+"server.txt")),true);

//定義一個字串

String line=null;

while((line=bufIn.readLine())!=null){

//把資料讀取到指定的檔案中

pw.println(line);

}

//向客戶端反饋一些資訊

PrintWriter out=new PrintWriter(s.getOutputStream(),true);

out.println("上傳成功");

//關閉資源

s.close();

pw.close();

}

}

應用三:TCP客戶端併發上傳圖片

  問題:怎樣讓多個客戶端同時併發訪問服務端?

  最好就是將每個客戶端封裝到一個單獨的執行緒中,這樣就可以同時處理多個客戶端請求。

如何定義執行緒呢?

  只要明確了每個客戶端要在服務端執行的程式碼即可,將該程式碼存入run方法中。

定義的執行緒部分

//定義一個執行緒

class PicThread implements Runnable{

//此執行緒需要接受一個客戶端

private Socket s;

public PicThread(Socket s){

this.s=s;

}

publicvoid run() {

//獲取IP

String ip=s.getInetAddress().getHostAddress();

FileOutputStream fis=null;

try{

System.out.println(ip+"連線成功");

//Socket服務中讀取客戶端傳過來的資料,因為時圖片,所有用位元組流

InputStream in=s.getInputStream();

//建立一個檔案物件

File file=new File(System.currentTimeMillis()+".jpg");

//把讀取到得圖片資料寫到指定的地方

fis=new FileOutputStream(file);

//定義一個位元組陣列

byte[]buf=newbyte[1024];

//定義一個整型

int len=0;

while((len=in.read(buf))!=-1){

//把讀取的資料寫到指定的檔案中

fis.write(buf,0,len);

}

//當上傳成功後給客戶端反饋一些資訊

OutputStream out=s.getOutputStream();

//寫一些資料

out.write("上傳成功".getBytes());

}catch(Exception e){

thrownew RuntimeException(ip+"上傳失敗");

}finally{

try{

if(fis!=null)

fis.close();

if(s!=null)

s.close();

}catch(Exception e){

e.printStackTrace();

}

}

}

}

客戶端:

//定義一個客戶端

class Client{

publicstaticvoid main(String[] args)throws Exception {

//接收一個引數

if(args.length!=1){

System.out.println("請選擇一個jpg格式的圖片");

return;

}

//建立一個檔案

File file=new File(args[0]);

//判斷是此檔案是否存在且為檔案

if(!(file.exists()&&file.isFile())){

System.out.println("此檔案不存在或者不是一個檔案");

return;

}

//判斷是否為jpg格式的

if(!file.getName().endsWith("jpg")){

System.out.println("只能上傳格式為jpg的檔案");

return;

}

//判斷檔案的大小

if(file.length()>1024*1024*3){

System.out.println("檔案超出了最大的上傳大小");

return;

}

//如果上面的步驟都通過了,可以上傳檔案了,首先建立Socket服務

Socket s=new Socket(InetAddress.getByName("localhost"),10009);

//建立一個讀取流

FileInputStream fis=new FileInputStream(file);

//建立一個Socket輸出流,把讀取到得資料輸出到服務端

OutputStream out=s.getOutputStream();

//定義一個位元組陣列

byte[]buf=newbyte[1024];

int len=0;

while((len=fis.read(buf))!=-1){

//把讀取到的資料寫入到流中

out.write(buf, 0, len);

}

//資料寫完後,顯示一個標記

s.shutdownOutput();

//接受服務端反饋的資訊

InputStream in=s.getInputStream();

//建立一個位元組陣列,把接受的資料先存放到陣列中

byte[]bufIn=newbyte[1024];

int num=in.read(bufIn);

System.out.println(new String(bufIn,0,num));

//關閉資源

fis.close();

s.close();

}

}

服務端:

//定義一個服務端

class Server{

publicstaticvoid main(String[] args)throws Exception {

//建立一個ServerSocket服務

ServerSocket ss=new ServerSocket(10009);

//不斷的接受客戶端

while(true){

//接受Socket客戶端

Socket s=ss.accept();

//建立一個執行緒

new Thread(new PicThread(s)).start();

}

}

}

應用四:客戶端的併發登入

因為同時可能有很多人登入,所以用多執行緒

//定義一個執行緒

class UserThread implements Runnable{

//接收一個Socket服務

private Socket s=null;

public UserThread(Socket s){

this.s=s;

}

@Override

publicvoid run() {

//獲取ip地址

String ip=s.getInetAddress().getHostAddress();

try {

for(int i=0;i<3;i++){

//讀取從客戶端讀取的資料

BufferedReader bufrIn=new BufferedReader(

new InputStreamReader(s.getInputStream()));

//建立一個字串用於儲存讀取的資料

 String name=bufrIn.readLine();

//從檔案中獲取資料

 BufferedReader bufr=new BufferedReader(new FileReader(

new File("d:"+File.separator+"user.txt")));

//定義一個字串

 String line=null;

//建立一個輸出流,向客戶端反饋資訊

 PrintWriter out=new PrintWriter(s.getOutputStream(),true);

//建立一個標記

boolean flag=false;

while((line=bufr.readLine())!=null){

//判斷從檔案中讀取的資料是否包含客戶端傳過來的

if(line.equals(name)){//包含則把標記改為true

 flag=true;

break;

 }

 }

if(flag){

 System.out.println(name+"登陸成功");

//向客戶端反饋資訊

 out.println(name+"歡迎登入");

break;

 }else{

 System.out.println(name+"嘗試登入");

 out.println(name+"登入失敗");

 }

 }

s.close();

catch (Exception e) {

e.printStackTrace();

thrownew RuntimeException(ip+"校驗失敗");

}

}

}

客戶端:登入三次,當三次失敗是退出程式

//定義一個客戶端

class LoginClient{

publicstaticvoid main(String[] args)throws Exception {

//建立一個Socket服務

Socket s=new Socket(InetAddress.getByName("localhost"),10008);

//從鍵盤輸入資料

BufferedReader bufr=new BufferedReader(

new InputStreamReader(System.in));

//把從鍵盤接收的資料輸出到服務端

PrintWriter out=new PrintWriter(s.getOutputStream(),true);

//接收服務端反饋回來的資訊

BufferedReader in=new BufferedReader(

new InputStreamReader(s.getInputStream()));

//定義一個字串

for(int i=0;i<3;i++){

String line=bufr.readLine();

if(line==null)

break;

out.println(line);

String str=in.readLine();

//列印接收的服務端反饋

System.out.println(str);

if(str.contains("歡迎"))

break;

}

//關閉資源

bufr.close();

s.close();

}

}

服務端:

//定義一個服務端

class LoginServer{

publicstaticvoid main(String[] args)throws Exception {

//建立ServerSocket服務

ServerSocket ss=new ServerSocket(10008);

//迴圈接收客戶端

while(true){

//接收一個客戶端Socket服務

Socket s=ss.accept();

//開啟一個執行緒

new Thread(new UserThread(s)).start();

}

}