目录

  • 带错误处理的回声服务器的实现
    • server.cpp
    • client.cpp
    • error_handling.cpp
    • error_handling.h
      • 遇到的问题,自定义函数显示未定义的引用。
        • 问题分析:
        • 同类问题:
      • 解决方法:
        • 编写我们的Makefile
    • 测试:
      • 正常测试:
      • 先关掉服务器,在关掉客户端马上在连接:
      • 其余测试:
  • 回声服务器的结语及下一部分的展望

带错误处理的回声服务器的实现

结合之前的博客我们已经知道了什么是网络编程以及如何通过调用socket函数来编写一个回声服务器,这个过程中我们可能遇到了很多的问题,但是我们都一点一点的克服了。这本身就是一种进步了。这个部分就是我们的简单的回声服务器的最后一篇博客了。在完成这篇博客之后可以进行下一个部分的学习(实现高并发http 服务器)。

server.cpp

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<ctype.h>#include "error_handling.h"#define SERVER_PORT 6666
#define MAXLINE 100int main(void) {/*定义  server端套接字文件描述符:sfdclient套接字文件描述符:cfdread函数读取到的字符数:n */int sfd, cfd, n;/* server端地址定义(包含IP、PORT、协议族)暂未指定:server_addrclient端地址定义(包含IP、PORT、协议族)不需要再server.c定义,accept函数会自动填充*/struct sockaddr_in server_addr, client_addr;socklen_t  client_len;//为 accept函数第三个参数做准备char buf[MAXLINE];//接收client端发来的字符的缓冲区/*bzero:将server端置空,为了给后续的IP、PORT、协议族赋值所用后续操作是为了 bind函数绑定IP、PORT和协议族的固定操作。*/bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;//IPV4server_addr.sin_port = htons(SERVER_PORT);//转换为网络字节序server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_addr.s_addr = htonl(INADDR_ANY);sfd = Socket(AF_INET, SOCK_STREAM, 0);    //调用 socket函数值后会返回一个文件描述符Bind(sfd, (struct sockaddr*)&server_addr, sizeof(server_addr));    //绑定IP、PORT、协议族Listen(sfd, 21);/* accept函数放在 while 里面和外面的结果是不一样的,accept放在while里面代表客户端只能和服务器端通信一次accept放在while外面那么客户端就可以一直和服务器进行通信*/client_len = sizeof(client_addr);cfd = Accept(sfd, (struct sockaddr*)&client_addr, &client_len);//accept调用和会给server端返回一个和client端连接好的socket。while (1) {n = Read(cfd, buf, MAXLINE);for (int i = 0; i < n; i++) {buf[i] = toupper(buf[i]);}Write(cfd, buf, n);}Close(cfd);return 0;}

client.cpp

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<ctype.h>#include "error_handling.h"#define SERVER_PORT 6666
#define MAXLINE 100int main(void) {int sfd, n;struct sockaddr_in server_addr;char buf[MAXLINE];bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);sfd = Socket(AF_INET, SOCK_STREAM, 0);connect(sfd, (struct sockaddr*)&server_addr, sizeof(server_addr));while (1) {fgets(buf, sizeof(buf), stdin);Write(sfd, buf, strlen(buf));n = read(sfd, buf, sizeof(buf));Write(STDOUT_FILENO, buf, n);}Close(sfd);return 0;}

error_handling.cpp

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<errno.h>
#include<ctype.h>#include "error_handling.h"void perr_exit(const char* s)
{perror(s); //输出打印信息。exit(-1);
}int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{int n;again:if ((n = accept(fd, sa, salenptr)) < 0) {if ((errno == ECONNABORTED) || (errno == EINTR))goto again;elseperr_exit("accept error");}return n;
}int Bind(int fd, const struct sockaddr* sa, socklen_t salen)
{int n;if ((n = bind(fd, sa, salen)) < 0)perr_exit("bind error");return n;
}int Connect(int fd, const struct sockaddr* sa, socklen_t salen)
{int n;n = connect(fd, sa, salen);if (n < 0) {perr_exit("connect error");}return n;
}int Listen(int fd, int backlog)
{int n;if ((n = listen(fd, backlog)) < 0)perr_exit("listen error");return n;
}int Socket(int family, int type, int protocol)
{int n;if ((n = socket(family, type, protocol)) < 0)perr_exit("socket error");return n;
}ssize_t Read(int fd, void* ptr, size_t nbytes)
{ssize_t n;again:if ((n = read(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}ssize_t Write(int fd, const void* ptr, size_t nbytes)
{ssize_t n;again:if ((n = write(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}int Close(int fd)
{int n;if ((n = close(fd)) == -1)perr_exit("close error");return n;
}/*参三: 应该读取的字节数*/
//socket 4096  readn(cfd, buf, 4096)   nleft = 4096-1500
ssize_t Readn(int fd, void* vptr, size_t n)
{size_t  nleft;              //usigned int 剩余未读取的字节数ssize_t nread;              //int 实际读到的字节数char* ptr;ptr = (char*)vptr;nleft = n;                  //n 未读取字节数while (nleft > 0) {if ((nread = read(fd, ptr, nleft)) < 0) {if (errno == EINTR)nread = 0;elsereturn -1;}else if (nread == 0)break;nleft -= nread;   //nleft = nleft - nread ptr += nread;}return n - nleft;
}ssize_t Writen(int fd, const void* vptr, size_t n)
{size_t nleft;ssize_t nwritten;const char* ptr;ptr = (char*)vptr;nleft = n;while (nleft > 0) {if ((nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR)nwritten = 0;elsereturn -1;}nleft -= nwritten;ptr += nwritten;}return n;
}ssize_t My_read(int fd, char* ptr)
{static int read_cnt;static char* read_ptr;static char read_buf[100];if (read_cnt <= 0) {again:if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {   //"hello\n"if (errno == EINTR)goto again;return -1;}else if (read_cnt == 0)return 0;read_ptr = read_buf;}read_cnt--;*ptr = *read_ptr++;return 1;
}/*readline --- fgets*/
//传出参数 vptr
ssize_t Readline(int fd, void* vptr, size_t maxlen)
{ssize_t n, rc;char    c, * ptr;ptr = (char*)vptr;for (n = 1; n < maxlen; n++) {if ((rc = My_read(fd, &c)) == 1) {   //ptr[] = hello\n*ptr++ = c;if (c == '\n')break;}else if (rc == 0) {*ptr = 0;return n - 1;}elsereturn -1;}*ptr = 0;return n;
}

error_handling.h

#ifndef __ERROR_HANDING__
#define __ERROR_HANDING__//下面的所有替换socket网络编程函数,在函数内部都增加了输出错误信息的判断语句。//打印输出错误信息
void perr_exit(const char* s);  //替换Socket函数,并在内部调用perr_exit 函数,可以输出错误信息
int Socket(int family, int type, int protocol);//替换Bind函数,并在内部调用perr_exit 函数,可以输出错误信息
int Bind(int fd, const struct sockaddr* sa, socklen_t salen);//替换Listen函数,并在内部调用perr_exit 函数,可以输出错误信息
int Listen(int fd, int backlog);//替换 accept 函数,并在内部调用perr_exit 函数,可以输出错误信息
int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr);//替换Connect函数,并在内部调用perr_exit 函数,可以输出错误信息
int Connect(int fd, const struct sockaddr* sa, socklen_t salen);//替换Close函数,并在内部调用perr_exit 函数,可以输出错误信息
int Close(int fd);ssize_t Read(int fd, void* ptr, size_t nbytes);
ssize_t Write(int fd, const void* ptr, size_t nbytes);ssize_t Readn(int fd, void* vptr, size_t n);
ssize_t Writen(int fd, const void* vptr, size_t n);
ssize_t My_read(int fd, char* ptr);
ssize_t Readline(int fd, void* vptr, size_t maxlen);#endif

遇到的问题,自定义函数显示未定义的引用。

server.c
client.c

问题分析:

Q1:先在 vs里面点击 server.cpp看能否跳转到我们自定义的错误函数处理部分。
A1::经测试发现能够直接跳转到我们自定义的错误处理函数,说明代码是没有错误的

Q2:是否是因为没有编译我们的 error_handling.cpp 导致的
测试:g++ error_handling.cpp -o error_handling

这里面连 main函数都没有定义,你又怎么可能编译成功呢?

同类问题:

c++中如何在主函数中调用其他文件内的函数?
其实大家也看到了,我们需要编写Makefile了,哈哈。以前大家被vs惯坏了,既然现在大家要投入linux中那么就需要去编写自己的Makefile了。博主对此不详细描述。

解决方法:

不再和大家开玩笑了,其实这就是vs的好处了,但现在我们要在linux下运行,我们就需要用到我们大名鼎鼎makefil了,在这里博主不详细介绍Makefile的编写规则,需要的话可以留言博主会开一个博客专门介绍makefil。

编写我们的Makefile

src = $(wildcard *.c)
obj = $(patsubst %.c, %.o, $(src))all: server clientserver: server.o error_handling.ogcc server.o error_handling.o -o server -Wall
client: client.o error_handling.ogcc client.o error_handling.o -o client -Wall%.o:%.cgcc -c $< -Wall.PHONY: clean all
clean: -rm -rf server client $(obj)

make

测试:

正常测试:

先关掉服务器,在关掉客户端马上在连接:

看到了吧,因为我们封装了错误处理函数,我们直接就能看到错误的原因。

其余测试:

还有很多种测试方法大家可以,具体的方案大家可以修改server.cpp的Accept函数和Close函数,以及client.cpp 的Close函数的位置来进行多种方案的测试,博主在这里就不在演示了。

回声服务器的结语及下一部分的展望

至此我们的 回声服务器已经是完成了,不说超级牛逼,但也能吊打很多回声服务器了(毕竟我们完成了各个函数的出错处理的方案,不需要大家在一步一步调试信息的慢慢寻找了。)

下一部分内容:

开始进入新的章节了

012.成型版回声服务器相关推荐

  1. C++回声服务器_3-UDP版本

    这次我们实现一个UDP版本的回声服务器. 用于传输数据的函数 UDP套接字不会像TCP套接字那样保持连接状态,因此每次传输数据都要添加目标地址信息. 用于传输数据的函数: 发送数据到目标服务器. #i ...

  2. 基于epoll实现一个IO多路复用的回声服务器

    任务: 实现一个TCP server的回声服务器,功能是将客户端发送的消息原样返回,应用epoll处理事件循环实现IO多路复用.借此任务理解IO多路复用应用的开发模式. 参考资料: http://ma ...

  3. Ubuntu桌面版与服务器版的区别

    Ubuntu桌面版vs服务器版 提到安装Linux,Ubuntu可谓是最受欢迎的.为了满足每个人的需求,出现了不少版本或风格的Ubuntu:其中两项便是桌面版与服务器版.只要发布版本号一致,这两者从核 ...

  4. 简单回声服务器的实现

    文章目录 1 简单回声服务器的实现 1 简单回声服务器的实现 实现非常非常简单,而且没啥实用价值,代码如下: server.c: #include <stdio.h> #include & ...

  5. boost::phoenix模块实现自适应回声服务器相关的测试程序

    boost::phoenix模块实现自适应回声服务器相关的测试程序 实现功能 C++实现代码 实现功能 boost::phoenix模块实现自适应回声服务器相关的测试程序 C++实现代码 #inclu ...

  6. epoll边缘触发_C++回声服务器_9-epoll边缘触发模式版本服务器

    epoll默认情况下是水平触发模式,这次将epoll设置为边缘触发模式来实现服务器,而客户端直接使用完美回声服务器的客户端. 服务器代码 #include #include #include #inc ...

  7. 单进程gevent版-TCP服务器(python 版)

    gevent版-TCP服务器 import sys import time import geventfrom gevent import socket,monkey monkey.patch_all ...

  8. 单进程epoll版-TCP服务器(python 版)

    epoll版-TCP服务器 1. epoll的优点: 没有最大并发连接的限制,能打开的FD(指的是文件描述符,通俗的理解就是套接字对应的数字编号)的上限远大于1024 效率提升,不是轮询的方式,不会随 ...

  9. 单进程select版-TCP服务器(python 版)

    select版-TCP服务器 1. select 原理 在多路复用的模型中,比较常用的有select模型和epoll模型.这两个都是系统接口,由操作系统提供.当然,Python的select模块进行了 ...

最新文章

  1. 教你如何运用python实现学生信息管理系统
  2. Zookeeper高级
  3. 一台物理机上VMware虚拟机实现拨号上网同时内网通信
  4. Java——集合的基本功能测试
  5. dsp处理浮点数_DSP学习笔记(二)——DSP中浮点数与定点数格式与运算处理
  6. javaFX学习之颜色选择器(ColorPicker)
  7. Java实习日记(1)
  8. 猜数字游戏:随机生成一个1-100之间的数据,提示用户猜测,猜大提示过大,猜小提示过小,直到猜中结束游戏
  9. Java毕业设计:人民医院体检预约系统(java+springboot+vue+mysql)
  10. asp.net mvc 实现判断用户是否登录的两种方式
  11. angularjs 动态监控数据
  12. 【JAVA长虹键法】第一式 初识设计模式(23种设计模式)
  13. 6年主导3个项目,我终于成了别人眼中的大神
  14. 【数据分析】—— 指标与指标体系
  15. 这些年过上幸福生活的程序员(中篇)
  16. 简历上的哪些内容才是 HR 眼中的干货?
  17. Hive启动报错 java.lang.RuntimeException: org.apache.hadoop.hive.ql.metadata.HiveException: java.lang
  18. C++ 7z解压缩编译及使用
  19. Python学习记录 使用tensorflow 2.8 完成猫狗识别 使用keras构建CNN神经网络
  20. 《信息学奥赛一本通(C++版)》求校体操队的人数

热门文章

  1. SQL实战之查找最晚入职员工的所有信息
  2. C++读取字符串中的数字的方法
  3. Ubuntu添加swap分区
  4. 中国农业种植施肥机械行业市场供需与战略研究报告
  5. 马化腾去年年薪同比下降 25%,腾讯的下一步怎么走?
  6. ​CSDN疯狂盲盒来啦!iPhone 12、机械键盘、Switch等你来拿!
  7. Linux Kernel 5.13 稳定版发布:初步支持 M1 芯片
  8. Google排名第一的编程语言,死磕它这两点,小白也能学的会!不信你看!
  9. iPhone、iPad明年或采用USB-C接口;虎牙回应央视点名网课内容充斥广告;Rust 1.44.0 发布| 极客头条...
  10. 99%的程序员都在用Lombok,原理竟然这么简单?