【Linux 网络编程1】使用UDP/TCP编写套接字,多进程/多线程版本的TCP编写的套接字,将套接字封装
创始人
2024-05-30 04:41:47
0

目录

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: "<

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# "<

 3.2.客户端

  • 客户端不需要显示bind,当传输第一个数据是会自动随机bind一个port(没有被使用的port)
#include
#include
#include
#include
#include
#include
#include
#include//client serverIP serverPort
int main(int argc,char* argv[])
{if(argc!=3){std::cout<<"请按格式输入: client serverIP serverPort"<>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#: "<

4.使用TCP编写套接字

4.1.TCP的特点

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

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

4.2.TCP客服端 

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

#include
#include
#include
#include
#include
#include
#include
#include
#includevoid Usage()
{std::cout<<"usage:./client server_IP server_port"<

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"<

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 "<0){buffer[s]=0;std::cout<<"client to server:"<

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:"<

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:"<

5.把套接字封装

  • 使用静态是因为不用创建对象就可以使用sock::Socket()等函数;
#pragma once
#include
#include
#include
#include
#include
#includenamespace ns_socket{class sock{public:static int Socket(){int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"socket"<

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...