1. 程式人生 > >TCP 伺服器/客戶端

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);
}