tcp伺服器編程
㈠ TCP和UDP網路通訊的區別及實現方式是什麼
TCP:Transmission Control Protocol 傳輸控制協議TCP是一種面向連接(連接導向)的、可靠的、基於位元組流的運輸層(Transport layer)通信協議,在 OSI模型中,它完成第四層傳輸層所指定的功能。x0dx0aUDP:是User Datagram Protocol的簡稱,用戶數據包協議,是 OSI 參考模型中一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務。x0dx0aTCP和UDP傳輸就類似於我們的手機通電話和手機發簡訊,一種必需連通了,才能夠通話,相對來說比穗肢較可靠,傳輸速度比較快,另一種可以在關機狀態(無連接)發送信息,相對來說,可靠性比較差,傳輸速度較慢。具體的差別如下:x0dx0aTCP協議面向連接,UDP協議面向非連接 x0dx0a TCP協議傳輸速度慢,UDP協議傳輸速度快 x0dx0a TCP協議保證數據順序,UDP協議不保證 x0dx0a TCP協議保證數據正確性,UDP協議可能丟包 x0dx0a TCP協議對系統資源要求多,UDP協議要求少x0dx0a不管是基於TCP還是基於UDP的網路通訊編程,都要區分伺服器端和客戶端,下面以TCP為例,實現客戶端和伺服器端通訊的實現步驟:x0dx0aTCP伺服器端的編寫步驟:x0dx0a1. 首先,你需要創建一個用於通訊的套介面,一般使用socket調用來實現。這等睜族正於你有了一個用於通訊的電話:) x0dx0a 2. 然後,你需要給你的套介面設定埠,相當於,悉悔你有了電話號碼。這一步 一般通過設置網路套介面地址和調用bind函數來實現。 x0dx0a 3. 調用listen函數使你的套介面成為一個監聽套接字。 以上三個步驟是TCP伺服器的常用步驟。 x0dx0a 4. 調用accept函數來啟動你的套接字,這時你的程序就可以等待客戶端的連接了。 x0dx0a 5. 處理客戶端的連接請求。 x0dx0a 6. 終止連接。x0dx0aTCP編程的客戶端一般步驟是:x0dx0a1、創建一個socket,用函數socket();x0dx0a 2、設置socket屬性,用函數setsockopt();* 可選x0dx0a 3、綁定IP地址、埠等信息到socket上,用函數bind();* 可選 x0dx0a 4、設置要連接的對方的IP地址和埠等屬性;x0dx0a 5、連接伺服器,用函數connect()(相當於撥號); x0dx0a 6、收發數據,用函數send()和recv(),或者read()和write()(相當於通話);
㈡ LINUX網路編程TCP伺服器 客戶端 有亂碼怎麼解決
解決辦法:
1.在客戶端n=read(socketfd,buff,1023);代碼之前加上memset(buff,0,sizeof(buff));,這是保證收到較短數據(使用TCP你不能保證每次接收的數據和發送的數據時等長的),列印也是正確的;
2.將客戶端buff[n+1]+='\0';修改為buff[n]='\0';,這是因為n是下標,已經是最後一個位置了;
3.將伺服器端buff[n+1]+='\0';修改為buff[n]='\0';,這是因為n是下標,已經是最後一個位置了,而且和第2)一樣,那個加號也要去掉,應該是筆誤吧;
4.最大的問題,將伺服器端write(connectfd,buff,1023);,你怎麼能夠保證收到1023個字元呢?也應該將while中條件移出作為WHILE中的一條語句,而且加上前面所述的memset語句,而將這里的write(connectfd,buff,1023);修改為write(connectfd,buff,strlen(buff))。
祝共同進步!
㈢ TCP通訊伺服器程序用什麼語言寫最好
C語言絕對是適合的配謹拿,只要你能寫出來。C語言運行的效率高,不解釋的。
java也勉強可以吧。能用培搭C語言寫的話,java不做考慮晌做。
㈣ TCP伺服器和客戶機編程
真費時間呢…… import java.net.ServerSocket;
import java.net.Socket;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;public class TcpServer {
public static void main(String[] args) {
try {
final int PORT = 8888;
ServerSocket serverSocket = new ServerSocket(PORT);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader in = new BufferedReader(isr); OutputStream os = socket.getOutputStream();
PrintWriter out = new PrintWriter(os,true); String line;
while((line = in.readLine()) != null) {
System.out.println("server got msg " + line + " from " + socket.getRemoteSocketAddress());
line = "I am server!";
out.println(line);
System.out.println("server sent size(byte): " + line.getBytes().length);
} out.close();
in.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
System.out.println(e);
System.exit(1);
}
}
}
import java.net.Socket;
import java.net.UnknownHostException;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.IOException;public class TcpClient {
public static void main(String[] args) throws IOException {
try {
final String SERVER_ADDRESS = "127.0.0.1";
final int PORT = 8888;
Socket socket = new Socket(SERVER_ADDRESS,PORT); OutputStream os = socket.getOutputStream();
PrintWriter out = new PrintWriter(os,true); InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader in = new BufferedReader(isr); String line;
line = "A line sent by the client";
out.println(line); line = in.readLine();
System.out.println("client got: " + line); out.close();
in.close();
socket.close();
} catch (UnknownHostException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
}
}
㈤ LINUX網路編程TCP的伺服器 客戶端 有亂碼怎麼解決
網一科技的代理伺服器正規專業.我朋友推薦我用了效果很好。希望對你有幫助您好!今後一起共勉!更多交流在CSDN,365testing,測評網
㈥ 如何用C#實現多線程TCP協議的伺服器端程序
用C#實現多線程TCP協議的伺服器端程序:
// <summary>
//毀帆/ Tcp客戶線程類(服務端),ThreadServerProcessor 線程產生的客戶連接,用該線程讀寫
/// </summary>
public class ThreadClientProcessor
{
//Tcp連接實例
private TcpClient tcpClient;
//消息框,本來想寫日誌用
private System.Windows.Forms.ListBox MessageList;
private string Password; //該連接登陸密碼
private string Cmd1Echo;
private string Cmd2Echo;
private bool ClientLogOn;//客戶是否登陸
private bool TcpClose;
public ThreadClientProcessor(){}
//構造函數,參數解釋:Tcp客戶,消息框,該服務密碼(password命令後的參數) ,命令回應串 1,2 ******************
public ThreadClientProcessor(TcpClient client , ListBox listBox,string LogonText ,string cmd1echo,string cmd2echo)
{
ClientList.Add(this); //把當前實例加入一個列表中,方便以後控制
this.tcpClient=client;
this.MessageList=listBox;
this.Password=LogonText;
this.Cmd1Echo=cmd1echo;
this.Cmd2Echo=cmd2echo;
this.ClientLogOn=false;
this.TcpClose=false;
}
public static char[] CmdSplit={' '}; //讀來的串由' ' 進行分離,命名+' '+參數
//public const string[] Cmd=new string[] { "password","cmd1","cmd2","echo","bye"};
//該函數由你自己寫,這個只是給一個例子,
//功能:命令處理器,給個命令串,返回該命令處理結果,把命令和處理結果放在一個文本文件里,便於系統升級
public string TcpCmd(string s)
{
string result;
try
{
string cmdarg=s.Trim();
string[] args=cmdarg.Split(CmdSplit);
string cmd=args[0].ToLower();
switch (cmd )
{
case "password" :
if (args.Length>1)
{
ClientLogOn= Password.Equals(args[1].Trim());
result=ClientLogOn? "登陸成功":"密碼不正確,未登陸";
}
else result= "登陸時候,沒有輸入密碼";
break;
case "cmd1":
result=ClientLogOn?this.Cmd1Echo:"該命令無權執行,請先登陸";
break;
case "cmd2":
result=ClientLogOn?this.Cmd2Echo:"該命令無權執行,請先登陸";
break;
case "echo":
result=string.Format("伺服器回纖行雹應:\n {0}",s);
break;
case "bye":
this.TcpClose=true;
result="DisConnected";
break;
default:
result="不可識別的命令";
break;
}
}
catch
{
result="解析命令發生錯誤,你輸入的是狗屁命令,TMD *^* ";
}
return result;
} //end cmd
//定帶讓義一個線程,該線程對應的函數是 void start()(不是Start())********************************
//一下程序主要是操作該線程
public System.Threading.Thread tcpClientThread;
//啟動客戶連接線程 *************************************************************
public void Start()
{
tcpClientThread=new Thread(new ThreadStart(start));
tcpClientThread.Priority=ThreadPriority.BelowNormal;
tcpClientThread.Start();
}
//斷開該當前實例連接,終止線程 **************************************************************
public void Abort()
{
if (this.tcpClientThread!=null)
{
//tcpClientThread.Interrupt();
tcpClientThread.Abort();
//一定要等一會兒,以為後邊tcpClient.Close()時候,會影響NetWorkStream的操作
Thread.Sleep(TimeSpan.FromMilliseconds(100));
tcpClient.Close();
}
}
//靜態列表,包含了每個連接實例(在構造實例時候使用了 ArrayList.Add( object))
private static System.Collections.ArrayList ClientList=new ArrayList();
//斷開所有的Tcp客戶連接,靜態方法*************************************************************
public static void AbortAllClient()
{
for(int j=0 ;j< ClientList.Count;j++)
{
//從實例列表中取一個對象,轉化為ThreadClientProcessor對象
ThreadClientProcessor o=(ThreadClientProcessor ) ClientList[j];
//調用ThreadClientProcessor 對象的停止方法
o.Abort();
}
//清除連接列表
ClientList.Clear();
}
//讀寫連接的函數,用於線程//*******************************************************************
private void start()
{
byte[] buf=new byte[1024*1024]; //預先定義1MB的緩沖
int Len=0; //流的實際長度
NetworkStream networkStream=tcpClient.GetStream(); //建立讀寫Tcp的流
try
{
byte[] p=Encoding.UTF8.GetBytes(" 歡迎光臨,請輸入密碼" );
//向Tcp連接寫 歡迎消息
if (!this.ClientLogOn )
networkStream.Write(p,0,p.Length);
//開始循環讀寫tcp流
while (!TcpClose)
{
//如果當前線程是在其它狀態,(等待掛起,等待終止.....)就結束該循環
if (Thread.CurrentThread.ThreadState!=ThreadState.Running)
break;
//判斷Tcp流是否有可讀的東西
if ( networkStream.DataAvailable)
{
//從流中讀取緩沖位元組數組
Len=networkStream.Read(buf,0,buf.Length);
//轉化緩沖數組為串
string cmd=Encoding.UTF8.GetString(buf,0,Len);
this.MessageList.Items.Add("客戶機:"+cmd);
//處理該緩沖的串(分析命令),分析結果為res串
string res=TcpCmd(cmd);
//把命令的返回結果res 轉化為位元組數組
byte[] result=Encoding.UTF8.GetBytes(res);
//發送結果緩沖數組給客戶端
networkStream.Write(result,0,result.Length);
this.MessageList.Items.Add("伺服器回應:"+res);
}
else
{
//Thread.Sleep(TimeSpan.FromMilliseconds(200d));
//this.MessageList.Items.Add("客戶機無命令");
//如果當前Tcp連接空閑,客戶端沒有寫入,則當前線程停止200毫秒
Thread.Sleep(TimeSpan.FromMilliseconds(200d));
}
}
㈦ 用C語言幫忙寫一個「TCP Client/Server模式的通信程序設計與實現」
CLIENT:
#include <stdio.h>
#include <stdio.h>
#include <winsock.h>
#pragma comment(lib,"Ws2_32")
#define PORT 6666 /* 客戶機連接遠程主機的埠 */
#define MAXDATASIZE 100 /* 每次可以接收的最大位元組 */
int main()
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
char msg[MAXDATASIZE];
char *argv="127.0.0.1";
struct sockaddr_in their_addr; /* 對方的地址埠信息 */
WSADATA ws;WSAStartup(MAKEWORD(2,2),&ws); //初始化Windows Socket Dll
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
//如果侍基建立socket失敗,退出程序
printf("socket error\n");
exit(1);
}
//連接對方
their_addr.sin_family = AF_INET; /* 協議類型是INET */
their_addr.sin_port = htons(PORT); /* 連接對方PORT埠悔洞 */
their_addr.sin_addr.s_addr = inet_addr(argv); /* 連接對方老前謹的IP */
if (connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1)
{
//如果連接失敗,退出程序
printf("connet error\n");
closesocket(sockfd);
exit(1);
}
while(1){
scanf("%s",msg);
//發送數據
if (send(sockfd, msg, MAXDATASIZE, 0) == -1)
{
printf("send error");
closesocket(sockfd);
exit(1);
}
//接收數據,並列印出來
if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1)
{
//接收數據失敗,退出程序
printf("recv error\n");
closesocket(sockfd);
exit(1);
}
buf[numbytes] = '\0';
printf("Received: %s\n",buf); }
closesocket(sockfd);
return 0;
}
SERVER:
#include <stdio.h>
#include <winsock.h>
#pragma comment(lib,"Ws2_32")
#define MYPORT 6666 /*定義用戶連接埠*/
#define BACKLOG 10 /*多少等待連接控制*/ #define MAXDATASIZE 100
int main()
{
int sockfd, new_fd; /*定義套接字*/
struct sockaddr_in my_addr; /*本地地址信息 */
struct sockaddr_in their_addr; /*連接者地址信息*/
int sin_size,numbytes; char msg[10],buf[MAXDATASIZE];
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws); //初始化Windows Socket Dll
//建立socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
//如果建立socket失敗,退出程序
printf("socket error\n");
exit(1);
}
//bind本機的MYPORT埠
my_addr.sin_family = AF_INET; /* 協議類型是INET */
my_addr.sin_port = htons(MYPORT); /* 綁定MYPORT埠*/
my_addr.sin_addr.s_addr = INADDR_ANY; /* 本機IP*/
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
//bind失敗,退出程序
printf("bind error\n");
closesocket(sockfd);
exit(1);
}
//listen,監聽埠
if (listen(sockfd, BACKLOG) == -1)
{
//listen失敗,退出程序
printf("listen error\n");
closesocket(sockfd);
exit(1);
}
printf("listen...");
//等待客戶端連接
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
{
printf("accept error\n");
closesocket(sockfd);
exit(1);
}
printf("\naccept!\n");
while(1) {
if((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) continue;
if(!strcmp(buf,"bye"))
{
//成功,關閉套接字
closesocket(sockfd);
closesocket(new_fd);
return 0;
}
printf("%s %d",buf,strlen(buf));
sprintf(msg,"%d",strlen(buf));
if (send(new_fd,msg,MAXDATASIZE, 0) == -1)
{
printf("send ERRO");
closesocket(sockfd);
closesocket(new_fd);
return 0;
}
}
}
㈧ Python網路編程 -- TCP/IP
首先放出一個 TCP/IP 的程序,這里是單線程伺服器與客戶端,在多線程一節會放上多線程的TCP/IP服務程序沖戚拍。
這里將服務端和客戶端放到同一個程序當中,方便對比服務端與客戶端的不同。
TCP/IP是網際網路的通信協議,其參考OSI模型,也採用了分層的方式,對每一層制定了相應的標准。
網際協議(IP)是為全世界通過互聯網連接的計算機賦予統一地址系統的機制,它使得數據包能夠從互聯網的一端發送至另一端,如 130.207.244.244,為了便於記憶,常用主機名代替IP地址,例如 .com。
UDP (User Datagram Protocol,用戶數據報協議) 解決了上述第一個問題,通過埠號來實現了多路復用(用不同的埠區分不同的應用程序)但是使用UDP協議的網路程序需要自己處理丟包、重包和包的亂序問題。
TCP (Transmission Control Protocol,傳輸控制協議) 解決了上述兩個問題,同樣使用埠號實現了復用。
TCP 實現可靠連接的方法:
socket通信模型及 TCP 通信過程如下兩張圖。
[圖片上傳失敗...(image-6d947d-1610703914730)]
[圖片上傳失敗...(image-30b472-1610703914730)]
socket.getaddrinfo(host, port, family, socktype, proto, flags)
返回: [(family, socktype, proto, cannonname, sockaddr), ] 由元組組成的列表散羨。
family:表示socket使用的協議簇, AF_UNIX : 1, AF_INET: 2, AF_INET6 : 10。 0 表示不指定。
socktype: socket 的類型, SOCK_STREAM : 1, SOCK_DGRAM : 2, SOCK_RAW : 3
proto: 協議, 套接字所用的協議,如果不指定, 則為 0。 IPPROTO_TCP : 6, IPPRTOTO_UDP : 17
flags:標記,限制返回內容。 AI_ADDRCONFIG 把計算機無法連接的所有地址都過濾掉(如果一個機構既有IPv4,又有IPv6,而主機只有IPv4,則會把 IPv6過濾掉)
AI _V4MAPPED, 如果本機只有IPv6,服務卻只有IPv4,這個標記會將 IPv4地址重新編碼為可實際使用的IPv6地址。
AI_CANONNAME,返回規范主機名:cannonname。
getaddrinfo(None, 'smtp', 0, socket.SOCK_STREAM, 0, socket.AP_PASSIVE)
getaddrinfo('ftp.kernel.org', 'ftp', 0, 'socket.SOCK_STREAM, 0, socket.AI_ADDRCONFIG | socket.AI_V4MAPPED)
利用已經通信的套接字名提供給getaddrinfo
mysock = server_sock.accept()
addr, port = mysock.getpeername()
getaddrinfo(addr, port, mysock.family, mysock.type, mysock.proto, socket.AI_CANONNAME)
TCP 數據發送模式:
由於 TCP 是發送流式數據,並且會自動分割發送的數據包,而仔槐且在 recv 的時候會阻塞進程,直到接收到數據為止,因此會出現死鎖現象,及通信雙方都在等待接收數據導致無法響應,或者都在發送數據導致緩存區溢出。所以就有了封幀(framing)的問題,即如何分割消息,使得接收方能夠識別消息的開始與結束。
關於封幀,需要考慮的問題是, 接收方何時最終停止調用recv才是安全的?整個消息或數據何時才能完整無缺的傳達?何時才能將接收到的消息作為一個整體來解析或處理。
適用UDP的場景:
由於TCP每次連接與斷開都需要有三次握手,若有大量連接,則會產生大量的開銷,在客戶端與伺服器之間不存在長時間連接的情況下,適用UDP更為合適,尤其是客戶端太多的時候。
第二種情況: 當丟包現象發生時,如果應用程序有比簡單地重傳數據聰明得多的方法的話,那麼就不適用TCP了。例如,如果正在進行音頻通話,如果有1s的數據由於丟包而丟失了,那麼只是簡單地不斷重新發送這1s的數據直至其成功傳達是無濟於事的。反之,客戶端應該從傳達的數據包中任意選擇一些組合成一段音頻(為了解決這一問題,一個智能的音頻協議會用前一段音頻的高度壓縮版本作為數據包的開始部分,同樣將其後繼音頻壓縮,作為數據包的結束部分),然後繼續進行後續操作,就好像沒有發生丟包一樣。如果使用TCP,那麼這是不可能的,因為TCP會固執地重傳丟失的信息,即使這些信息早已過時無用也不例外。UDP數據報通常是互聯網實時多媒體流的基礎。
參考資料:
㈨ TCP編程和UDP編程的主要區別是什麼
tcp和udp從協議上的主要區別是:tcp是可靠的,流時的,但是鏈接建立比較「重」,需要三次握手,udp不能保證數據一空知定到達接收方,並且數據包並友虧余不能保證按照發送方的順序接收。編程上的區別應該並不大,因為底層的API都屏蔽了協好滾議上的差別。
㈩ 如何編程自己實現tcp/ip的三次握手
在TCP/IP協議中,TCP協議提供可靠的連接服務,採用三次握手建立一個連接。
第一次握手:建立連接時,客戶端發送syn包(syn=j)到伺服器,並進入SYN_SEND狀態,等待伺服器確認;
第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態;
第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和伺服器進入ESTABLISHED狀態,完成三次握手。
完成三次握手,客戶端與伺服器開始傳送數據,在上述過程中,還有一些重要的概念:
未連接隊列:在三次握手協議中,伺服器維護一個未連接隊列,該隊列為每個客戶端的SYN包(syn=j)開設一個條目,該條目表明伺服器已收到SYN包,並向客戶發出確認,正在等待客戶的確認包。這些條目所標識的連接在伺服器處於Syn_RECV狀態,當伺服器收到客戶的確認包時,刪除該條目,伺服器進入ESTABLISHED狀態。
Backlog參數:表示未連接隊列的最大容納數目。
SYN-ACK
重傳次數
伺服器發送完SYN-ACK包,如果未收到客戶確認包,伺服器進行首次重傳,等待一段時間仍未收到客戶確認包,進行第二次重傳,如果重傳次數超過系統規定的最大重傳次數,系統將該連接信息從半連接隊列中刪除。注意,每次重傳等待的時間不一定相同。
半連接存活時間:是指半連接隊列的條目存活的最長時間,也即服務從收到SYN包到確認這個報文無效的最長時間,該時間值是所有重傳請求包的最長等待時間總和。有時我們也稱半連接存活時間為Timeout時間、SYN_RECV存活時間。