Linux的核心思想之一 "一切皆文件"

内容 , socket在Linux内核的实现的代码及TCP和UDP的实现

              网络编程常用头文件:  https://blog.csdn.net/jx232515/article/details/51912700

1.  例如本地文件对一个字符串的"aaaaaaa"的读写是 open()返回的句柄 fd 作为载体, 调用 write 和 read 进行读写

  而网络文件的和本地文件差不多但多了一些特性  用socket()返回的句柄 socket 传输读写数据前 要再创建一个sockaddr_in 对象 设置好协议族family 地址ip 端口prot 后 把设置好的sockaddr_in 对象利用 bind() 和 socket 绑定 ; 再用 listen()监听socket 有哪些连接和设置最大监听数量

    完成上述一系列后可以调用 write 和 read 进行读写 , 也有一些特殊的: accept()  connect()  (setsocketopt()设置sendbuff和recvbuff的size)


2.内核的socket就是这样一个简单的结构 , 里面每种结构都是函数指针或变量的集合 ,

      方便实现每种协议的接口函数名和最终目的相同,但是实现过程却不同的面向对象思想

在Linux内核里 ops是一些函数的集合如accept() connect()等 , file里是一些write() read() 等 , sk是 bind() listen() 等;

      每种协议栈的接口函数名是相同的但是内部实现是不同的


3.在创建cocket对象时要告诉内核你要使用哪种协议 , socket()第一个参数添加ipv4/ipv6协议 第三个参数添加TCP/UDP协议也可以为0(默认具体依赖第二个参数) , 第二个参数,如果是流套接字的SOCK_ STREAM默认是TCP协议的 , 如果是数据包套接字SOCK_DGRAM默认是UDP ,还有个原始套接字SOCK_RAW是使用其他协议,具体要给第三个参数

套接字分为三类;

流式socket(SOCK_STREAM):提供可靠,面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性。

数据报socket(SOCK_DGRAM):数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,无序的,并且不保证可靠,无差错的。它使用的数据报协议是UDP。

原始socket:原始套接字允许对底层协议如TP或ICMP进行直接访问,它功能强大但使用复杂,主要用于一些协议的开发。


4.TCP/IP协议

  4.1分层概念 : mac  ip  tcp

    物理层 MAC(源MAC 目标MAC 长度和类型L/T )------>ARP 解决链路和物理连接问题

    网络层 IP  解决远程通信问题

    传输层 TCP/UDP ---->端口(对应了本机的某个进程) 决定了数据传给哪个进程

    应用层 HTTP HTTPS FTP TELNET SSH

  4.2 ip格式 点分十进制 二进制

      网络字节序(大端)0x12 0x34 0x56 x078 本地字节序(小端) 0x78 0x56 0x34 0x12

      case 1 : 点分十进制 转换 网络字节序的二进制  有个专用函数  in_addr_t inet_addr(const char* cp);

      case2 : 网络字节序的二进制 转换 点分十进制          char* inet_ntoa(struct in_addr_t in);

  4.3子网掩码 用于确定网段的范围  24位能用0~255个ip地址  28位能用14个IP地址和一个255广播地址

      24 == 32位二进制中有 24位是1 剩下的8位是0 (一个字节2位)

      255.255.255.0  == 11111111.11111111.11111111.00000000

  4.4 拆包和封包过程


5.TCP编程原理和流程

socket()的返回值: 成功 >0 (返回文件描述符的序号) , 失败 -1 ( 创建失败 INVALID_SOCKET也是-1 )

bind()的返回值: 成功 0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

listen()    : 成功0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

accept()    : 成功>0(返回文件描述符的序号一般从3开始) , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

connect()  : 成功 0  , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

recv/recvfrom() : 成功 >0 (返回字符串的len)  , =0 (断开连接) ,失败 <0 (可用WSAGETLASTERROR 函数取错误码)

send/sendto() :成功 >0 (返回字符串的len)  , =0 (断开连接) ,失败 <0 (可用WSAGETLASTERROR 函数取错误码)

recv和recvfrom的区别在于recvfrom可以获取或设置对端的信息(明确发送或接收的目标), send和sendto同理

sendto可以在参数中指定发送的目标地址 , 适用于发送未建立连接的UDP数据包 , send需要socket已建立连接,sendto可用于无连接的socket

对于有连接的socket,两者一样,sendto最后两个参数没用

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

int send( SOCKET s, const char FAR *buf, int len, int flags );

阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回;  connect 和  accept 都是阻塞函数

  5.1 TCP服务端

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include <arpa/inet.h>
#include<pthread.h> /*
面向对象三步:创建对象 设置对象 分配对象
*/
int main( int argc, char* argv[] )
{
//1. creat socket
int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sock < )
{
printf("create socket fail");
return ;
}
//2.set socket
//设置本地协议族和地址端口 设置时用sockaddr_in 绑定 或使用时强转sockaddr*
struct sockaddr_in local;
local.sin_family = AF_INET; //协议族
//htons: host to network 把本地端口转换成网络的 ntohs 相反
local.sin_port = htons(); //端口
//inet_addr点分十进制转换网络二进制
local.sin_addr.s_addr = inet_addr("127.0.0.1"); //源地址 inet_addr()点分十进制(255.255.255.0))转换成网络字节序的二进制(1111.1111.1111.0) //3.分配对象
if(bind(sock,(struct sockaddr *)&local,sizeof(local)) < ) //将源信息local绑定到套接字socket上
{
perror("bind error\n");
close(sock);
return ;
}
printf("bind success\n");
//4.连接
if(listen(sock,) < ) //接受连接和设置最大连接数
{
perror("listen error\n");
close(sock);
return ;
}
printf("Listen success\n"); struct sockaddr_in peer; //保存对端信息
socklen_t len = sizeof(peer);
//5.监听
int fd = accept(sock,(struct sockaddr *)&peer ,&len); //阻塞函数直到有客户端连接也可以选择不保存第二第三可以为NULL
if(fd < )
{
perror("accept error\n");
close(sock);
return ;
}
printf("accept success\n"); char recvbuff[];
//6.开始通信
while()
{
memset(recvbuff,,sizeof(recvbuff));
//recv(fd, recvbuff, sizeof(recvbuff),0);
read(fd, recvbuff, sizeof(recvbuff)); //读取accept返回的对端数据
if(strcmp(recvbuff,"exit\n")==)
break;
fputs(recvbuff, stdout);
///send(fd, recvbuff, sizeof(recvbuff),0);
write(fd, recvbuff, sizeof(recvbuff)); //往对端写入数据
} close(fd);
close(sock);
return ;
}

  5.2客户端

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include <arpa/inet.h>
#include<pthread.h> int main( int argc, char* argv[] )
{
//1. creat socket
int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sock < )
{
perror("socket error\n");
return ;
}
printf("socket success!\n");
//2.set socket
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons();
local.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(sock , (struct sockaddr* )&local , sizeof(local)) < ) //连接服务器
{
perror("connect error\n");
close(sock);
return ;
}
printf("connect success!\n"); char recvbuff[];
char sendbuff[];
while(fgets(sendbuff,sizeof(sendbuff),stdin))
{
write(sock,sendbuff,strlen(sendbuff));
if( == strcmp(sendbuff,"exit\n"))
break;
read(sock,recvbuff,sizeof(recvbuff));
fputs(recvbuff,stdout); memset(sendbuff, ,sizeof(sendbuff));
memset(recvbuff, ,sizeof(recvbuff));
}
close(sock);
return ;
}

6.UDP协议

  客户端不需要绑定是因为在使用sendto()

  sendto() 和 recvfrom() 返回值 为整型,如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。

  recvfrom() 用serversock接收客户端的数据包和ip信息并另存为buff 和 最后两个参数

    s:套接字,

    buff:接收数据缓冲区 ,

    bufflen:缓冲区长度 ,

    flags:调用操作方式(一般是0) ,

    from:指针(保存发送端的信息),

    fromlen:保存的长度 (socklen_t类型的变量的首地址) &len

  sendto()

    s:套接字,

    buff:发送数据缓冲区 ,

    bufflen:缓冲区长度 ,

    flags:调用操作方式(一般是0) ,

    from:指针(指向发送端信息),

    fromlen:发送的长度 (sizeof() 或传入 socklen_t类型的变量) len

  数据类型socklen_t和int具有相同的长度.否则就会破坏 BSD套接字层的填充  转换使用

6.1 服务端

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h> int main( int argc, char* argv[] )
{ int serverSock = socket(AF_INET,SOCK_DGRAM,); struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons();
local.sin_addr.s_addr = inet_addr("127.0.0.1"); printf("socket success\n");
int bindr = bind(serverSock,(struct sockaddr*)&local,sizeof(local));
if( bindr < )
{
perror("bind error");
close(serverSock);
return ;
}
printf("bind success\n"); struct sockaddr_in clientSock;
socklen_t len = sizeof(clientSock);
char buff[];
while()
{
memset(buff,,sizeof(buff));
      //recv()和accept()的结合体
recvfrom(serverSock,buff,sizeof(buff),,(struct sockaddr*)&clientSock,&len);
fputs(buff,stdout);
      //send()和connect()的结合体
sendto(serverSock,buff,sizeof(buff),,(struct sockaddr*)&clientSock,len);
}
   close(serverSock);
   return ;
}

6.2客户端

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h> int main( int argc, char* argv[] )
{ //1.create socket
int clientsock = socket(AF_INET , SOCK_DGRAM , );
//2.set socket
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons();
local.sin_addr.s_addr = inet_addr("127.0.0.1");
if(clientsock < )
{
perror("socket perror");
     close(clientsock);
return -;
}
printf("socket Success\n");
socklen_t len;
char buf[] ;
while()
{
printf("aaa:");
fgets(buf,sizeof(buf),stdin);
    //往8888端口发送buf
if(sendto(clientsock,buf,sizeof(buf),,(struct sockaddr*)&local,sizeof(local)) < )
{
perror("sendto error");
       close(clientsock);
return ;
}
socklen_t len = sizeof(local);
recvfrom(clientsock,buf,sizeof(buf),,(struct sockaddr*)&local,&len);
fputs(buf,stdout);
memset(buf,,sizeof(buf));
}
close(clientsock);
return ;
}

7.进阶

  • udp广播数据包(广播只能是udp实现)

    路由器不转发广播数据包, 交换机会转发广播数据包 . 广播只能在一个广播域(局域网)中传播 , 而不能跨网段传播

    能够在组播组里进行传播 , 且路由器可以进行组播数据包转发, 能跨局域网传播(必须是同一个组播组)

    广播组播的区别: 都是用setsockopt开启,但是组播有ip 广播没有

  • 如何识别广播包

    MAC: 广播包的目标mac 是全ff  FF:FF:FF:FF:FF:FF

    IP:  当前网段的最后一个地址 x.x.x.255


8.socket选项设置与读取函数

  int setsockopt()  是否开启广播等功能

           详解 https://blog.csdn.net/a493203176/article/details/70053137

      SOCKET sock, 指向一个打开的套接口描述字

      int level,  指定选项的类型。
           SOL_SOCKET: 基本套接口
           IPPROTO_IP: IPv4套接口
           IPPROTO_IPV6: IPv6套接口
           IPPROTO_TCP: TCP套接口

      int optname, 选项的名称

      char* optval, 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ } 默认为0(false)不开启广播 , 1(ture)为开启

      int optlen optval 的大小      

  int getsockopt()  用法 https://www.cnblogs.com/wangshuo/archive/2011/04/20/2022279.html

      sockfd:一个标识套接口的描述字。
      level:选项定义的层次。支持的层次有SOL_SOCKET、IPPROTO_TCP。
      optname:需获取的套接口选项。
      optval:指针,指向存放所获得选项值的缓冲区。
      optlen:指针,指向optval缓冲区的长度值。
 
 

总结: TCP:server: socket()-----绑定bind()----监听listen()----接受连接并保存对端accept()-----读取数据recv()-----发送数据send()
     client:  socket()------------------------------------发起连接connect()------------------发送数据send()----接收数据recv()
    UDP:server: socket()----绑定bind()-----accept和recv的结合体recvfrom()-----connect和send的结合体sendto()
      client: socket()---------------------connect和send的结合体sendto()-----accept和recv的结合体recvfrom()
setsockopt()设置socket_fd的选项开启或关闭一些功能或更改缓冲区, 比如开启UDP的广播功能等

Linux基础(05)socket编程的更多相关文章

  1. Python 基础之socket编程(二)

    Python 基础之socket编程(二) 昨天只是对socket编程做了简单的介绍,只是把socket通信的框架搭建起来,要对其中的功能进行进一步的扩充,就来看看今天的料哈! 一.基于tcp的套接字 ...

  2. python基础之socket编程 (转自林海峰老师)

    python基础之socket编程   阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 ...

  3. Linux学习之socket编程(二)

    Linux学习之socket编程(二) 1.C/S模型——UDP UDP处理模型 由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实 ...

  4. Python 基础之socket编程(三)

    python 基础之socket编程(三) 前面实现的基于socket通信只能实现什么呢?在tcp协议的通信中就是一个用户说一句,服务端给你回一句,你再给服务端说一句,服务端再给你回一句,就这样一直友 ...

  5. Python 基础之socket编程(一)

    Python 基础之socket编程(一) 可以进行通信玩儿了,感觉不错不错,网络通信就像打电话,我说一句你听一句之后,你再说一句,我听一句,就这样.....下去了.不扯淡了,来来来,看看今天都搞了点 ...

  6. Linux学习之socket编程(一)

    socket编程 socket的概念: 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为socket. 在TCP协议中,建立连接的两个进 ...

  7. 十三python基础之socket编程

      阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现 ...

  8. python基础之socket编程

    一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现象 十 什么是 ...

  9. Linux下Golang Socket编程原理分析与代码实现

    在POSIX标准推出后,socket在各大主流OS平台上都得到了很好的支持.而Golang是自带Runtime的跨平台编程语言,Go中提供给开发者的Socket API是建立在操作系统原生Socket ...

随机推荐

  1. 使用s3-sftp-proxy 暴露minio s3 数据为sftp 访问

    尽管s3 很不错,但是ftp 也有自己存在的价值,以下是一个简单的通过s3-sftp-proxy 暴露minio s3 数据为ftp 的访问方式 环境准备 docker-compose 文件 vers ...

  2. REdis一致性方案探讨

    REdis功能强大众所周知,能够大幅简化开发和提供大并发高性能,但截止到REdis-5.0.5仍然存在如下几大问题: 一致性问题 这是由于REdis的主从复制采用的是异步复制,异常时可能发生主节点的数 ...

  3. uni-app 项目记录

    await等候,等待:期待 什么是async.awaitawait 用于等待异步完成通常async.await都是跟随Promise一起使用的 async返回的都是一个Promise对象同时async ...

  4. nginx之升级openssl及自定义nginx版本

    favicon.ico浏览器图标配置 favicon.ico 文件是浏览器收藏网址时显示的图标,当客户端使用浏览器问页面时,浏览器会自己主动发起请求获取页面的favicon.ico文件,但是当浏览器请 ...

  5. Fluent设置充分发展湍流入口(利用profile)

    计算模型: 物性参数为: 密度:100kg/m3 粘度系数:0.003333kg/(m·s) 原视频下载地址: https://pan.baidu.com/s/1W3n_K-dZCVMF7M63wV2 ...

  6. pandas连接多个表格concat()函数

    网易云课堂该课程链接地址 https://study.163.com/course/courseMain.htm?share=2&shareId=400000000398149&cou ...

  7. layer.confirm 防止post重复提交

    layer.confirm("确定对该资源单的价格"+isa+value+"元吗!", function(index, layero){ $(layero).f ...

  8. golang程序编译时提示“package runtime: unrecognized import path "runtime" (import path does not begin with hostname)”

    在编译golang的工程时提示错误的, 提示的错误信息如下: package bytes: unrecognized import path "bytes" (import pat ...

  9. react 核心技术点

    1.react生命周期 react生命周期分为初始化阶段.运行阶段.销毁阶段. (1) 初始化阶段: componentWillMount:实例挂载之前 Render:渲染组件 componentDi ...

  10. Python3基础 None 使用is来判断

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...