当前位置: 首页 > news >正文

【Linux 网络编程1】使用UDP/TCP编写套接字,多进程/多线程版本的TCP编写的套接字,将套接字封装

目录

1.学习网络编程前的一些基础知识

2.UDP(user datagram protocol)协议的特点

3.使用有UPD编写套接字

4.使用TCP编写套接字

4.2.TCP客服端 

4.3.TCP服务器端

4.4.单进程版本(没有人会使用)

4.5.多进程版本

4.6.多线程版本

5.把套接字封装


1.学习网络编程前的一些基础知识

1.1.IP地址

  • IP地址是在IP协议中, 用来标识网络中不同主机的地址(一个IP地址标识一台主机);
  • 通常使用 "点分十进制" 的字符串表示IP地址, 例如127.0.0.1 ; 用点分割的每一个数字表示一个字节, 范围是 0 - 255,就是一个字节的大小,IP地址刚好是4字节, 32位的整数;

1.2.端口号(port) 

  • 端口号是一个2字节16位的整数
  • 端口号用来标识一个进程,那么IP地址+端口号就可以标识某一台主机的某一个进程

1.3.TCP/IP四层模型 

 

1.4.网络字节序 

  • 网络字节序就是大端字节序(低位放在高地址上),使用相同的字节序便于网络间通信;

有系统提供的接口 

2.UDP(user datagram protocol)协议的特点

UDP协议是一个传输层协议

  • 无连接:没有连接,客户端发给服务器端,服务器端要先保存客户端的信息,服务器端再使用这个信息发给对应的客户端(简单地说就是需要指明发送给谁
  • 不可靠传输:只是传递数据,成功与否都不会反馈
  • 面向数据报:不能向面向字节流的TCP一样使用read和write来读写

3.使用有UPD编写套接字

3.1.服务器端

3.1.1.创建套接字

    int sock=socket(AF_INET,SOCK_DGRAM,0);//创建套接字if(sock<0){std::cerr<<"socket fail: "<<errno<<std::endl;return 1;} 

3.1.2.bind服务器 

网络字节序就是大端字节序(低位放在高地址上),使用相同的字节序便于网络间通信;

  1. 1.需要将人识别的点分十进制,字符串风格IP地址,转化为4字节整数IP.2.考虑大小端的问题

服务器不用bind一个固定的IP的原因

  1. 1.云服务器,不允许bind公网IP,另外,在一般编写也不会指明IP.2.一般主机有多个IP,如果只是bind一个IP,发给其他IP的数据就不会交给主机处理
  2. INADDR_ANY只要是发给这个主机的数据都会被处理

    struct sockaddr_in local;local.sin_family=AF_INET;local.sin_port=htons(port);//1.需要将人识别的点分十进制,字符串风格IP地址,转化为4字节整数IP.2.考虑大小端的问题//1.云服务器,不允许bind公网IP,另外,在一般编写也不会指明IP.2.一般主机有多个IP,如果只是bind一个IP,发给其他IP的数据就不会交给主机处理;//INADDR_ANY只要是发给这个主机的数据都会被处理local.sin_addr.s_addr=INADDR_ANY;if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)//bind主机{std::cerr << "bind error : " << errno << std::endl;return 2;}

3.1.3.传递接受数据

  • UDP是无连接的,需指明发给谁,也需要保存发送端的信息

    //业务逻辑char message[1024];bool quit=false;while(!quit){//保存发送端信息struct sockaddr_in peer; socklen_t len=sizeof(peer);//接受数据ssize_t s=recvfrom(sock,message,sizeof(message)-1,0,(struct sockaddr*)&peer,&len);if(s>0){message[s]=0;std::cout<<"client# "<<message<<std::endl;}else{std::cerr<<"recvfrom"<<errno<<std::endl;return 2;}//给对端发送一个你好吗std::string tm="你好吗?";std::cout<<"server to client: "<<tm<<std::endl;sendto(sock,tm.c_str(),tm.size(),0,(struct sockaddr*)&peer,len);}

 3.2.客户端

  • 客户端不需要显示bind,当传输第一个数据是会自动随机bind一个port(没有被使用的port)
#include<iostream>
#include<cerrno>
#include<string>
#include<cstdlib>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>//client serverIP serverPort
int main(int argc,char* argv[])
{if(argc!=3){std::cout<<"请按格式输入: client serverIP serverPort"<<std::endl;return 2;}int sock=socket(AF_INET,SOCK_DGRAM,0);//创建套接字if(sock<0){std::cout<<"socket create errno: "<<errno<<std::endl;return 1;}//客户端不用显示bind,OS会自动bind;//服务器端会有规划,让port是没有被占用的,让别人来访问这个port;//client正常发送数据的时候,OS会自动给你bind,采用随机端口的方式struct sockaddr_in server;server.sin_family=AF_INET;server.sin_port=htons(atoi(argv[2]));server.sin_addr.s_addr=inet_addr(argv[1]);while(1){std::string message;std::cout<<"请输入#";std::cin>>message;sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);char buffer[1024];ssize_t s=recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&tmp,&tlen);if(s>0){std::cout<<"server say#: "<<buffer<<std::endl;buffer[s]=0;   }else{std::cerr<<"recvfrom"<<errno<<std::endl;return 2;}}return 0;
}

4.使用TCP编写套接字

4.1.TCP的特点

TCP是一个传输层协议,和UDP的特点相反

  • 有连接
  • 可靠传输
  • 面向字节流

4.2.TCP客服端 

先写的客户端,因为服务器端会写多个版本

#include<iostream>
#include<stdio.h>
#include<cerrno>
#include<unistd.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>void Usage()
{std::cout<<"usage:./client server_IP server_port"<<std::endl;
}
int main(int argc,char* argv[])
{if(argc!=3){Usage();return 1;}//建立套接字int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"socket"<<errno<<std::endl;return 2;}//自动bind//连接服务器struct sockaddr_in local;local.sin_addr.s_addr=inet_addr(argv[1]);local.sin_port=htons(atoi(argv[2]));local.sin_family=AF_INET;connect(sock,(struct sockaddr*)&local,sizeof(local));//业务逻辑while(1){char buffer[1024]={0};std::cout<<"请输入";fgets(buffer,sizeof(buffer),stdin);write(sock,buffer,sizeof(buffer));char mes[1024]={0};read(sock,mes,sizeof(mes));std::cout<<mes<<std::endl;}return 0;
}

4.3.TCP服务器端

4.3.1.创建套接字和bind服务器

  • socket的第二个参数是SOCK_STREAM,UPD是SOCK_DGRAM(datagram),TCP是SOCK_STREAM(stream,就是字节流)
//建立套接字int listen_sock=socket(AF_INET,SOCK_STREAM,0);if(listen_sock<0){std::cerr<<"socket"<<errno<<std::endl;return 1;}//bind服务器struct sockaddr_in local;local.sin_family=AF_INET;local.sin_port=htons(PORT);local.sin_addr.s_addr=INADDR_ANY;//INADDR_ANY会bind执行代码的主机if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"bind"<<errno<<std::endl;return 2;}

4.3.2.listen设为聆听状态

    //listen状态listen(listen_sock,5);

 4.3.3.accept接受客户端的连接,并返回一个文件描述符

  • 因为TCP套接字是有连接的,连接成功后也是使用返回的套接字和对端进行网络通信

struct sockaddr_in tmp;
socklen_t tlen=sizeof(tmp);
//建立连接
int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);

4.4.单进程版本(没有人会使用)

  • 一次只能让一个客户端访问
  • 写端关闭读端读到文件结尾,再读返回0
int main()
{//...创建套接字、bind、listen都省略了,每次都一样冗余while(1){struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);//建立连接int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);if(fd<0){std::cerr<<"accept "<<errno<<std::endl;return 3;}std::cout<<"get a new link "<<std::endl;//1.单进程versionwhile(1){char buffer[1024]={0};ssize_t s=read(fd,buffer,sizeof(buffer));if(s>0){buffer[s]=0;std::cout<<"client to server:"<<buffer<<std::endl;std::string message;message+="server to client:你好!";//给连接的客户端发一个你好write(fd,message.c_str(),message.length());}else if(s==0){//写端关闭读端读到文件结尾,再读返回0std::cout<<"client quit!"<<std::endl;break;}else{std::cerr<<"read "<<errno<<std::endl;break;}}}
}

4.5.多进程版本

4.5.1.父进程是一个循环,他要一直接收新的客服端不能等待子进程,解决方法

  1. 父进程等待子进程,子进程创建后再创建孙子进程执行后序代码,子进程秒退等待时间可以忽略不计;下面代码

pid_t pid = fork();if(pid<0){continue;}else if(pid==0){//子进程close(listen_sock);if(fork()>0)exit(0);//子进程创建就直接退出,创建的孙子进程执行后序代码,孙子进程变成孤儿进程被1号进程领养serviceIO(fd);close(fd);exit(0);}else{//父进程close(fd);//子进程的PCB以父进程的PCB做模板初始化,不是共享的//waitpid(&pid,NULL,0);//父进程等待子进程,子进程创建后再创建孙子进程执行后序代码,子进程秒退等待时间可以忽略不计}

        2.signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信号,子进程将自动释放; 

2.关闭不用的文件描述符,父进程关闭accept返回的文件描述符,子进程关闭socket返回的文件描述符

void serviceIO(const int& fd)
{//1.单进程version,做成一个函数while(1){char buffer[1024]={0};ssize_t s=read(fd,buffer,sizeof(buffer));if(s>0){buffer[s]=0;std::cout<<"client to server:"<<buffer<<std::endl;std::string message;message+="server to client:你好!";//给连接的客户端发一个你好write(fd,message.c_str(),message.length());}else if(s==0){//写端关闭读端读到文件结尾,再读返回0std::cout<<"client quit!"<<std::endl;break;}else{std::cerr<<"read "<<errno<<std::endl;break;}}
}int main()
{//...创建套接字、bind、listen都省略了,每次都一样冗余signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信号,子进程将自动释放while(1){struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);//建立连接int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);std::cout<<"get a new link "<<std::endl;if(fd<0){std::cerr<<"accept "<<errno<<std::endl;return 3;}//2.多进程versionpid_t pid = fork();if(pid<0){continue;}else if(pid==0){//子进程close(listen_sock);serviceIO(fd);close(fd);exit(0);}else{//父进程close(fd);//子进程的PCB以父进程的PCB做模板初始化,不是共享的}}
}

4.6.多线程版本

  • 线程是共享PCB的,所以在线程内使用完毕关闭文件描述符即可;
  • 不等待线程可以使用线程分离的方法
void serviceIO(const int& fd)
{//1.单进程versionwhile(1){char buffer[1024]={0};ssize_t s=read(fd,buffer,sizeof(buffer));if(s>0){buffer[s]=0;std::cout<<"client to server:"<<buffer<<std::endl;std::string message;message+="server to client:你好!";//给连接的客户端发一个你好write(fd,message.c_str(),message.length());}else if(s==0){//写端关闭读端读到文件结尾,再读返回0std::cout<<"client quit!"<<std::endl;break;}else{std::cerr<<"read "<<errno<<std::endl;break;}}
}
void* HandlerRequest(void* agrs)
{pthread_detach(pthread_self());int fd=*((int*)agrs);serviceIO(fd);close(fd);//记得关闭文件描述符
}
int main()
{while(1){struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);//建立连接int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);std::cout<<"get a new link "<<std::endl;if(fd<0){std::cerr<<"accept "<<errno<<std::endl;return 3;}pthread_t tid;pthread_create(&tid,nullptr,HandlerRequest,(void*)&fd);}return 0;
}

5.把套接字封装

  • 使用静态是因为不用创建对象就可以使用sock::Socket()等函数;
#pragma once
#include<iostream>
#include<cstdlib>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>namespace ns_socket{class sock{public:static int Socket(){int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"socket"<<std::endl;exit(1);}return sock;}static void Bind(int sock,char* port){struct sockaddr_in local;local.sin_family=AF_INET;local.sin_addr.s_addr=INADDR_ANY;local.sin_port=htons(atoi(port));if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"bind"<<std::endl;exit(2);}}static void Listen(int sock){if(listen(sock,5)<0){std::cerr<<"listen"<<std::endl;exit(3);}}static int Accept(int sock) {struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);int new_sock=accept(sock,(struct sockaddr*)&tmp,&tlen);if(new_sock<0){std::cerr<<"accept"<<std::endl;exit(4);}return new_sock;  }static void Connect(int sock,char* server_ip,char* server_port){struct sockaddr_in local;local.sin_family=AF_INET;local.sin_addr.s_addr=inet_addr(server_ip);local.sin_port=htons(atoi(server_port));if(connect(sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"connect"<<std::endl;exit(5);}else{std::cout<<"connet success"<<std::endl;}}};
}

http://www.lryc.cn/news/33441.html

相关文章:

  • 滑动窗口求最大和最小
  • 跟ChatGPT学量化,菜鸟炒股也不怕
  • 扬帆优配|2600亿新能源巨头狂飙!外资唱多中国:再涨15%
  • ChatGPT技术与商业模式及产业发展布局方案
  • CIMCAI port ai shipping ai artificial intelligence smart port
  • 《数据解构》HashMap源码解读
  • Databend 开源周报 第 83 期
  • Spring | 基础
  • windows7安装sql server 2000安装步骤 及安装过程中遇到的问题和解决方式
  • Python 开发-批量 FofaSRC 提取POC 验证
  • Linux系统中部署软件
  • PHP常用框架介绍与比较
  • Umi + React + Ant Design Pro 项目实践(一)—— 项目搭建
  • MySQL知识点总结(1)
  • day45第九章动态规划(二刷)
  • 第十四届蓝桥杯第三期模拟赛原题与详解
  • client打包升级
  • Blazor_WASM之3:项目结构
  • OperWrt 包管理系统02
  • 人人都学会APP开发 提高就业竞争力 简单实用APP应用 安卓浏览器APP 企业内部通用APP制作 制造业通用APP
  • 【自然语言处理】从词袋模型到Transformer家族的变迁之路
  • LIME: Low-light Image Enhancement viaIllumination Map Estimation
  • 源码指标编写1000问4
  • Golang中GC和三色屏障机制【Golang面试必考】
  • MOS FET继电器(无机械触点继电器)设计输入侧电源时的电流值概念
  • 5. 驱动开发
  • 模板方法模式详解
  • 关于 Lightweight process container,ChatGPT的答案
  • 机器学习和深度学习的综述
  • Synopsys Sentaurus TCAD系列教程之--Sprocess(SmallMOS_2D3D) 解析