# TCP connection #### 這是可以參考的資料 https://dangerlover9403.pixnet.net/blog/post/212391408-%5B%E6%95%99%E5%AD%B8%5Dc++-socket%E8%B3%87%E6%96%99%E6%95%B4%E7%90%86 ## 很搞剛的設定 如果你是用Visual C++ 來做這次的實作的話,建議你先做以下設定。 <b>專案</b> ==> <b>屬性</b> ==> <b>c/c++</b> ==> <b>前置處理器</b> ==> <b>點開前置處理器定義</b> ==> <b>編輯</b> 我們要來定義一寫東西 ![](https://i.imgur.com/5dXmhLQ.png) 加入 `` _CRT_SECURE_NO_WARNINGS `` 如此一來才可以在VC內用strcpy等<string.h>內定義的許多工具,否則他會一直跑warning教你改成strcpy_s \ \ `` _WINSOCK_DEPRECATED_NO_WARNINGS `` 如此一來才可以在VC內用inet_ntoa這個工具,否則他會一直跑教你調一些有的沒的 <b>這些設定簡單來說就是叫VC to Shut up,我想這樣寫你...你別管我!</b> \ \ ## 簡單介紹 首先我們先來定義一下傳輸的概念,當傳輸端(不管是server、client),都要必需先傳輸資料,並且進入一個while loop等待接收端的ACK回應,直到有接收到程式才可以繼續往下跑 ```c=21 void send_and_wait(){ send(clientSocket, temp, strlen(temp), 0); int bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } } ``` \ <b>我們可以看下面的應用</b> ```c=94 // Here is for the first menu strcpy(temp, "\n"); send_and_wait(); strcpy(temp, "--- Menu ---"); send_and_wait(); strcpy(temp, "1. Read all existing message."); send_and_wait(); strcpy(temp, "2. Write a new message"); send_and_wait(); strcpy(temp, "Please type \"1\"" " or \"2\" " "to select an option"); send_and_wait(); // // Finish line strcpy(temp, "#"); send_and_wait(); ``` 以上是server端傳給 client 的 Menu,先將要傳輸的資料填入全域變數temp內,在套用send_and_wait()來進行傳輸。 \ \ <b>client端這邊也有特別的東西</b> ```c=14 void receive_function() { int bytesRead = 0; while (1) { bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(serverSocket, buf, MAX_SIZE, 0); } send(serverSocket, buf, bytesRead, 0); if (buf[0] == '#') { break; } buf[bytesRead] = '\0'; printf("%s", buf); printf("\n"); } } ``` \ 這是client用來持續接收訊息的function,若收到server端傳來的終止訊號"#",就會斷開來。 # 程式簡介 ## server端 include 函式庫 ```c=2 #pragma comment(lib, "wsock32.lib") #include <winsock2.h> #include <stdio.h> #include <string.h> #define MAX_SIZE 2048 #define MY_ERROR(s) printf(s); system("PAUSE"); exit(1); char store[5000]; char temp[100]; int ptr = 0; char buf[MAX_SIZE + 1]; SOCKET serverSocket, clientSocket; // create a socket ``` \\ 特別的function ```c=14 void end() { for (int i = 0; i < 5; i++) { store[ptr] = '%'; ptr = ptr + 1; } } void send_and_wait(){ send(clientSocket, temp, strlen(temp), 0); int bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } } ``` send_and_wait()上面有提到 end()的用意是來做儲存料的尖閣區分,每筆資料用5個%來分隔 例如: 用戶輸入hello,再輸入yo9968,我會在store陣列內儲存hello%%%%%yo9968%%%%%,以用來做判斷上的區分。 \ \ \ fixed variable set port 預設port 為 83 ```c=35 int serverPort = 83; // here is for define IP port // If the file get input if (argc == 2) { serverPort = atoi(argv[1]); } ``` 這個前面的設定自己看,裡面有註解 ```c=44 // call WSAStartup first for Winsock WSADATA wsadata; // MAKEWORD(2,2) mean version 2.2 // the second parameter means open a wsadata if (WSAStartup(MAKEWORD(2, 2), (LPWSADATA)&wsadata) != 0) { printf("123"); MY_ERROR("Winsock Error\n"); } // Create socket // PF_INET is for ipv4 // SOCK_STREAM is for TCP serverSocket = socket(PF_INET, SOCK_STREAM, 0); // Set the server information // initialization address array memset(&serverAddress, 0, sizeof(serverAddress)); // just for IPV4 serverAddress.sin_family = AF_INET; // set the server IP to any serverAddress.sin_addr.s_addr = INADDR_ANY; // set the server Ip to the host //serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); serverAddress.sin_port = htons(serverPort); //converts a u_short from host to TCP/IP network byte order // Bind the socket if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { MY_ERROR("Bind Error\n"); } // Prepare to listen to the incoming clients // the second parmeter is for the max listening for client if (listen(serverSocket, 3) < 0) { MY_ERROR("Listen Error\n"); } ``` \ \ <b>進入while loop後.......</b> \ \ server 端要持續偵測是否有client要進來 ```c=87 // Accept a client clientAddressLen = sizeof(clientAddress); clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressLen); if (clientSocket == INVALID_SOCKET){ continue; } ``` \ \ 接下來要持續偵測,直到client有船東西過來,最後送ACK回去給client做確認 ```c=110 // Receive the data from the client, and send it back // it will return the data length bytesRead = 0; while(bytesRead == 0){ bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } buf[bytesRead] = '\0'; send(clientSocket, buf, bytesRead, 0); ``` \ \ 輸出傳輸狀況,以確認連線的正確性 ```c=119 //inet_ntoa function is for turn binary IP to decimal Ip address //printf("Client IP is : %s \n", inet_ntoa(clientAddress.sin_addr)); printf("Receive %d byte(s): %s\n", bytesRead, buf); printf("Client IP is : %s \n", inet_ntoa(clientAddress.sin_addr)); ``` \ \ <b>如果是Menu 的狀況二的話</b> \ \ 先傳給client前導訊息 ```c=123 if (buf[0] == '2' && strlen(buf) == 1) { // This is for initial strcpy(temp, "------------------------------"); send_and_wait(); strcpy(temp, "start to write something"); send_and_wait(); strcpy(temp, "#"); send_and_wait(); ``` \ \ 等待client傳入要寫入的資料 ```c=132 bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } printf("Receive %d byte(s): %s\n", bytesRead, buf); buf[bytesRead] = '\0'; for (int i = 0; i < bytesRead-1; i++) { store[ptr] = buf[i]; ptr = ptr + 1; } end(); ``` 最後傳輸終止訊號,告訴client停止持續接受訊息 ```c=144 strcpy(temp,"#"); send(clientSocket, temp, strlen(temp), 0); continue; } ``` \ \ <b>如果是Menu 的狀況一的話</b> \ \ 跟前面的概念差不多,就只是在做字串的解析而已 ```c=149 else if (buf[0] == '1' && strlen(buf) == 1) { // send the line first strcpy(temp, "------------------------------"); send_and_wait(); strcpy(temp, "All messages:"); send_and_wait(); // connection can't break for (int i = 0; i < ptr; i++) { if (store[i] != '%') { temp[piv] = store[i]; piv = piv + 1; continue; } else { for (int k = 1; k <= 4; k++) { if (store[i + k] != '%') { no = 1; break; } } } if (no == 1) { temp[piv] = store[i]; piv = piv + 1; no = 0; continue; } send(clientSocket, temp, piv, 0); bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } piv = 0; buf[bytesRead] = '\0'; i = i + 4; } strcpy(temp, "\n"); send_and_wait(); strcpy(temp, "\n"); send_and_wait(); send(clientSocket, "#", 1, 0); bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } ``` 最後記得要把socket關掉 ```c=213 closesocket(clientSocket); ``` \ \ ## Client端 我覺得概念差不多,我就不詳述了科科 # 完整code ## server.c ```c= #pragma comment(lib, "wsock32.lib") #include <winsock2.h> #include <stdio.h> #include <string.h> #define MAX_SIZE 2048 #define MY_ERROR(s) printf(s); system("PAUSE"); exit(1); char store[5000]; char temp[100]; int ptr = 0; char buf[MAX_SIZE + 1]; SOCKET serverSocket, clientSocket; // create a socket void end() { for (int i = 0; i < 5; i++) { store[ptr] = '%'; ptr = ptr + 1; } } void send_and_wait(){ send(clientSocket, temp, strlen(temp), 0); int bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } } int main(int argc, char* argv[]) { struct sockaddr_in serverAddress, clientAddress; // sockaddr_in int clientAddressLen; int bytesRead; int no = 0; int serverPort = 83; // here is for define IP port // If the file get input if (argc == 2) { serverPort = atoi(argv[1]); } // call WSAStartup first for Winsock WSADATA wsadata; // MAKEWORD(2,2) mean version 2.2 // the second parameter means open a wsadata if (WSAStartup(MAKEWORD(2, 2), (LPWSADATA)&wsadata) != 0) { printf("123"); MY_ERROR("Winsock Error\n"); } // Create socket // PF_INET is for ipv4 // SOCK_STREAM is for TCP serverSocket = socket(PF_INET, SOCK_STREAM, 0); // Set the server information // initialization address array memset(&serverAddress, 0, sizeof(serverAddress)); // just for IPV4 serverAddress.sin_family = AF_INET; // set the server IP to any serverAddress.sin_addr.s_addr = INADDR_ANY; // set the server Ip to the host //serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); serverAddress.sin_port = htons(serverPort); //converts a u_short from host to TCP/IP network byte order // Bind the socket if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { MY_ERROR("Bind Error\n"); } // Prepare to listen to the incoming clients // the second parmeter is for the max listening for client if (listen(serverSocket, 3) < 0) { MY_ERROR("Listen Error\n"); } int piv = 0; while (1) { // Accept a client clientAddressLen = sizeof(clientAddress); clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressLen); if (clientSocket == INVALID_SOCKET){ continue; } // Here is for the first menu strcpy(temp, "\n"); send_and_wait(); strcpy(temp, "--- Menu ---"); send_and_wait(); strcpy(temp, "1. Read all existing message."); send_and_wait(); strcpy(temp, "2. Write a new message"); send_and_wait(); strcpy(temp, "Please type \"1\"" " or \"2\" " "to select an option"); send_and_wait(); // // Finish line strcpy(temp, "#"); send_and_wait(); // Receive the data from the client, and send it back // it will return the data length bytesRead = 0; while(bytesRead == 0){ bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } buf[bytesRead] = '\0'; send(clientSocket, buf, bytesRead, 0); //inet_ntoa function is for turn binary IP to decimal Ip address //printf("Client IP is : %s \n", inet_ntoa(clientAddress.sin_addr)); printf("Receive %d byte(s): %s\n", bytesRead, buf); printf("Client IP is : %s \n", inet_ntoa(clientAddress.sin_addr)); if (buf[0] == '2' && strlen(buf) == 1) { // This is for initial strcpy(temp, "------------------------------"); send_and_wait(); strcpy(temp, "start to write something"); send_and_wait(); strcpy(temp, "#"); send_and_wait(); bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } printf("Receive %d byte(s): %s\n", bytesRead, buf); buf[bytesRead] = '\0'; for (int i = 0; i < bytesRead-1; i++) { store[ptr] = buf[i]; ptr = ptr + 1; } end(); strcpy(temp,"#"); send(clientSocket, temp, strlen(temp), 0); continue; } else if (buf[0] == '1' && strlen(buf) == 1) { // send the line first strcpy(temp, "------------------------------"); send_and_wait(); strcpy(temp, "All messages:"); send_and_wait(); // connection can't break for (int i = 0; i < ptr; i++) { if (store[i] != '%') { temp[piv] = store[i]; piv = piv + 1; continue; } else { for (int k = 1; k <= 4; k++) { if (store[i + k] != '%') { no = 1; break; } } } if (no == 1) { temp[piv] = store[i]; piv = piv + 1; no = 0; continue; } send(clientSocket, temp, piv, 0); bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } piv = 0; buf[bytesRead] = '\0'; i = i + 4; } strcpy(temp, "\n"); send_and_wait(); strcpy(temp, "\n"); send_and_wait(); send(clientSocket, "#", 1, 0); bytesRead = recv(clientSocket, buf, MAX_SIZE, 0); } else{ // error message strcpy(temp, "\n"); send_and_wait(); strcpy(temp, "==================================================="); send_and_wait(); strcpy(temp, "\n"); send_and_wait(); strcpy(temp, "This option is not in the menu, please enter again: "); send_and_wait(); strcpy(temp, "\n"); send_and_wait(); strcpy(temp, "==================================================="); send_and_wait(); strcpy(temp, "#"); send_and_wait(); } closesocket(clientSocket); } return 0; } ``` ## Client.c ```c= #pragma comment(lib,"Ws2_32.lib") #include <stdio.h> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #define MAX_SIZE 2048 #define ADDRESS_SIZE 20 #define MY_ERROR(s) printf(s); system("PAUSE"); exit(1); SOCKET serverSocket, clientSocket; char buf[MAX_SIZE + 1]; void receive_function() { int bytesRead = 0; while (1) { bytesRead = 0; while (bytesRead == 0) { bytesRead = recv(serverSocket, buf, MAX_SIZE, 0); } send(serverSocket, buf, bytesRead, 0); if (buf[0] == '#') { break; } buf[bytesRead] = '\0'; printf("%s", buf); printf("\n"); } } int main(int argc, char* argv[]) { int serverAddresslen; struct sockaddr_in serverAddress; int bytesRead; char serverAddressStr[ADDRESS_SIZE] ; strcpy(serverAddressStr,"127.0.0.1"); int serverPort = 83; if (argc == 3) { strcpy(serverAddressStr, argv[1]); serverPort = atoi(argv[2]); } // call WSAStartup first for Winsock WSADATA wsadata; if (WSAStartup(MAKEWORD(2, 2), (LPWSADATA)&wsadata) != 0) { MY_ERROR("Winsock Error\n"); } // Set the server information memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; //serverAddress.sin_addr.s_addr = inet_pton(AF_INET, serverAddressStr); // transform to 32-bit unsigned integer inet_pton(AF_INET, serverAddressStr, &serverAddress.sin_addr); serverAddress.sin_port = htons(serverPort); //converts a u_short from host to TCP/IP network byte order int gate = 0; while (1) { // Create socket serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (connect(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { MY_ERROR("Connect Error\n"); } receive_function(); scanf("%s", buf); buf[strlen(buf)] = '\0'; //printf("Server IP is : %s \n", inet_ntoa(serverAddress.sin_addr)); // Send the data to the server, and receive it back send(serverSocket, buf, strlen(buf)+1, 0); bytesRead = 0; while(bytesRead == 0){ bytesRead = recv(serverSocket, buf, MAX_SIZE, 0); } buf[bytesRead] = '\0'; if (buf[0] == '1' && strlen(buf) == 1) { receive_function(); printf("\n"); gate = 0; } else if (buf[0] == '2' && strlen(buf) == 1) { // start to write something receive_function(); scanf("%s", buf); buf[strlen(buf)] = '\0'; send(serverSocket, buf, strlen(buf)+1, 0); bytesRead = 0; while(bytesRead == 0){ bytesRead = recv(serverSocket, buf, MAX_SIZE, 0); } printf("%s", "New Message sent"); printf("%s", "\n"); buf[bytesRead] = '\0'; gate = 0; continue; } else { // receiving error message receive_function(); } closesocket(serverSocket); } return 0; } ```