计算机网络课设之TCP通讯录 - DoctorXiong
这篇文章我主要是想对这学期末计算机网络课程设计所做的一个小项目也就是基于tcp协议的通讯录做一个总结梳理。项目的具体的代码实现是基于C语言,当然在此之前网上也有一些基于c++编写的tcp通讯录,原理都是相同的,最基本的还是如何灵活运用Socket套接字来发送和接受数据。因为之前并未系统学过有关socket通信的知识,所以在正式开始动手之前,博主还是查阅了一些相关的书籍,有了基本的知识储备,才能为后续的项目开发提供保障。废话不多说,开始进入正文。
开发环境:windows 10 开发工具: Visual Studio2017
1.首先要了解在tcp/ip网络环境下的客户端主机和服务器端主机的通信流程。即C/S模型。客户端在需要提供服务时向服务端发出请求,服务端等待客户端提出请求,服务端始终运行,监听网络接口,收到client请求启动服务进程响应客户同时继续监听服务窗口,保证后续的client也能连接上服务。
2.其次要掌握tcp通信所需要的几个主要功能函数。这里可以参考http://www.cnblogs.com/yuqiao/p/5786427.html
3.掌握面向连接的C/S程序的工作流程
服务端:
- 在服务端首先使用WSAStartup()函数检查系统协议栈使用情况
- 使用socket()函数创建服务器端的通信套接口
- 使用bind()函数将创建的套接口与服务器地址绑定
- 使用listen()函数使服务器套接口做好接受连接请求准备
- 使用accept()接收来自客户端由connect()函数发出的连接请求
- 连接建立后服务器端可以使用send()函数发送数据或者recv()函数接收数据()
- 使用closesocket()函数关闭套接口
- 最后调用WSACleanup()结束winsock sockets API
客户端:
1.在客户端首先使用WSAStartup()函数检查系统协议栈使用情况
2.使用socket()函数创建服务器端的通信套接口
3.使用connect()函数发出向服务器的连接请求
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() 主菜单
最终的效果图如下:
客户端:
服务端一级主菜单:
二级主菜单
文本存储:
六、附录 服务器端代码: //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; }
在完成这个小项目的过程中,可以说是历经坎坷,从最开始的一脸懵,到慢慢的上手做出来了一些东西,实现通讯录的功能,还是收获了不少。