TCP 伺服器/客戶端
TCP/IP : TCP/IP:在網路通訊中,TCP/IP是主流協議() 應用層:使用者自定義的協議(HTTP,EMAIL,),用於使用者之間資料的傳送 傳輸層:(傳輸控制:TCP,UDP)負責點對點之間連線建立,傳輸控制協議的指定() 網路層:用於查詢路由(查詢) 網路介面層 : 將二進位制轉換為資料幀。
TCP幀: [源端號][目的埠] [ 順序號 ] [TCP包頭長][URG/ACK/PSH/RST/SYN/FIN][視窗大小] [檢驗和][緊急指標] [可選項 ] [資料包]
TCP:面向連線,安全可靠有狀態的傳輸協議。(比UDP效率差) 怎樣連線:三次握手簡述(確保雙方一定同時線上) A與B建立TCP連線時:首先A向B發SYN(同步請求) 然後B回覆SYN+ACK(同步請求應答) 最後A回覆ACK確認 這樣TCP的一次連線(三次握手)的過程就建立了!
TCP握手協議詳述 : 在TCP/IP協議中,TCP協議提供可靠的連線服務,採用三次握手建立一個連線. 第一次握手:建立連線時,客戶端傳送syn包(syn=j)到伺服器,並進入SYN_SEND狀態,等待伺服器確認; SYN:同步序列編號(Synchronize Sequence Numbers) 第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也傳送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態; 第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包ACK(ack=k+1),此包傳送完畢,客戶端和伺服器進入ESTABLISHED狀態,完成三次握手. 完成三次握手,客戶端與伺服器開始傳送資料
所謂三次握手(Three-Way Handshake)即建立TCP連線,就是指建立一個TCP連線時,需要客戶端和服務端總共傳送3個包以確認連線的建立。在socket程式設計中,這一過程由客戶端執行connect來觸發,整個流程如下圖所示:
(1)第一次握手:Client將標誌位SYN置為1,隨機產生一個值seq=J,並將該資料包傳送給Server,Client進入SYN_SENT狀態,等待Server確認。 (2)第二次握手:Server收到資料包後由標誌位SYN=1知道Client請求建立連線,Server將標誌位SYN和ACK都置為1,ack=J+1,隨機產生一個值seq=K,並將該資料包傳送給Client以確認連線請求,Server進入SYN_RCVD狀態。 (3)第三次握手:Client收到確認後,檢查ack是否為J+1,ACK是否為1,如果正確則將標誌位ACK置為1,ack=K+1,並將該資料包傳送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連線建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸資料了。 SYN攻擊: 在三次握手過程中,Server傳送SYN-ACK之後,收到Client的ACK之前的TCP連線稱為半連線(half-open connect),此時Server處於SYN_RCVD狀態,當收到ACK後,Server轉入ESTABLISHED狀態。SYN攻擊就是Client在短時間內偽造大量不存在的IP地址,並向Server不斷地傳送SYN包,Server回覆確認包,並等待Client的確認,由於源地址是不存在的,因此,Server需要不斷重發直至超時,這些偽造的SYN包將產時間佔用未連線佇列,導致正常的SYN請求因為佇列滿而被丟棄,從而引起網路堵塞甚至系統癱瘓。SYN攻擊時一種典型的DDOS攻擊,檢測SYN攻擊的方式非常簡單,即當Server上有大量半連線狀態且源IP地址是隨機的,則可以斷定遭到SYN攻擊了,使用如下命令可以讓之現行: #netstat -nap | grep SYN_RECV
上面轉載到此處結束 ****/
TCP通訊: 伺服器: 1、建立套接字(開啟裝置) socket 2、繫結 bind 3、監聽:(設定最大的可連線的線路的數量) listen 4、等待連線:是一個阻塞函式(注:執行一該函式,則連線一次) int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 返回一個套接字,用於在此套接字上收發訊息 5、收到訊息 recv 6、關閉套接字 注:由於accept和recv都需要阻塞等待,accept返回新的連線描述符。 為每一個新的連線請求分配一個新的程序或執行緒
客戶端: 1、建立套接字(開啟裝置) 2、繫結套接字 3、發出連線請求 connect 4、收發訊息 send recv 5、關閉請求 close 傳輸層:UPD報文SOCK_DGRAM TCP:流式套接字SOCK_STREAM
注:由於套接字上繫結有埠號,為了防止複用埠號,套接字預設不允許複用。 API:裝置套接字 ----> setsockopt ( int sock,int level ) ; level:SOL_SOCKET 對套接字進行設定 socklen_t len=1 ; if ( setsockopt ( sock,SOL_SOCKET,SO_REUSEADDR,&len,sizeof(len) ) <0 ) { perror ( "setsocket fail" ) ; return -1 ; }
TCP 伺服器的程式碼實現 :
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
//建立鎖
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
//子執行緒:接收鏈路資訊
void* threadfun(void* arg)
{
int sock=*(int*)arg;
//解鎖
pthread_mutex_unlock(&mutex);
//接收訊息
char buf[100]="";
int ilen=0;
while(1)
{
ilen=recv(sock,buf,99,0);
if(ilen<=0)
break;
buf[ilen]='\0';
printf("收到:%s\n",buf);
}
close(sock);
}
//TCP:流式套接字
int main()
{
//1建立套接字---開啟裝置
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket fail");
return -1;
}
//修改套接字的網路層:允許IP複用
socklen_t len=1;
if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&len,sizeof(len))<0)
{
perror("setsocket fail");
return -1;
}
//2填充結構體並繫結
struct sockaddr_in myaddr; //7979;
memset(&myaddr,0,sizeof(myaddr));
myaddr.sin_family =AF_INET;
myaddr.sin_port =htons(7979);
myaddr.sin_addr.s_addr =INADDR_ANY;
if(bind(sock,(struct sockaddr*)&myaddr,sizeof(myaddr))==-1)
{
perror("bind fail");
return -1;
}
//3監聽:設定最大的可連線數量,並監聽
listen(sock,44);
//4等待連線 accept
int newsock=-1;
pthread_t tid;
while(1)
{
//上鎖
pthread_mutex_lock(&mutex);
newsock=accept(sock,NULL,NULL);
if(-1==newsock)
break;
//建立執行緒--函式
pthread_create(&tid,NULL,threadfun,&newsock);
//cout<<"有人連線我了\n"<<endl;
}
/*
//5等待該鏈路上訊息到來
char buf[100]="";
recv(newsock,buf,99,0);
printf("收到:%s\n",buf);
*/
//6關閉
close(sock);
return 0;
}
TCP 客戶端的程式碼實現 :
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<string.h>
int main()
{
//1建立套接字
int sock=socket(AF_INET,SOCK_STREAM,0); //SOCK_STREAM 流氏sock
//2[繫結:系統為該套接字預設繫結一個空的埠號和一個網絡卡地址]
//3主動發出連線請求
/*填充連線伺服器的IP資訊*/
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr)); //填充為0
saddr.sin_family =AF_INET;
saddr.sin_port =htons(7979);
saddr.sin_addr.s_addr =inet_addr("192.168.8.209");
if(connect(sock,(struct sockaddr*)&saddr,sizeof(saddr))<0) //連線
{
perror("connect fail");
return -1;
}
//4收發訊息
char buf[100]="";
while(1)
{
scanf("%s",buf); //客戶端可以連續的傳送內容
send(sock,buf,strlen(buf),0);//strlen 求字串的長度
}
//5關閉
close(sock);
}