这篇文章我主要是想对这学期末计算机网络课程设计所做的一个小项目也就是基于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程序的工作流程

服务端:

  1. 在服务端首先使用WSAStartup()函数检查系统协议栈使用情况
  2. 使用socket()函数创建服务器端的通信套接口
  3. 使用bind()函数将创建的套接口与服务器地址绑定
  4. 使用listen()函数使服务器套接口做好接受连接请求准备
  5. 使用accept()接收来自客户端由connect()函数发出的连接请求
  6. 连接建立后服务器端可以使用send()函数发送数据或者recv()函数接收数据()
  7. 使用closesocket()函数关闭套接口
  8. 最后调用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通讯录的更多相关文章

  1. 计算机网络课设之基于UDP协议的简易聊天机器人

    前言:2017年6月份计算机网络的课设任务,在同学的帮助和自学下基本搞懂了,基于UDP协议的基本聊天的实现方法.实现起来很简单,原理也很简单,主要是由于老师必须要求使用C语言来写,所以特别麻烦,而且C ...

  2. 如何用 python 优雅地完成数据库课设

    0 前言 偶然间发现 Google 收录了学校实验打卡系统的接口,正好要做数据库课设,便拿来作为 environment. 机房居然装了 python ,早就听说 python 写爬虫速度一流,课上的 ...

  3. C语课设心得分享(二)

    咱们今儿说说IDE的事儿. IDE是「集成开发环境」的意思,比如咱们常用的VC6.0,就是开发C语言所用的IDE的一种.对于IDE的认识,可能有些朋友有点儿模糊,咱们捋一捋,我也会给出一些IDE方面的 ...

  4. C语课设心得分享(三)

    调试. 以前咱们写课后习题,一般也不需要使用调试,如果程序编译error,根据错误信息就可以改好:如果是结果错误,那么在稿纸上过几遍基本也可以得出结果. 但咱们这个课设比较大,就需要很多调试的过程,尤 ...

  5. C语言课设心得分享(一)

    今儿上完课设,老师果然讲的比较少,周四还不用去,看来还是学生自己折腾.我在做课设的过程中,攒了一些心得/体会,希望能和大家分享分享,也希望能一起探讨探讨.如果是我能回答的问题,我很乐意能够提供帮助. ...

  6. 将C语课设传到了Github和Code上 2015-91-18

    一直听说Git好使,以前捣鼓过没弄成,现在考完试了终于可以静下心来研究研究. 哎,我要是当时做课设的时候就用Git,也能省下不少事呢. 使用的Git教程,刚看个开头: 廖雪峰的Git教程 http:/ ...

  7. JAVA课设---五子棋

    1.团队博客链接 JAVA课设-五子棋-团队博客 2.个人负责模块: ①对鼠标事件的处理 , 此模块需处理五子棋的放置问题.颜色转换问题.以及当五子连线时弹出窗口显示结果. ②对MainFrame中主 ...

  8. JAVA课设个人博客--多源数据教学管理系统

    JAVA课设个人博客--多源数据教学管理系统 1.团队课程设计博客链接 https://www.cnblogs.com/hq9-/p/10278470.html 2. 个人负责模块或任务说明 主要模块 ...

  9. 【js课设】电子画板01

    这学期web开发课的课设选了电子画板课题.(人家本来想做富文本编辑器的嘛然鹅老师在第二版里把这题删掉了。゚ヽ(゚´Д`)ノ゚。) 主要考虑的有[界面美观][画笔类型][画布分层]这三个点了. [界面美 ...

随机推荐

  1. pthread_t描述说明

    在posix线程api中,通过pthread_self(void) 函数获取当前线程的id 线程id的类型为pthread_t pthread.h 第267行声明了pthread_self (void ...

  2. 6-Ubuntu与Windows不能相互复制

    在安装Ubuntu系统后发现与Windows系统的文件不能相互复制,网上查了很多教程,发现都是不能用的,能实现的方法如下所示: 第一步: sudo apt-get autoremove open-vm ...

  3. maven项目 实现 spring mybatis 两个框架整合

    1.maven项目 src main java java源文件 resources 配置文件 beans.xml spring配置文件 <?xml version="1.0" ...

  4. 线程并发线程安全介绍及java.util.concurrent包下类介绍

    线程Thread,在Java开发中多线程是必不可少的,但是真正能用好的并不多! 首先开启一个线程三种方式 ①new Thread(Runnable).start() ②thread.start(); ...

  5. PBYTE

    typedef BYTE near           *PBYTE;

  6. DataStage 错误集(持续更新)

    DataStage 错误集(持续更新) DataStage序列文章 DataStage 一.安装 DataStage 二.InfoSphere Information Server进程的启动和停止 D ...

  7. centos下修改文件后如何保存退出

    centos下修改文件后如何保存退出 保存命令 按ESC键 跳到命令模式,然后: :w 保存文件但不退出vi :w file 将修改另外保存到file中,不退出vi :w! 强制保存,不推出vi :w ...

  8. Android Gson 操作

    JSON序列化后的数据不带类名与名命空间,所以这两个服务端跟客户端可以不对应,需要保证字段对应即可 Asp.net MVC端 using System; using System.Collection ...

  9. BPNN

    链接网址:http://blog.csdn.net/heyongluoyao8/article/details/48213345 BPNN 人工神经网络   我们知道,人的脑袋具有很强的学习.记忆.联 ...

  10. handsontable-developer guide-cell function

    renderer 展示的数据不是来自于数据源,而是先把DOM和其他信息传给renderer,然后展示. //五种展示函数 TextRenderer: default NumericRenderer A ...