TCP把鏈接做爲最基本的對象,每一條TCP鏈接都有兩個端點,這種端點咱們稱爲套接字(socket)。端口號拼接到IP地址後面就構成了套接字,例如192.0.0.32:80。IP協議雖然能把數據報文送到目的主機,可是並無交付給主機的具體應用進程,而端到端的通訊纔是應用進程之間的通訊。html
TCP報文是TCP層傳輸的數據單元,也叫報文段。TCP報文的格式以下圖:
TCP報文中的標誌位:編程
TCP通訊時的創建鏈接須要三次握手,意思是創建鏈接的時候,客戶端與服務器之間須要三次數據包的交流。
服務器
TCP是以段爲單位發送數據的,在創建TCP鏈接的同時,每一個數據包的長度也被肯定下來,通常稱其爲最大消息長度(MSS: Maximum Segment Size)。TCP在傳輸大量數據時,是以MSS的大小將數據進行分割發送的,進行重發時也是以MSS爲單位。兩端的主機在發出創建鏈接的請求時,會在TCP首部中寫入MSS選項,告訴對方本身的接口可以適應的MSS的大小。爲附加MSS選項,TCP的首部將再也不是20字節,而是4字節的整數倍。會在二者之間選擇一個較小的值投入使用。
socket
四次揮手,意思就是釋放鏈接的時候客戶端與服務器之間須要四次數據包的交流。
tcp
socket編程通常採用客戶端-服務器模式(即由客戶進程向服務器進程發出請求,服務器進程執行請求的任務並將執行結果返回給客戶進程的模式)ide
TCP客戶端socket編程流程函數
hostent結構體 The hostent structure is defined in <netdb.h> as follows: struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses */ } #define h_addr h_addr_list[0] /* for backward compatibility */ The members of the hostent structure are: h_name The official name of the host. h_aliases An array of alternative names for the host, terminated by a NULL pointer. h_addrtype The type of address; always AF_INET or AF_INET6 at present. h_length The length of the address in bytes. h_addr_list An array of pointers to network addresses for the host (in network byte order), terminated by a NULL pointer. h_addr The first address in h_addr_list for backward compatibility.
static const char send_data[] = "This is TCP Client from RT-Thread."; /* 發送用到的數據 */ void tcpclient(int argc, char **argv) { int ret; char *recv_data; struct hostent *host; int sock, bytes_received; struct sockaddr_in server_addr; const char *url; int port; if (argc < 3) { rt_kprintf("Usage: tcpclient URL PORT\n"); rt_kprintf("Like: tcpclient 192.168.12.44 5000\n"); return ; } url = argv[1]; port = strtoul(argv[2], 0, 10); /* 經過函數入口參數url得到host地址(若是是域名,會作域名解析) */ host = gethostbyname(url); /* 分配用於存放接收數據的緩衝 */ recv_data = rt_malloc(BUFSZ); if (recv_data == RT_NULL) { rt_kprintf("No memory\n"); return; } /* 建立一個socket,類型是SOCKET_STREAM,TCP類型 */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { /* 建立socket失敗 */ rt_kprintf("Socket error\n"); /* 釋放接收緩衝 */ rt_free(recv_data); return; } /* 初始化預鏈接的服務端地址 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr = *((struct in_addr *)host->h_addr); rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); /* 鏈接到服務端 */ if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { /* 鏈接失敗 */ rt_kprintf("Connect fail!\n"); closesocket(sock); /*釋放接收緩衝 */ rt_free(recv_data); return; } else { /* 鏈接成功 */ rt_kprintf("Connect successful\n"); } while (1) { /* 從sock鏈接中接收最大BUFSZ - 1字節數據 */ bytes_received = recv(sock, recv_data, BUFSZ - 1, 0); if (bytes_received < 0) { /* 接收失敗,關閉這個鏈接 */ closesocket(sock); rt_kprintf("\nreceived error,close the socket.\r\n"); /* 釋放接收緩衝 */ rt_free(recv_data); break; } else if (bytes_received == 0) { /* 默認 recv 爲阻塞模式,此時收到0認爲鏈接出錯,關閉這個鏈接 */ closesocket(sock); rt_kprintf("\nreceived error,close the socket.\r\n"); /* 釋放接收緩衝 */ rt_free(recv_data); break; } /* 有接收到數據,把末端清零 */ recv_data[bytes_received] = '\0'; if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0) { /* 若是是首字母是q或Q,關閉這個鏈接 */ closesocket(sock); rt_kprintf("\n got a 'q' or 'Q',close the socket.\r\n"); /* 釋放接收緩衝 */ rt_free(recv_data); break; } else { /* 在控制終端顯示收到的數據 */ rt_kprintf("\nReceived data = %s ", recv_data); } /* 發送數據到sock鏈接 */ ret = send(sock, send_data, strlen(send_data), 0); if (ret < 0) { /* 接收失敗,關閉這個鏈接 */ closesocket(sock); rt_kprintf("\nsend error,close the socket.\r\n"); rt_free(recv_data); break; } else if (ret == 0) { /* 打印send函數返回值爲0的警告信息 */ rt_kprintf("\n Send warning,send function return 0.\r\n"); } } return; }
static void tcpserv(int argc, char **argv) { char *recv_data; /* 用於接收的指針,後面會作一次動態分配以請求可用內存 */ socklen_t sin_size; int sock, connected, bytes_received; struct sockaddr_in server_addr, client_addr; rt_bool_t stop = RT_FALSE; /* 中止標誌 */ int ret; recv_data = rt_malloc(BUFSZ + 1); /* 分配接收用的數據緩衝 */ if (recv_data == RT_NULL) { rt_kprintf("No memory\n"); return; } /* 一個socket在使用前,須要預先建立出來,指定SOCK_STREAM爲TCP的socket */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { /* 建立失敗的錯誤處理 */ rt_kprintf("Socket error\n"); /* 釋放已分配的接收緩衝 */ rt_free(recv_data); return; } /* 初始化服務端地址 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(5000); /* 服務端工做的端口 */ server_addr.sin_addr.s_addr = INADDR_ANY; rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); /* 綁定socket到服務端地址 */ if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { /* 綁定失敗 */ rt_kprintf("Unable to bind\n"); /* 釋放已分配的接收緩衝 */ rt_free(recv_data); return; } /* 在socket上進行監聽 */ if (listen(sock, 5) == -1) { rt_kprintf("Listen error\n"); /* release recv buffer */ rt_free(recv_data); return; } rt_kprintf("\nTCPServer Waiting for client on port 5000...\n"); while (stop != RT_TRUE) { sin_size = sizeof(struct sockaddr_in); /* 接受一個客戶端鏈接socket的請求,這個函數調用是阻塞式的 */ connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size); /* 返回的是鏈接成功的socket */ if (connected < 0) { rt_kprintf("accept connection failed! errno = %d\n", errno); continue; } /* 接受返回的client_addr指向了客戶端的地址信息 */ rt_kprintf("I got a connection from (%s , %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); /* 客戶端鏈接的處理 */ while (1) { /* 發送數據到connected socket */ ret = send(connected, send_data, strlen(send_data), 0); if (ret < 0) { /* 發送失敗,關閉這個鏈接 */ closesocket(connected); rt_kprintf("\nsend error,close the socket.\r\n"); break; } else if (ret == 0) { /* 打印send函數返回值爲0的警告信息 */ rt_kprintf("\n Send warning,send function return 0.\r\n"); } /* 從connected socket中接收數據,接收buffer是1024大小,但並不必定可以收到1024大小的數據 */ bytes_received = recv(connected, recv_data, BUFSZ, 0); if (bytes_received < 0) { /* 接收失敗,關閉這個connected socket */ closesocket(connected); break; } else if (bytes_received == 0) { /* 打印recv函數返回值爲0的警告信息 */ rt_kprintf("\nReceived warning,recv function return 0.\r\n"); closesocket(connected); break; } /* 有接收到數據,把末端清零 */ recv_data[bytes_received] = '\0'; if (strcmp(recv_data, "q") == 0 || strcmp(recv_data, "Q") == 0) { /* 若是是首字母是q或Q,關閉這個鏈接 */ closesocket(connected); break; } else if (strcmp(recv_data, "exit") == 0) { /* 若是接收的是exit,則關閉整個服務端 */ closesocket(connected); stop = RT_TRUE; break; } else { /* 在控制終端顯示收到的數據 */ rt_kprintf("RECEIVED DATA = %s \n", recv_data); } } } /* 退出服務 */ closesocket(sock); /* 釋放接收緩衝 */ rt_free(recv_data); return ; }
本文做者: CrazyCatJackui
本文連接: https://www.cnblogs.com/CrazyCatJack/p/14408903.html編碼
版權聲明:本博客全部文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!url
關注博主:若是您以爲該文章對您有幫助,能夠點擊文章右下角推薦一下,您的支持將成爲我最大的動力!