這篇文章我主要是想對這學期末計算機網絡課程設計所作的一個小項目也就是基於tcp協議的通信錄作一個總結梳理。項目的具體的代碼實現是基於C語言,固然在此以前網上也有一些基於c++編寫的tcp通信錄,原理都是相同的,最基本的仍是如何靈活運用Socket套接字來發送和接受數據。由於以前並未系統學過有關socket通訊的知識,因此在正式開始動手以前,博主仍是查閱了一些相關的書籍,有了基本的知識儲備,才能爲後續的項目開發提供保障。廢話很少說,開始進入正文。html
開發環境:windows 10 開發工具: Visual Studio2017ios
1.首先要了解在tcp/ip網絡環境下的客戶端主機和服務器端主機的通訊流程。即C/S模型。客戶端在須要提供服務時向服務端發出請求,服務端等待客戶端提出請求,服務端始終運行,監聽網絡接口,收到client請求啓動服務進程響應客戶同時繼續監聽服務窗口,保證後續的client也能鏈接上服務。c++
2.其次要掌握tcp通訊所須要的幾個主要功能函數。這裏能夠參考http://www.cnblogs.com/yuqiao/p/5786427.htmlwindows
3.掌握面向鏈接的C/S程序的工做流程數組
服務端:服務器
客戶端:
1.在客戶端首先使用WSAStartup()函數檢查系統協議棧使用狀況網絡
2.使用socket()函數建立服務器端的通訊套接口socket
3.使用connect()函數發出向服務器的鏈接請求tcp
4鏈接創建後可使用send()函數發送數據或者recv()函數接收數據()函數
5.使用closesocket()函數關閉套接口
6.最後調用WSACleanup()結束winsock sockets API
服務端主要函數:
void MainMenu() 一級主菜單
void Menu() 二級主菜單
void Start() 建立套接字 等待客戶鏈接
void Add() 添加聯繫人信息
void Delete()刪除聯繫人
int Display() 顯示聯繫人信息
客戶端主要函數:
void Display() 顯示聯繫人信息
int Add() 添加聯繫人信息
void Start() 建立套接字
void Close_serve()關閉鏈接
int Menu() 主菜單
最終的效果圖以下:
客戶端:
服務端一級主菜單:
二級主菜單
文本存儲:
6、附錄 服務器端代碼: //addrserver.cpp #include "stdafx.h" #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <Winsock2.h> #include <stdio.h> #include <stdlib.h> #define DEFAULT_PORT 5050 //服務端默認端口 #pragma comment(lib, "wsock32.lib") //#include<iostream> //using namespace std; typedef struct Stu_Info { char cName[20]; char cAge[15]; char cSex[5]; char cUnit[20]; char cTel[30]; char cIp[30]; }Data; FILE *f; SOCKET sListen, sAccept; struct sockaddr_in ser, cli;//服務器和客戶的地址 char recbuff[4096];//字符數組,用來接收信息 char sendBuf[100];//用來發送信息 int buflen = sizeof(recbuff); int Display() { f = fopen("D:\\addr.txt", "r"); if (f == NULL) { printf("打開文件失敗!"); } int n = 0; int ch; while ((ch = fgetc(f)) != EOF) { if (ch == '\n') { n++; } } char LineBuff[2048]; fseek(f, 0, SEEK_SET); memset(LineBuff, 0, MAX_PATH); for(int i=0;i<n+1;i++) { fgets(LineBuff, MAX_PATH, f); printf("%s\n", LineBuff); } printf("\n"); return 1; } void Add() { Data dt; printf("請輸入姓名:\n"); scanf("%s", &dt.cName); printf("請輸入年齡:\n"); scanf("%s", &dt.cAge); printf("請輸入性別:\n"); scanf("%s", &dt.cSex); printf("請輸入所在單位:\n"); scanf("%s", &dt.cUnit); printf("請輸入電話號碼:\n"); scanf("%s", &dt.cTel); printf("請輸入IP地址:\n"); scanf("%s", &dt.cIp); f = fopen("D:\\addr.txt", "a"); if (f == NULL) { printf("打開文件失敗!"); } fprintf(f, "\n"); fprintf(f, "%s", dt.cName); fprintf(f, "\t"); fprintf(f, "%s", dt.cAge); fprintf(f, "\t"); fprintf(f, "%s", dt.cSex); fprintf(f, "\t"); fprintf(f, "%s", dt.cUnit); fprintf(f, "\t"); fprintf(f, "%s", dt.cTel); fprintf(f, "\t"); fprintf(f, "%s", dt.cIp); fclose(f); List.push_back(dt); Display(); } void Delete() { FILE *ff, *fp,*f; char a[1000];//放置fgets的某一行內容 char b[1000];//保存用戶輸入的內容 memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b)); printf("請輸入要刪除的姓名:\n"); scanf("%s", b);//輸入要刪除的內容 //strcat(b, "\n");//由於fgets函數在讀取的字符後面自動添加一個換行符,爲能用strcmp比較a和b,這裏爲b加上「\n」 ff = fopen("D:\\addr.txt", "r+");//讀打開原文件index.txt fp = fopen("D:\\result.txt", "w+");//寫打開臨時文件result.txt if (ff == NULL || fp == NULL) { printf("打開文件失敗\n"); exit(0);//退出 } while (fgets(a, 1000, ff))//逐行執行index.tzt裏面的內容 { if (a[0] != b[0] && a[1] != b[1] && a[2] != b[2])//若是與輸入的內容不相等temp不等於0,則不刪除 { //printf("%s", a);//將不刪除的內容輸出在控制檯上(跟result文件內容是一致的) fputs(a, fp);//將不刪除的內容寫入result.txt中 } } fclose(ff); fclose(fp);//關閉result文件 f = fopen("D:\\result.txt", "rt+"); if (f == NULL) { printf("打開文件失敗!"); } ff = fopen("D:\\addr.txt", "w"); fseek(ff, 0, SEEK_END); char* text; fseek(f, 0, SEEK_END); long lSize = ftell(f); // 用完後須要將內存free掉 text = (char*)malloc(lSize + 1); memset(text, 0, lSize); rewind(f); //文件指針從新指向文件開頭 fread(text, sizeof(char), lSize, f); text[lSize + 1] = '\0'; fprintf(ff, "%s", text); fclose(ff); system("pause"); } int Menu() { system("color 0a"); printf("******************************************歡迎使用基於TCP的通信錄***********************\n"); printf("\t\t\t1.顯示全部聯繫人信息\n"); printf("\t\t\t2.添加聯繫人信息\n"); printf("\t\t\t3.刪除聯繫人信息\n"); printf("\t\t\t0.退出通信錄\n"); printf("*****************************************************************************************\n"); printf("請輸入您想要執行的功能序號1/2/3/0\n"); int select; scanf("%d", &select); switch (select) { case 0: exit(0); break; case 1: Display(); break; case 2: Add(); break; case 3: Delete(); break; default: printf("無查詢功能!"); break; } return select; } int OnReceive() { char tmpbuf[3]; if (recv(sAccept, tmpbuf, sizeof(tmpbuf), 0) <= 0) { return false; } char cmd = tmpbuf[0]; //printf("cmd is %c\n", cmd); int n = 0; int ch; long lSize; char LineBuff[1024]; switch (cmd) { case '1': f = fopen("D:\\addr.txt", "r"); if (f == NULL) { printf("打開文件失敗!"); } char* text; fseek(f, 0, SEEK_END); lSize = ftell(f); // 用完後須要將內存free掉 text = (char*)malloc(lSize + 1); rewind(f); fread(text, sizeof(char), lSize, f); text[lSize] = '\0'; int n; if (n = send(sAccept, text, strlen(text), 0)) { printf("%d\t", n); printf("%s\n", text); } else { printf("error"); } break; case '2': Data dt; FILE *f; recv(sAccept, (char*)&dt, sizeof(Data), 0); //List.push_back(dt); f = fopen("D:\\addr.txt", "a"); if (f == NULL) { printf("打開文件失敗!"); } fprintf(f, "\n"); fprintf(f, "%s", dt.cName); fprintf(f, "\t"); fprintf(f, "%s", dt.cAge); fprintf(f, "\t"); fprintf(f, "%s", dt.cSex); fprintf(f, "\t"); fprintf(f, "%s", dt.cUnit); fprintf(f, "\t"); fprintf(f, "%s", dt.cTel); fprintf(f, "\t"); fprintf(f, "%s", dt.cIp); fclose(f); Display(); break; case '3': break; case '0': break; default: printf("無此功能!"); break; } return 1; } int Start() { int iPort = DEFAULT_PORT; WSADATA wsaData; //SOCKET sListen, sAccept; int iLen; //客戶地址長度 int iSend;//發送數據長度 //char buf[] = "I am a server";//要發送給客戶的信息 //struct sockaddr_in ser, cli;//服務器和客戶的地址 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to load Winsock.\n"); return -1; } sListen = socket(AF_INET, SOCK_STREAM, 0);//建立服務器端套接口 if (sListen == INVALID_SOCKET) { printf("socket() Failed: %d\n", WSAGetLastError()); return -1; } //如下創建服務器端地址 //使用IP地址族 ser.sin_family = AF_INET; //使用htons()把雙字節主機序端口號轉換爲網絡字節序端口號 ser.sin_port = htons(iPort); //htonl()把一個四字節主機序IP地址轉換爲網絡字節序主機地址 //使用系統指定的IP地址INADDR_ANY ser.sin_addr.s_addr = htonl(INADDR_ANY); //bind()函數進行套接字與地址的綁定 if (bind(sListen, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR) { printf("bind() Failed: %d\n", WSAGetLastError()); return -1; } //進入監聽狀態 if (listen(sListen, 5) == SOCKET_ERROR) { printf("lisiten() Failed: %d\n", WSAGetLastError()); return -1; } else { printf("服務器已開啓等待客戶端的鏈接...........\n"); } //初始化客戶地址長度參數 iLen = sizeof(cli); //進入一個無限循環,等待客戶的鏈接請求 while (1) { sAccept = accept(sListen, (struct sockaddr *)&cli, &iLen); if (sAccept == INVALID_SOCKET) { printf("accept() Failed: %d\n", WSAGetLastError()); return -1; } //輸出客戶IP地址和端口號 printf("Accepted clientIP:[%s], port : [%d]\n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port)); f = fopen("D:\\addr.txt", "rt+"); if (f == NULL) { printf("打開文件失敗!"); } char* text; fseek(f, 0, SEEK_END); long lSize = ftell(f); // 用完後須要將內存free掉 text = (char*)malloc(lSize + 1); memset(text, 0, lSize); rewind(f); //文件指針從新指向文件開頭 fread(text, sizeof(char), lSize, f); text[lSize+1] = '\0'; int n; printf("%s", text); send(sAccept, text, strlen(text), 0); fclose(f); //free(text); OnReceive(); closesocket(sAccept); } closesocket(sListen); WSACleanup(); } void MainMenu() { system("color 0a"); printf("************************歡迎來到服務器端界面*****************\n"); printf("\t\t\t1.進入開始菜單 \n"); printf("\t\t\t2.進入服務器響應界面\n"); printf("\t\t\t3.退出 \n"); printf("請輸入您要執行的操做(1/2)\n"); int select; scanf("%d", &select); switch (select) { case 1: Menu(); break; case 2: Start(); case 3: exit(0); default: break; } } int main(int argc, char* argv[]) { while (true) { MainMenu(); } return 0; system("pause"); } 客戶端代碼: //addrclient.cpp #include "stdafx.h" #define _WINSOCK_DEPRECATED_NO_WARNINGS #define _AFXDLL #include <Winsock2.h> #include <stdio.h> #include "stdlib.h" #define DATA_BUFFER 1024 //默認緩衝區大小 #pragma comment(lib, "wsock32.lib") #include<string.h> #include"conio.h" #include"process.h" char buf[DATA_BUFFER];//接收數據的緩衝區 接收服務器端發送來的全部文件記錄信息 SOCKET sClient; char recBuf[100]; struct sockaddr_in ser = { 0 }; typedef struct Stu_Info //定義一個結構體類型的變量 存儲要添加的一條聯繫人記錄 { char cName[20]; char cAge[15]; char cSex[5]; char cUnit[20]; char cTel[30]; char cIp[30]; }Data; void Display() { char recbuf[1024] = { 0 }; system("color 0a"); send(sClient, "1", strlen("1"), 0); //memset(recbuf23, 0, sizeof(recbuf)); Sleep(1000); if (int n = recv(sClient, recbuf, strlen(recbuf), 0)) //memcpy(data, recbuf, strlen(recbuf)); { printf("接收成功%d", n); printf("%s\n", recbuf); memset(recbuf, 0, sizeof(recbuf)); } else { printf("%d\n", n); printf("接收失敗 %d\n", WSAGetLastError()); } Sleep(1000); } void Start() { WSADATA wsaData; //char buf[DATA_BUFFER];//接收數據的緩衝區 //struct sockaddr_in ser = { 0 };//服務器端地址 //判斷參數輸入是否正確:client [Server IP] memset(buf, 0, sizeof(buf));//接收緩衝區初始化 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to load Winsock.\n"); } //填寫要鏈接的服務器地址信息 ser.sin_family = AF_INET; ser.sin_port = htons(5050); ser.sin_addr.s_addr = inet_addr("127.0.0.1"); //ser.sin_addr.S_un.S_addr = inet_addr(INADDR_ANY); //創建客戶端流式套接口 sClient = socket(AF_INET, SOCK_STREAM, 0); if (sClient == INVALID_SOCKET) { printf("socket() Failed: %d\n", WSAGetLastError()); } //請求與服務器端創建TCP鏈接 if (connect(sClient, (struct sockaddr *)&ser, sizeof(ser)) == INVALID_SOCKET) { printf("connect() Failed: %d\n", WSAGetLastError()); } else { //從服務器端接收數據 int iLen = recv(sClient, buf, sizeof(buf), 0); //printf(" %s\n", buf); } } void Close_serve() { closesocket(sClient); WSACleanup(); } void Add() { Data dt; printf("請輸入姓名:\n"); scanf("%s",&dt.cName); printf("請輸入年齡:\n"); scanf("%s", &dt.cAge); printf("請輸入性別:\n"); scanf("%s", &dt.cSex); printf("請輸入所在單位:\n"); scanf("%s", &dt.cUnit); printf("請輸入電話號碼:\n"); scanf("%s", &dt.cTel); printf("請輸入IP地址:\n"); scanf("%s", &dt.cIp); int len = sizeof(SOCKADDR); char sendbuff[1024]; memset(sendbuff, 0, 1024); memcpy(sendbuff, &dt, sizeof(dt)); send(sClient, "2", sizeof("2"), 0); send(sClient,(char*)&dt, sizeof(Data), 0); } int Menu() { system("color 0a"); printf("******************************************歡迎使用基於TCP的通信錄***********************\n"); printf("\t\t\t1.顯示全部聯繫人信息\n"); printf("\t\t\t2.添加聯繫人信息\n"); printf("\t\t\t3.退出通信錄\n"); printf("*****************************************************************************************\n"); printf("請輸入您想要執行的功能序號1/2/3\n"); int select; scanf("%d", &select); switch (select) { case 1: Start(); //Display(); printf("%s\n", buf); Close_serve(); break; case 2: Start(); Sleep(1000); Add(); Close_serve(); break; case 3: exit(0); break; default: printf("無查詢功能!"); break; } return select; } int main(int argc, char * argv[]) { while (true) { Menu(); } system("pause"); return 0; }
在完成這個小項目的過程當中,能夠說是歷經坎坷,從最開始的一臉懵,到慢慢的上手作出來了一些東西,實現通信錄的功能,仍是收穫了很多。