文章目錄
- 前言
- 一、TCP原理
- 二、TCP編程步驟
- 三、代碼撰寫
- 四、代碼結果分析
前言
本文所寫代碼是基于linux下的編程
一、TCP原理
TCP:Transmission Control Protocol傳輸控制協議,TCP協議是一個面向連接的、可靠的、基于位元組流的傳輸層協議,

TCP實作原理為什么需要三次握手?兩次握手不可以?四次握手不可以?

TCP三次握手執行程序:
- 首先,服務端和客戶端都是處于CLOSED狀態的,然后服務端啟動,監聽埠,狀態變為LISTEN(監聽)狀態,
- 客戶端為了請求資源,發送連接,發送同步序列號SYN,此時客戶端就變成了SYN-SEND狀態,
- 服務端接收到客戶端請求之后,發送SYN和ACK,然后服務端狀態就變成SYN-RCVD狀態,
- 客戶端接收到資訊之后,再次發送ACK,然后變成ESTABLISHED(已確認)狀態,服務端接收到回傳資訊后,狀態也變成ESTABLISHED(已確認)狀態,
知道了TCP的三次握手的基本作業原理之后,就可以解釋為什么TCP需要三次握手?為什么不設計成兩次握手就可以?原因就是為了避免重復連接,在網路環境比較復雜的情況,客戶端可能會連續發送多次請求,如果只設計成兩次握手的情況,服務端只能一直接收請求,然后回傳請求資訊,也不知道客戶端是否請求成功,這些過期請求的話就會造成網路連接的混亂,所以設計成三次握手的情況,客戶端在接收到服務端SEQ+1的回傳訊息之后,就會知道這個連接是歷史連接,所以會發送報文給服務端,告訴服務端,所以TCP設計成三次握手的目的就是為了避免重復連接,當然也是可以設計為四次,五次,不過為了節約資源,三次握手就可以了,
二、TCP編程步驟
網路通信三要素: 源 目的 長度
服務器:
①用socket套接字獲取句柄fb = socket(xxx,xxx,xxx)和傳參設定相應的通訊模式,
②bind系結句柄bind(xxx,xxx,xxx),把fb句柄和IP,埠系結起來,
③listen(xxx,xxx)開始啟動監聽資料,
④accept(xxx,xxx,xxx)接受連接,同時可以從引數里獲取到發起連接的客服端的地址,
⑤使用send/recv進行資料的收發,
客服端:
①用socket套接字獲取句柄fb = socket(xxx,xxx,xxx)和傳參設定相應的通訊模式,
②connect(xxx,xxx,xxx)發起連接,需要知道服務器端的ip地址,填充進sockaddr_in結構體里面,發起連接,
③使用send/recv進行資料的收發,
三、代碼撰寫
server.c
/*具體的函式應該包含什么頭檔案可以在linux中的man手冊查找相應的函式所需要的頭檔案*/
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* bind
* listen
* accept
* send/recv
*/
#define SERVER_PORT 8888 //埠號為8888,在實體化結構體sockaddr_in中用到
#define BACKLOG 10 //同時可以監聽的數量10
int main(int argc, char **argv)
{
int iSocketServer;
int iSocketClient;
struct sockaddr_in tSocketServerAddr; //用于存放本機服務器的相關資訊
struct sockaddr_in tSocketClientAddr; //用于存放客服端的地址資訊
int iRet;
int iAddrLen;
int iRecvLen;
unsigned char ucRecvBuf[1000]; //接識訓沖區
int iClientNum = -1;
signal(SIGCHLD,SIG_IGN); //用于處理僵尸行程
iSocketServer = socket(AF_INET, SOCK_STREAM, 0); //ipv4,tcp,0
if (-1 == iSocketServer)
{
printf("socket error!\n");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; //本機全部地址
memset(tSocketServerAddr.sin_zero, 0, 8);
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("bind error!\n");
return -1;
}
iRet = listen(iSocketServer, BACKLOG);
if (-1 == iRet)
{
printf("listen error!\n");
return -1;
}
while (1)
{
iAddrLen = sizeof(struct sockaddr);
iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);//得到客服端的地址資訊
if (-1 != iSocketClient)
{
iClientNum++;
printf("Get connect from client %d : %s\n", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
if (!fork()) //創建新的行程執行if條件下的陳述句,主要是用于等待接收客服端發來的資料
{
while (1)
{
iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0); //接收客服端發來的資料
if (iRecvLen <= 0)
{
close(iSocketClient);
return -1;
}
else
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);
}
}
}
}
}
close(iSocketServer);
return 0;
}
client.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
/* socket
* connect
* send/recv
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iSocketClient;
struct sockaddr_in tSocketServerAddr; //這里需要實體化的是服務器的資訊,然后進行connet
int iRet;
unsigned char ucSendBuf[1000];
int iSendLen;
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
iSocketClient = socket(AF_INET, SOCK_STREAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
{
printf("invalid server_ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("connect error!\n");
return -1;
}
while (1)
{
if (fgets(ucSendBuf, 999, stdin)) //從控制臺獲取資料,然后發送給服務器
{
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}
四、代碼結果分析
①服務器中的代碼是INADDR_ANY; 使用本機的全部地址,所以在同一虛擬機上運行客服端連接哪個地址都行,但開發板上的是需要同一網段的,所以只能是192.168.1.11

②資料的傳輸

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/305640.html
標籤:其他
上一篇:軟體架構風格
下一篇:你想為開源社區做貢獻嗎?機會來了
