计算机网络课设之TCP通讯录
这篇文章我主要是想对这学期末计算机网络课程设计所做的一个小项目也就是基于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;
}
在完成这个小项目的过程中,可以说是历经坎坷,从最开始的一脸懵,到慢慢的上手做出来了一些东西,实现通讯录的功能,还是收获了不少。
计算机网络课设之TCP通讯录的更多相关文章
- 计算机网络课设之基于UDP协议的简易聊天机器人
前言:2017年6月份计算机网络的课设任务,在同学的帮助和自学下基本搞懂了,基于UDP协议的基本聊天的实现方法.实现起来很简单,原理也很简单,主要是由于老师必须要求使用C语言来写,所以特别麻烦,而且C ...
- 如何用 python 优雅地完成数据库课设
0 前言 偶然间发现 Google 收录了学校实验打卡系统的接口,正好要做数据库课设,便拿来作为 environment. 机房居然装了 python ,早就听说 python 写爬虫速度一流,课上的 ...
- C语课设心得分享(二)
咱们今儿说说IDE的事儿. IDE是「集成开发环境」的意思,比如咱们常用的VC6.0,就是开发C语言所用的IDE的一种.对于IDE的认识,可能有些朋友有点儿模糊,咱们捋一捋,我也会给出一些IDE方面的 ...
- C语课设心得分享(三)
调试. 以前咱们写课后习题,一般也不需要使用调试,如果程序编译error,根据错误信息就可以改好:如果是结果错误,那么在稿纸上过几遍基本也可以得出结果. 但咱们这个课设比较大,就需要很多调试的过程,尤 ...
- C语言课设心得分享(一)
今儿上完课设,老师果然讲的比较少,周四还不用去,看来还是学生自己折腾.我在做课设的过程中,攒了一些心得/体会,希望能和大家分享分享,也希望能一起探讨探讨.如果是我能回答的问题,我很乐意能够提供帮助. ...
- 将C语课设传到了Github和Code上 2015-91-18
一直听说Git好使,以前捣鼓过没弄成,现在考完试了终于可以静下心来研究研究. 哎,我要是当时做课设的时候就用Git,也能省下不少事呢. 使用的Git教程,刚看个开头: 廖雪峰的Git教程 http:/ ...
- JAVA课设---五子棋
1.团队博客链接 JAVA课设-五子棋-团队博客 2.个人负责模块: ①对鼠标事件的处理 , 此模块需处理五子棋的放置问题.颜色转换问题.以及当五子连线时弹出窗口显示结果. ②对MainFrame中主 ...
- JAVA课设个人博客--多源数据教学管理系统
JAVA课设个人博客--多源数据教学管理系统 1.团队课程设计博客链接 https://www.cnblogs.com/hq9-/p/10278470.html 2. 个人负责模块或任务说明 主要模块 ...
- 【js课设】电子画板01
这学期web开发课的课设选了电子画板课题.(人家本来想做富文本编辑器的嘛然鹅老师在第二版里把这题删掉了。゚ヽ(゚´Д`)ノ゚。) 主要考虑的有[界面美观][画笔类型][画布分层]这三个点了. [界面美 ...
随机推荐
- thinkphp 3.2.3 addAll方法的坑
在批量插入一组数据的时候,总是提示以下错误 Insert value list does not match column list: Column count doesn't match value ...
- WebService之SOAP
SOAP(Simple Object Access Protocol),它是一种标准消息传递协议,通常是Web Service的事实标准.SOAP是以XML为基础,SOAP消息格式是由XML Sche ...
- 岛屿的个数12 · Number of Islands12
[抄题]: 给一个01矩阵,求不同的岛屿的个数. 0代表海,1代表岛,如果两个1相邻,那么这两个1属于同一个岛.我们只考虑上下左右为相邻. [ [1, 1, 0, 0, 0], [0, 1, 0, 0 ...
- Java程序设计8——抽象类、接口与内部类
1 抽象类 当编写一个类时,常常会为该类定义一些方法,这些方法用以描述该类的行为方式,那么这些方法都有具体的方法体.但在某些情况下,某个父类并不需要实现,因为它只需要当做一个模板,而具体的实现,可以由 ...
- TableLayout 中不显示动态添加的tableRow
下面的代码不显示: TableRow lay = new TableRow(layIndex.getContext()); lay.setLayoutParams(lpRow); //layIndex ...
- Linux下的用户权限
用户权限: drwxr-xr-x root root - : boot #权限位 硬连接数 所属用户 所属组 大小 最后修改时间 文件/目录# r w x 4 2 1 用户权限位分为3段,分别对应US ...
- PHP(三)运算符、流程控制和函数初步
- line-height:150% 和 line-height:1.5
line-height属性的细节与大多数CSS属性不同,line-height支持属性值设置为无单位的数字.有无单位在子元素继承属性时有微妙的不同. 有单位(包括百分比)与无单位之间的区别有单位时,子 ...
- CocoaPods安装和使用教程[转]
目录 CocoaPods是什么? 如何下载和安装CocoaPods? 如何使用CocoaPods? 场景1:利用CocoaPods,在项目中导入AFNetworking类库 场景2:如何正确编译运行一 ...
- 自我简介与Github的注册和使用
我叫陈鑫,学号1413042059,来自网络工程142班.喜欢打乒乓球,玩策略类游戏,团队竞技. ...