android中的tcp和udp不需要任何權(quán)限。因?yàn)樗鼈兌际蔷W(wǎng)絡(luò)通訊協(xié)議的一種,只要手機(jī)沒有問題,能夠上網(wǎng),就可以使用TCP和UDP協(xié)議了。
從網(wǎng)站建設(shè)到定制行業(yè)解決方案,為提供做網(wǎng)站、網(wǎng)站設(shè)計(jì)服務(wù)體系,各種行業(yè)企業(yè)客戶提供網(wǎng)站建設(shè)解決方案,助力業(yè)務(wù)快速發(fā)展。成都創(chuàng)新互聯(lián)公司將不斷加快創(chuàng)新步伐,提供優(yōu)質(zhì)的建站服務(wù)。
TCP/IP是用于電腦通信的一組協(xié)議,我們通常稱之為TCP/IP協(xié)議族,它是七十年代中期美國國防部為其ARPANET廣域網(wǎng)開發(fā)的網(wǎng)絡(luò)體系結(jié)構(gòu)和協(xié)議標(biāo)準(zhǔn),以它為基礎(chǔ)組建的Internet是目前國際上規(guī)模最大的電腦網(wǎng)絡(luò),正因?yàn)镮nternet的廣泛使用,使得TCP/IP成了事實(shí)上的標(biāo)準(zhǔn)。之所以說TCP/IP是一個(gè)協(xié)議族,是因?yàn)門CP/IP協(xié)議包括TCP、IP、UDP、ICMP、RIP、TELNET、FTP、SMTP、ARP、TFTP等許多協(xié)議,這些協(xié)議統(tǒng)稱為TCP/IP協(xié)議。
TCP/IP協(xié)議的名字實(shí)際上是來自最重要的兩個(gè)協(xié)議,TCP(傳輸控制協(xié)議)和IP(網(wǎng)際協(xié)議)。它負(fù)責(zé)把需要傳輸?shù)男畔⒎指畛稍S多小包,也叫做信息包,然后把這些信息包發(fā)往目的地,它能有效地保證傳輸?shù)陌踩院驼_性。
在Internet內(nèi)部,信息不是以一個(gè)恒定的流從主機(jī)傳送到主機(jī),而是把數(shù)據(jù)分解成小包,即數(shù)據(jù)包進(jìn)行傳送。例如你傳送一封很長的信件給你的朋友,TCP就可以把這些信息分成很多個(gè)數(shù)據(jù)包,每個(gè)數(shù)據(jù)包用一個(gè)序號(hào)和一個(gè)接收地址來標(biāo)定。此外,TCP還插入一些糾錯(cuò)信息。
接著數(shù)據(jù)包被傳過網(wǎng)絡(luò),這就是IP的工作,即把它們傳送給遠(yuǎn)程主機(jī)。在另一端,TCP接收到數(shù)據(jù)包并核查錯(cuò)誤。如果有錯(cuò)誤發(fā)生,TCP可以要求重發(fā)這個(gè)特定的數(shù)據(jù)包。只要所有的數(shù)據(jù)包都被正確地接收到,TCP將用序號(hào)來重新構(gòu)造原始信息。換句話說,IP的工作是把原始數(shù)據(jù)從一地傳送到另一地,TCP的工作是管理這種流動(dòng)并確保其數(shù)據(jù)是正確的。?把數(shù)據(jù)分解成數(shù)據(jù)包有很多好處。首先,它允許Internet讓很多不同的用戶在同一時(shí)刻使用同一通訊線路。因?yàn)檫@些數(shù)據(jù)包不必一起輸送,所以通訊線路可以載著所有類型的數(shù)據(jù)包按它們自己的路徑從一地到另一地。就如一條高速公路上各個(gè)汽車都在公路上行駛。?用數(shù)據(jù)包傳輸?shù)牧硪粋€(gè)好處是:當(dāng)某處出錯(cuò),只需重新傳送單個(gè)數(shù)據(jù)包,而不是整個(gè)信息,這樣會(huì)大大加快Internet的傳輸總速度。
TCP/IP是把電腦和通訊設(shè)備組織成網(wǎng)絡(luò)的協(xié)議大家庭,兩個(gè)最重要的協(xié)議是TCP和IP。IP從一地到另一地傳輸數(shù)據(jù),而TCP則保證它們都正確地工作。
目前,遍布世界范圍的Internet網(wǎng)絡(luò)主要采用的就是TCP/IP協(xié)議,而且,國內(nèi)大多數(shù)網(wǎng)絡(luò)建設(shè)現(xiàn)在已朝著TCP/IP協(xié)議的方向發(fā)展。
tcp和udp都是網(wǎng)絡(luò)傳輸協(xié)議, 和android沒有直接關(guān)系。所有基于網(wǎng)絡(luò)訪問的過程都可能涉及這2個(gè)協(xié)議。簡單來講:tcp是可靠連接,即傳輸?shù)臄?shù)據(jù)必須完整,如用戶登錄的信息的驗(yàn)證; udp是不可靠連接,即傳輸過程不能保證數(shù)據(jù)的完整性,如語音聊天,當(dāng)網(wǎng)絡(luò)條件不好時(shí),可能會(huì)丟失內(nèi)容。
安卓的主線程中是不能用耗時(shí)性的操作讀寫,所以,把SOCKET讀寫操作都要放到線程中。
可以用即時(shí)線程的辦法。參考我的SOCKET客戶端。
//--------Socket服務(wù)端----------------------
void dispClients()
{ //顯示所有客戶
int n=vector.size();
String ss="\n"+n+" clients:\n";
for (int i=0;ivector.size();i++)
{
Socket sk=vector.get(i);
String ip=sk.getInetAddress().toString();
String port=""+sk.getPort();
ss+=""+ip+","+port+"\n";
}
textView1.setText(ss);
}
void writeSocket(Socket sk,String s)
{ // 向客戶端Socket發(fā)字符串
DataOutputStream outf;
try
{
outf=new DataOutputStream(sk.getOutputStream());
if (!sk.isClosed())
outf.writeUTF(s);
}
catch(Exception e)
{
try
{
setTitle("err");
sk.close(); // 對(duì)方已關(guān)閉
}
catch(Exception e1)
{
}
}
}
void writeSock(final Socket sk,final String s)
{ // 子線程中向客戶端Socket發(fā)字符串
new Thread(new Runnable()
{
@Override
publicvoid run()
{
writeSocket(sk,s);
}
}).start();
}
void sendAll(final String s)
{ // 向所有管理中的客戶Socket發(fā)串
boolean del=false;
for (int i=0;ivector.size();i++)
{
Socket sk=vector.get(i);
if (sk.isClosed())
{
vector.remove(i); // 已斷開的連接
del=true;
}
else
writeSock(sk,s);
}
if (del)
{ // 客戶端有變化,通知主程序刷新顯示
Message msg=sHandler.obtainMessage(0,"");
sHandler.sendMessage(msg);
}
}
void readSocket(Socket sk)
{ // 服務(wù)端讀Socket
DataInputStream inf;
try
{
inf=new DataInputStream(sk.getInputStream());
while(sk.isConnected() !sk.isClosed())
{
String s=inf.readUTF();
if (s.length()0)
{ // 讀到字符串后,通知主程序處理
String ip=sk.getInetAddress().toString();
String port=""+sk.getPort();
s=ip+","+port+":"+s+"\n";
Message msg=sHandler.obtainMessage(1,s);
sHandler.sendMessage(msg);
}
}
}
catch(Exception e)
{
}
}
void readSock(final Socket sk)
{ // 子線程中讀
new Thread(new Runnable()
{
@Override
publicvoid run()
{
readSocket(sk);
}
}).start();
}
void listen(ServerSocket ssk) // 接受多個(gè)客戶端連接請(qǐng)求
{
while (!ssk.isClosed())
try
{
Socket sk=ssk.accept();
vector.add(sk); // 有客戶連接
readSock(sk);
// 通知刷新客戶列表
Message msg=sHandler.obtainMessage(0,"");
sHandler.sendMessage(msg);
}
catch(Exception e)
{
}
}
void listen() // 線程接受多個(gè)客戶端連接請(qǐng)求
{
new Thread(new Runnable()
{
@Override
publicvoid run()
{
listen(serverSocket);
}
}).start();
}
//---------Socket客戶端----------------------------------
cHandler=new Handler()
{ // 客戶端消息處理器
@Override
public void handleMessage(Message msg)
{
String s=msg.obj.toString();
textView2.append(s);
}
};
clientRead("127.0.0.1",9003);
// clientRead("10.0.2.15",9003);
textView2=(TextView)findViewById(R.id.textView2);
textView2.setText("");
editText1=(EditText)findViewById(R.id.editText1);
button1=(Button)findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
try
{
String s=editText1.getText().toString();
writeSock(clientSocket,s);
}
catch(Exception e)
{
}
} //public
});
button2=(Button)findViewById(R.id.button2);
button2.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
try
{
clientSocket.close();
String s="中華人民共和國";
Message msg=sHandler.obtainMessage(1,s);
sHandler.sendMessage(msg);
}
catch(Exception e)
{
}
} //public
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{ // 按返回鍵的退出處理
if (keyCode == KeyEvent.KEYCODE_BACK )
{
try
{ // 防止出現(xiàn)端口已綁定的錯(cuò)誤
serverSocket.close();
finish();
System.exit(0);
}
catch(Exception e)
{
}
}
return(false);
}
之前已經(jīng)講過了tcp客戶端的實(shí)現(xiàn)了,大家有興趣的話,可以參看文章
Android上實(shí)現(xiàn)TCP客戶端
那么,今天我們就來講講tcp之服務(wù)端的封裝吧。我已經(jīng)將tcp服務(wù)端封裝成了一個(gè)類—TcpServer,下面就來講講它的使用吧。
今天涉及內(nèi)容:
先來波效果圖
在 tcp服務(wù)端 建立 ServerSocket 的時(shí)候,我們通常是這樣的:
其實(shí)以上方法調(diào)用的是
其中涉及到的參數(shù):
鑒于tcp服務(wù)端 ServerSocket 一般運(yùn)行在 "本機(jī)" 上,則快速初始化 ServerSocket 運(yùn)用上面的方法:
意思是建立的ServerSocket IP地址為本機(jī),可容納socket個(gè)數(shù)為 50 。
在理解了 ServerSocket 初始化問題后,讓我們來看看封裝類TcpServer的幾個(gè)主要方法:
TcpServer 主要是在 java 上運(yùn)行,所以就讓我們?cè)?Androidstudio 上模擬下在 Java 中運(yùn)行tcp服務(wù)端的場景:
這里涉及到的兩個(gè)類 SocketConfig 和 SocketHelper 和之前的一樣,大家可以參考文章 Android上實(shí)現(xiàn)TCP客戶端 中與之相關(guān)的介紹,這里就不贅述了。
tcp服務(wù)端主要容易出現(xiàn)以下兩個(gè)問題:
對(duì)于第一個(gè)問題,這里需要強(qiáng)調(diào)的是 TcpServer 的接收方法 receiveMessage(String charsetName) 是以 (result = bufferedReader.readLine()) != null 做判斷讀取 stream 的,所以客戶端向 TcpServer 發(fā)送消息時(shí),需要在結(jié)尾加上\n,這樣 TcpServer 的receiveMessage(String charsetName)方法才能將傳過來的數(shù)據(jù)接收完整。
對(duì)于第二個(gè)問題,則需要客戶端與服務(wù)端設(shè)置相同的字符集以保證數(shù)據(jù)不亂碼。
封裝類 TcpServer 源碼如下:
Http是應(yīng)用層協(xié)議,TCP是網(wǎng)絡(luò)層協(xié)議,應(yīng)用層在TCP/IP四層架構(gòu)中位于TCP的上一層。
建立Http連接在實(shí)現(xiàn)時(shí)有以下兩種方式:
1、[java] view plaincopy
DefaultHttpClient http = new DefaultHttpClient();
HttpGet method = new HttpGet(url);
HttpResponse response =http.execute(method);
2、[java] view plaincopy
URL url = new URL(uri);
HttpURLConnection connection = (HttpURLConnection)
url.openConnection();
connection.connect();
而TCP連接在實(shí)現(xiàn)時(shí)要借助Socket(套接字 IP+端口號(hào))
[java] view plaincopy
Socket s = new Socket("localhost", 12345);
區(qū)別從這兩個(gè)連接的實(shí)現(xiàn)方式就可以看出來,HTTP連接需要指明資源的URL,發(fā)出請(qǐng)求的應(yīng)用不知道服務(wù)器的IP,雖然域名服務(wù)器也是要把域名解析成IP地址,但不屬于應(yīng)用所關(guān)心的范疇,是網(wǎng)絡(luò)層應(yīng)該完成的工作。所以Http連接屬于無狀態(tài)的短連接,若再請(qǐng)求其他數(shù)據(jù),需要再重新建立連接??蛻舳讼蚍?wù)器發(fā)送請(qǐng)求后,服務(wù)器才知道客戶端的存在。
TCP連接實(shí)現(xiàn)時(shí)需要指明IP地址和端口號(hào),就可以跟目的主機(jī)通過三次握手建立聯(lián)系,該連接一直保持直到某一方提出取消連接,通過四次握手關(guān)閉連接。Socket支持TCP/UDP協(xié)議,如果使用TCP協(xié)議,那么socket連接就是TCP連接。論文提到的應(yīng)用場景是手機(jī)與云端的服務(wù)器建立聯(lián)系,因?yàn)橐3诌B接并指定連接的建立時(shí)間,所以在這種場景下使用TCP連接最合適。3G網(wǎng)絡(luò)不支持端到端建立TCP連接,因?yàn)樗莄lient-server模式,所以需要通過云端服務(wù)器的輔助來實(shí)現(xiàn)手機(jī)的端到端通信。