文章目录

  • UDP实现CS
  • UDP_server
    • 创建、初始化socket
    • 准备接受数据
    • 开始接/发数据
  • UDP_client
    • 创建、初始化socket
    • 准备接受数据
    • 开始发/接数据
  • TCP实现CS
  • TCP_Client
    • 创建、初始化socket
    • 创建连接&准备发/收数据
  • TCP_Server
    • 创建、初始化socket
    • 接受数据
    • 发送数据
  • Source code
    • UDP_server
    • UDP_Client
    • TCP_Client
    • TCP_server

阿里嘎多,来为计网实验做个准备

本文代码运行环境为 linux系统

你可以在我的github仓库中找到源代码,或者在本文底部 可以star一波 小声bb

要构建一个应用程序

  • 首先要选择它的结构,有两种典型架构: CS Client-ServerP2P peer to peer。其中CS架构是我们最常接触和使用到的。

  • 接下来我们要实现进程间通信。 这就是今天我们要干的事情:在CS架构下实现 Client与server的通信。

网络进程间通信是通过 socket(应用层与运输层之间的接口) 来向网络 发送/接收 报文实现的。 大多数语言都为我们提供了socket API。 我们能做的就是 使用这些API,选择运输层参数,实现通信。

UDP实现CS

在这部分我们实现一个 server将client发送内容转化为大写并返回的功能

UDP_server

首先分析一下 UDP协议下server需要做什么。

  1. 创建socket,绑定自己的ip和端口。 这是server和client都需要做的 *应用层必须按协议栈向下运行才能实现通信。*而socket正是这个应用层与下层的接口。
  2. 从socket读取client发送来的内容。
  3. 通过socket,确定目的地(client的ip和端口号),发送响应内容。

接下来我们来一步步实现

创建、初始化socket

 //创建Udp_socket,socket函数来自 sys/socket.h, IPPOROTO_UDP来自netinet/in.hint Udp_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

socket函数原型为:

int socket(int af, int type, int protocol);

af代表地址族(IP),一般为ipv4或ipv6

其中AF_INET代表IPV4 ip地址,SOCK_DGRAM表示是无保障传输,protocol即运输协议。

我们在这里就是声明了一个UDP的socket

for more information

为了能让client能访问到我们,我们使用bind函数进行绑定,将server的套接字与一个IP和一个端口相连。

 struct sockaddr_in server_addr;//     struct sockaddr_in{//     sa_family_t     sin_family;   //地址族(Address Family),也就是地址类型//     uint16_t        sin_port;     //16位的端口号//     struct in_addr  sin_addr;     //32位IP地址//     char            sin_zero[8];  //不使用,一般用0填充// };//     struct in_addr{//            in_addr_t  s_addr;  //32位的IP地址//       };memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(1234);//转为大端序server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//function from arpa/inet.hbind(Udp_socket,(struct sockaddr*)&server_addr,sizeof(server_addr));

这里一下子多了很多内容,我们来看一下。

首先是结构体 sockaddr_in,他的内容已在上面代码写出。

​ 首先是 sin_family成员,和sock函数的第一个参数含义相同,取值也要相同。IPV4, AF_INET

​ sin_port为要绑定的端口号。理论取值为 0 ~65536,但因为 0 ~1023端口一般都分配给特点的应用,所 以尽量避免使用。 可以在这个范围里随意选一个

​ sin_addr竟然也是一个结构体…用来绑定IP

bind函数原型:

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);  //Linux

关于sockaddr和sockaddr_in的区别详见: 这里

准备接受数据

芜湖,我们终于结束了折磨人的初始化环节,接下来就简单了!

我们首先要申请缓冲区来读写数据,并申请一些变量。就像Linux中通过文件描述符读写文件也需要缓冲区一样

char recv_buf[50];//接受缓冲区
char send_buf[50];//发送缓冲区
int recv_num;//接收字节数
int send_num;//发送字节数
struct sockaddr_in client_addr;//客户地址
int len=sizeof(client_addr);//客户地址长度

为了将数据发送给client,我们也需要他的IP和端口号这就是在这里声明一个 client_addr的原因。

开始接/发数据

    while(true){cout<<"waiting for data"<<endl;//从client端接收数据recv_num=recvfrom(Udp_socket,recv_buf,sizeof(recv_buf)-1,0,(struct sockaddr*)&client_addr,(socklen_t *)&len);//检验错误if(recv_num<0){cout<<"receive ERROR"<<endl;exit(1);}recv_buf[recv_num]='\0';//添加字符串结束符cout<<"Receive "<<recv_num<<"bytes: "<<recv_buf<<endl;//实现server功能,即转化为大写。for(int i=0;i<recv_num;i++){if(recv_buf[i]>='a'&&recv_buf[i]<='z')send_buf[i]=toupper(recv_buf[i]);}send_buf[recv_num]='\0';//发送转化完的数据send_num=sendto(Udp_socket,send_buf,recv_num,0,(struct sockaddr*)&client_addr,len);if(send_num<0){cout<<"send ERROR"<<endl;exit(1);}}

可以看到使用了recvfrom和sendto两个函数,分别用来接、发数据

recvfrom函数原型

int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from,int *fromlen);

s为socketfd,即server当前使用的套接字

buf为接收缓冲区,即声明的数组

len为缓冲区大小,一般使用sizeof表示

flags参数一般不用,设置为0

from为client端地址,使用 (struct sockaddr)&client_addr*的形式

fromlen为指针,指向from缓冲区长度值。

sendto函数原型

int sendto(int s, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tolen);

与recvfrom类似,只是将buf(接收缓冲区)换成了msg(发送缓冲区);将fromlen指针直接换成了长度参数。

for more information

最后只需要关闭socket。(非常像fd!

  close(Udp_socket);

UDP_client

与server类似,我们也先来分析一下client需要干什么事情。

  • 创建socket(有socket才能通信),绑定server的ip和端口。
  • 准备发送数据。
  • 通过socket发送数据,并接收反馈数据。

创建、初始化socket

这部分client和server有很大的不同。

server中,我们首先将socket与自己的IP和端口绑定了;但在client中,我们并没有声明任何有关自己的地址变量。

目前可以从 UDP的特点进行解释:

UDP是不面向连接的传输协议,每一个socket由一个二元组(目的IP和目的Port)决定,而且在server中,通过recvfrom函数是可以获得client的IP和Port的。

所以此时我们只要确定目的IP和Port就可以唯一在server中确定一个socket来接受数据。

注意:这样的情况只有在CS架构下才可能实现:

如果在P2P架构下,每个人都会是server,如果不绑定自己的IP和Port,别就无法向你发送数据。

而CS架构下由于client一直为发出数据后再接收;server一直为 接收数据后再发送。所以server并不需要自己知道自己的IP和Port,只需要让server知道即可。

 int Udp_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);struct sockaddr_in server_addr;memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(1234);server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");

准备接受数据

创建缓冲区…balabala…

   int len=sizeof(server_addr);char send_buf[50];char recv_buf[50];int send_num,recv_num;cout<<"please input string!"<<endl;cin>>send_buf;

开始发/接数据

和上文中server类似,只不过是收发顺序颠倒一下,少了一个功能实现部分。

    send_num=sendto(Udp_socket,send_buf,strlen(send_buf),0,(struct sockaddr*)&server_addr,len);if(send_num<0){cout<<"send ERROR"<<endl;exit(1);}recv_num=recvfrom(Udp_socket,recv_buf,sizeof(recv_buf),0,(struct sockaddr*)&server_addr,(socklen_t*)&len);if(recv_num<0){cout<<"receive ERROR"<<endl;exit(1);}recv_buf[recv_num]='\0';cout<<"Server give back: "<<recv_buf<<endl;close(Udp_socket);return 0;
}

TCP实现CS

TCP协议与UDP协议的一大不同就是: TCP时面向连接传输层协议,而UDP是不面向连接的。

二者的类比就像是: TCP是打电话,UDP是写信。即一个会建立专线,另一个和大家共享资源。

UDP协议只是在IP上面很简单地封装了一层,而TCP要更加复杂。

不管怎么,在这篇博客中我们并不需要关心底层,我们只需要知道API就行了 :happy:

另外,请记住,TCP是面向连接的!

TCP_Client

相信看过UDP程序后你对socket编程已经很有心得了,本部分我们就简单地实现一个server在收到消息后简单地print一句话给client的naive程序。

你会注意到这次我先编写的Client程序,原因是TCP协议下用户和服务器连接需要用户先于服务器 “握手” 创建连接,这样的顺序应该会更符合逻辑

创建、初始化socket

注意将 socket()函数中的type和protocol参数更改为稳定数据传输和TCP

其他和UDP_client的内容基本相同,也是不需要绑定自己的IP和Port

//创建套接字int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//向服务器(特定的IP和端口)发起请求struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充serv_addr.sin_family = AF_INET;  //使用IPv4地址serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址serv_addr.sin_port = htons(1234);  //端口connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

创建连接&准备发/收数据

由于我们实现的功能没有发数据,所以就少了一些内容。

注意,connect函数就是client用来和server建立连接的过程

 connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//读取服务器传回的数据char buffer[40];read(sock, buffer, sizeof(buffer)-1);printf("Message form server: %s\n", buffer);//关闭套接字close(sock);return 0;

connect函数原型:

int connect(int sockfd, const struct sockaddr* server_addr, socklen_t addrlen)

sockfd即指定数据发送的套接字;

server_addr即指定数据发送地址;

addrlen即指定server_addr结构体的长度;

然后我们就可以通过read函数将取服务器发回的数据读入缓冲区中

TCP_Server

创建、初始化socket

注意将 socket()函数中的type和protocol参数更改为稳定数据传输和TCP

其他和UDP_server的内容基本相同。

int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//将套接字和IP、端口绑定struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充serv_addr.sin_family = AF_INET;  //使用IPv4地址serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址serv_addr.sin_port = htons(1234);  //端口bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

接受数据

由于我们实现的功能只有print,所以就不需要预先设定缓冲区

//进入监听状态,等待用户发起请求listen(serv_sock, 20);//接收客户端请求struct sockaddr_in clnt_addr;socklen_t clnt_addr_size = sizeof(clnt_addr);int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);//向客户端发送数据char str[] = "hello client!";write(clnt_sock, str, sizeof(str));

这里出现了listen函数,即监听函数,这是对client中connect函数的一个照应。

listen函数原型:

int listen ( int sockfd,  int backlog );

sockfd即被 listen函数作用的套接字。是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数。

backlog 即server同时建立连接的上限值。

然后同accept函数接受client的请求

accept函数作用:接收一个套接字中已建立的连接。

原型:

int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

emmm,相信大家都能看懂,不然就来这。

发送数据

 //向客户端发送数据char str[] = "hello client!";write(clnt_sock, str, sizeof(str));//关闭套接字close(clnt_sock);close(serv_sock);return 0;}

通过write向client写数据。

最后关闭socket即可。

Source code

UDP_server

#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <string>
using namespace std;int main()
{//创建Udp_socket,socket函数来自 socket.h, IPPOROTO_UDP来自in.hint Udp_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);struct sockaddr_in sever_addr;//     struct sockaddr_in{//     sa_family_t     sin_family;   //地址族(Address Family),也就是地址类型//     uint16_t        sin_port;     //16位的端口号//     struct in_addr  sin_addr;     //32位IP地址//     char            sin_zero[8];  //不使用,一般用0填充// };memset(&sever_addr,0,sizeof(sever_addr));sever_addr.sin_family=AF_INET;sever_addr.sin_port=htons(1234);sever_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//function from arpa/inet.hbind(Udp_socket,(struct sockaddr*)&sever_addr,sizeof(sever_addr));cout<<"Sever is ready to response!!!"<<endl;char recv_buf[50];//接受缓冲区char send_buf[50];//发送缓冲区int recv_num;int send_num;struct sockaddr_in client_addr;int len=sizeof(client_addr);while(true){cout<<"waiting for data"<<endl;recv_num=recvfrom(Udp_socket,recv_buf,sizeof(recv_buf)-1,0,(struct sockaddr*)&client_addr,(socklen_t *)&len);if(recv_num<0){cout<<"receive ERROR"<<endl;exit(1);}recv_buf[recv_num]='\0';cout<<"Receive "<<recv_num<<"bytes: "<<recv_buf<<endl;for(int i=0;i<recv_num;i++){if(recv_buf[i]>='a'&&recv_buf[i]<='z')send_buf[i]=toupper(recv_buf[i]);}send_buf[recv_num]='\0';send_num=sendto(Udp_socket,send_buf,recv_num,0,(struct sockaddr*)&client_addr,len);if(send_num<0){cout<<"send ERROR"<<endl;exit(1);}}close(Udp_socket);return 0;
}

UDP_Client

#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>using namespace std;int main()
{int Udp_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);struct sockaddr_in sever_addr;memset(&sever_addr,0,sizeof(sever_addr));sever_addr.sin_family=AF_INET;sever_addr.sin_port=htons(1234);sever_addr.sin_addr.s_addr=inet_addr("127.0.0.1");int len=sizeof(sever_addr);char send_buf[50];char recv_buf[50];int send_num,recv_num;cout<<"please input string!"<<endl;cin>>send_buf;send_num=sendto(Udp_socket,send_buf,strlen(send_buf),0,(struct sockaddr*)&sever_addr,len);if(send_num<0){cout<<"send ERROR"<<endl;exit(1);}recv_num=recvfrom(Udp_socket,recv_buf,sizeof(recv_buf),0,(struct sockaddr*)&sever_addr,(socklen_t*)&len);if(recv_num<0){cout<<"receive ERROR"<<endl;exit(1);}recv_buf[recv_num]='\0';cout<<"Sever give back: "<<recv_buf<<endl;close(Udp_socket);return 0;
}

TCP_Client

    #include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/socket.h>int main(){//创建套接字int sock = socket(AF_INET, SOCK_STREAM, 0);//向服务器(特定的IP和端口)发起请求struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充serv_addr.sin_family = AF_INET;  //使用IPv4地址serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址serv_addr.sin_port = htons(1234);  //端口connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//读取服务器传回的数据char buffer[40];read(sock, buffer, sizeof(buffer)-1);printf("Message form server: %s\n", buffer);//关闭套接字close(sock);return 0;}

TCP_server

    #include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/socket.h>#include <netinet/in.h>int main(){//创建套接字int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//将套接字和IP、端口绑定struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充serv_addr.sin_family = AF_INET;  //使用IPv4地址serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址serv_addr.sin_port = htons(1234);  //端口bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//进入监听状态,等待用户发起请求listen(serv_sock, 20);//接收客户端请求struct sockaddr_in clnt_addr;socklen_t clnt_addr_size = sizeof(clnt_addr);int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);//向客户端发送数据char str[] = "hello client!";write(clnt_sock, str, sizeof(str));//关闭套接字close(clnt_sock);close(serv_sock);return 0;}

C++实现CS模型(计算机网络)相关推荐

  1. linux的基础知识——CS模型流程图

    1.典型的CS模型流程图 2.服务端需要做的事情 (1)socket():建立服务端套接字: (2)bind():绑定IP和端口号,初始化struct sockaddr_in addr: (3)lis ...

  2. Java网络编程案例--CS模型的简单实现

    Java网络编程案例–CS模型的简单实现 Java网络编程案例CS模型的简单实现 基本概述 程序原理图 源代码 基本概述 该程序采用C/S模型,在服务器端简单的建立了一个多线程类,来实现对多个客户端传 ...

  3. bs模型 cs模型

    以前一直知道有C/S和B/S模型,但不知道是什么东西.今天看了其他博客的算了比较理解了.记录下吧,记录的东西常常回来翻看,就可以长久记忆了. C/S结构,即Client/Server(客户机/服务器) ...

  4. 超详细:实现过程-Linux 环境下的简易聊天室,采用CS模型,实现多客户端之间的稳定数据传输。--注册和登录(但之后会连续更新内容,直至全部实现)

    前言 在学完不够全面的Linux操作系统编程后(这也意味着我后期也要不断学习,这也符合我活到老学到老的人生观点),需要以一些项目来检测自己的所学,毕竟实践见真章. 所以在今后的几天里,我将以无界面聊天 ...

  5. 计算机网络7层协议模型,计算机网络(一) OSI七层模型及TCP/IP dubbo协议

    3.TCP/IP 四层模型: TCP/IP协议:是一个网络通信模型,是OSI 七层模型的简化,为4层模型,泛指众多(TCP,UDP,IP等)协议: OSI TCP/IP 功能 协议 应用层 应用层 文 ...

  6. Java Socket笔记-利用tcp socket搭建CS模型

    目录 基本概念 代码与实例 基本概念 这里采用的是TCP模型,只说明一点,在Java中有 import java.net.Socket; 这个包中TCP的用法个人觉得和QTcpSocket差不多(个人 ...

  7. 网络编程2——CS模型的TCP通信流程总结 及 server、client的实现

    一.socket模型创建流程图 二.server的实现 1,报错文件先写好,基本头文件写好,C的.网络的 2,创建socket,定义文件描述符lfd,记得要返回值检查 3,创建bind函数,其中第二个 ...

  8. 利用套接字实现 CS 模型

    例子:大写转小写. 注意:代码都是运行在Linux内核中. 服务器端: #include <stdio.h> #include <unistd.h> #include < ...

  9. CS 144 计算机网络

    UNIX 自带socket int socket(domain, type, 0) domain 值可取AF_INET. AF_INET6 . AF_LOCAL . AF_INET决定了要用 ipv4 ...

  10. 2021.10.02超详细实现过程-Linux 环境下的简易聊天室,采用CS模型,实现多客户端之间的稳定数据传输。(添加好友,删除好友、屏蔽好友、查看好友列表(针对数据库的操作))

    这次主要对加好友.删除好友.屏蔽好友.查看好友列表功能(单纯的基于数据库操作),并且也对之前的代码做了部分的改动,为中间涉及全局变量问题. 对之前的改动(主要): 其中对结构体进行了改动和结构体全局变 ...

最新文章

  1. Javascript自定义事件功能与用法实例分析
  2. 自动生成Insert数据的SQL脚本
  3. minecraft服务器_如何使用Minecraft领域设置简单的无压力Minecraft服务器
  4. 如何形象的解释javascript中map,foreach,reduce的区别
  5. 消息中间件学习总结(4)——RocketMQ之RocketMQ 迈入50万TPS消息俱乐部
  6. [转]一个程序员的哲学思考(关于编程、关于人生)
  7. Android Studio如何去除界面默认标题栏
  8. 一文带你看透手机号码归属地
  9. [转转转]面试常见逻辑推理题目及答案整理
  10. vue 脚手架启动html,vue脚手架项目创建步骤详解
  11. 剑指offer第9题及扩展 斐波那契数列
  12. 白杨SEO:QQ群SEO是什么?QQ群排名如何做引流与营销?【举例】
  13. Robin:SEO从业者打造个人品牌的八个建议,白杨SEO个人品牌实战举例
  14. 完全数据驱动的对话模型和社交机器人
  15. 微信小程序星星评分代码片段(含半星)
  16. 2023软件测试最难求职季,哪些测试技能更容易拿到offer?
  17. chatgpt智能提效职场办公-ppt怎么蒙层
  18. Comparison method violates its general contract 出现原因和解决方法
  19. 建信金科是外包吗_为什么网上黑建信金科的这么多?
  20. hdu6194 string string string

热门文章

  1. 渗透测试 ( 10 ) --- 扫描 web目录、文件 (dirb、wfuzz、wpscan、nikto)
  2. MySQL专题系统归纳快速上手(常用cmd命令,常用函数汇总,SQL语句精讲带示例)适用初学、用法速查
  3. 终于懂得孤独是躲不开的单行道
  4. Easy Connect 当前IE代理启用了自动配置脚本,不允许使用CS客户端登录
  5. c语言else需要条件,C语言else条件判断
  6. 什么是无线信号的灵敏度和保真度
  7. 用网盘和git来管理资源
  8. 18年NDVI斜率图生成
  9. R语言绘制带误差和可信区间的折线图
  10. 抖音作品实时监控采集数据,抖音达人下关键词数据抓取