端口映射 Morecoin ajax wpf string checkbox threejs swift2 ionic framework vue传值 vue代码规范 php零基础入门视频 pmp视频教程 coreldraw入门学习 quartz配置 python调用方法 python中re模块 stringjava java时间戳转换成时间 java架构 linuxtail命令 电子书之家 在线pr序列设置 跳一跳脚本 笔记本外接显示器好吗 wscript flash制作工具 魔兽地图七个人 painter下载 ps字体描边 混凝土配合比计算软件 快递电子面单打印软件 lol游戏环境异常 谷歌浏览器xp版下载 数据库密码忘了怎么办 php教程下载 android模拟器下载 dataformatstring 联想人脸识别 ap天赋
当前位置: 首页 > 学习教程  > 编程语言

(网络编程笔记):SOCKET编程

2020/10/8 18:33:47 文章标签:

目录 SOCKET编程 网络字节序 IP地址转换函数 socket编程用到的重要的结构体:struct sockaddr socket编程主要的API函数介绍 socket的API函数编写服务端和客户端程序的步骤图示 服务端开发流程 客户端的开发流程 SOCKET编程 传统的进程间通信借助内核提供的IPC机制进行&#xff…

目录

SOCKET编程

网络字节序

IP地址转换函数

socket编程用到的重要的结构体:struct sockaddr

socket编程主要的API函数介绍

socket的API函数编写服务端和客户端程序的步骤图示

服务端开发流程

客户端的开发流程

 

SOCKET编程

  • 传统的进程间通信借助内核提供的IPC机制进行,但是只能限于本机通信,若要跨机通信,,就必须使用网络通信。( 本质上借助内核-内核提供了socket伪文件的机制实现通信----实际上是使用文件描述符),这就需要用到内核提供给用户的socket API函数库
  • 既然提到socket伪文件,所以可以使用文件描述符相关的函数read write
  • 使用socket会建立一个socket pair
  • 如下图,一个文件描述符操作两个缓冲区, 这点跟管道是不同的, 管道是两个文件描述符操作一个内核缓冲区.

网络字节序

  • 大端和小端的概念
    • 大端: 低位地址存放高位数据, 高位地址存放低位数据
    • 小端: 低位地址存放低位数据, 高位地址存放高位数据
  • 大端和小端的使用使用场合:
    • 大端和小端只是对数据类型长度是两个及以上的, 如int  short, 对于单字节没限制, 在网络中经常需要考虑大端和小端的是IP和端口.
  • 网络传输用的是大端法, 如果机器用的是小端法, 则需要进行大小端的转换
  • 下面4个函数就是进行大小端转换的函数:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • 函数名的h表示主机host, n表示网络network, s表示short, l表示long
  • 上述的几个函数, 如果本来不需要转换函数内部就不会做转换.

IP地址转换函数

  • p->表示点分十进制的字符串形式
  • to->到
  • n->表示network网络
int inet_pton(int af, const char *src, void *dst);
  • 函数说明: 将字符串形式的点分十进制IP转换为大端模式的网络IP(整形4字节数)
  • 参数说明:
    • af: AF_INET
    • src: 字符串形式的点分十进制的IP地址
    • dst: 存放转换后的变量的地址
  • 例如: inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);    
    • 手工也可以计算: 如192.168.232.145, 先将4个正数分别转换为16进制数, 
    • 192--->0xC0  168--->0xA8   232--->0xE8   145--->0x91
    • 最后按照大端字节序存放: 0x91E8A8C0, 这个就是4字节的整形值.
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • 函数说明: 网络IP转换为字符串形式的点分十进制的IP
  • 参数说明:
    • af: AF_INET
    • src: 网络的整形的IP地址
    • dst: 转换后的IP地址,一般为字符串数组
    • size: dst的长度
  • 返回值: 
    • 成功--返回指向dst的指针
    • 失败--返回NULL, 并设置errno
  • 例如: IP地址为010aa8c0, 转换为点分十进制的格式:
    • 01---->1    0a---->10   a8---->168   c0---->192
    • 由于从网络中的IP地址是高端模式, 所以转换为点分十进制后应该为: 192.168.10.1

socket编程用到的重要的结构体:struct sockaddr

  • struct sockaddr结构说明:
struct sockaddr {
        sa_family_t sa_family;
        char     sa_data[14];
}
  • struct sockaddr_in结构:
struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
    uint32_t  s_addr;     /* address in network byte order */
};	 //网络字节序IP--大端模式
  • 通过man 7 ip可以查看相关说明

socket编程主要的API函数介绍

int socket(int domain, int type, int protocol);
  • 函数描述: 创建socket
  • 参数说明:
    • domain: 协议版本
      • AF_INET IPV4
      • AF_INET6 IPV6
      • AF_UNIX AF_LOCAL本地套接字使用
    • type:协议类型
      • SOCK_STREAM 流式, 默认使用的协议是TCP协议
      • SOCK_DGRAM  报式, 默认使用的是UDP协议
    • protocal: 
      • 一般填0, 表示使用对应类型的默认协议.
  • 返回值: 
    • 成功: 返回一个大于0的文件描述符
    • 失败: 返回-1, 并设置errno
  • 当调用socket函数以后, 返回一个文件描述符, 内核会提供与该文件描述符相对应的读和写缓冲区, 同时还有两个队列, 分别是请求连接队列和已连接队列

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 函数描述: 将socket文件描述符和IP,PORT绑定
  • 参数说明:
    • socket: 调用socket函数返回的文件描述符
    • addr: 本地服务器的IP地址和PORT, 
struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
//serv.sin_addr.s_addr = htonl(INADDR_ANY);
//INADDR_ANY: 表示使用本机任意有效的可用IP

inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
  • addrlen: addr变量的占用的内存大小 
  • 返回值: 
    • 成功: 返回0
    • 失败: 返回-1, 并设置errno
int listen(int sockfd, int backlog);
  • 函数描述: 将套接字由主动态变为被动态
  • 参数说明:
    • sockfd: 调用socket函数返回的文件描述符
    • backlog: 同时请求连接的最大个数(还未建立连接) 
  • 返回值:
    • 成功: 返回0
    • 失败: 返回-1, 并设置errno                
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);    
  • 函数说明:获得一个连接, 若当前没有连接则会阻塞等待.
  • 函数参数:
    • sockfd: 调用socket函数返回的文件描述符
    • addr: 传出参数, 保存客户端的地址信息
    • addrlen: 传入传出参数,  addr变量所占内存空间大小
  • 返回值:
    • 成功: 返回一个新的文件描述符,用于和客户端通信
    • 失败: 返回-1, 并设置errno值.
  • accept函数是一个阻塞函数, 若没有新的连接请求, 则一直阻塞.
  • 从已连接队列中获取一个新的连接, 并获得一个新的文件描述符, 该文件描述符用于和客户端通信.  (内核会负责将请求队列中的连接拿到已连接队列中)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 函数说明: 连接服务器
  • 函数参数:
    • sockfd: 调用socket函数返回的文件描述符
    • addr: 服务端的地址信息
    • addrlen: addr变量的内存大小
  • 返回值:
    • 成功: 返回0
    • 失败: 返回-1, 并设置errno值
  • 接下来就可以使用write和read函数进行读写操作了.
  • 除了使用read/write函数以外, 还可以使用recv和send函数
  • 读取数据和发送数据:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);    
  • 对应recv和send这两个函数flags直接填0就可以了.
  • 注意: 如果写缓冲区已满, write也会阻塞, read读操作的时候, 若读缓冲区没有数据会引起阻塞.

socket的API函数编写服务端和客户端程序的步骤图示

服务端开发流程

  • 创建socket,返回一个文件描述符lfd---socket()        --该文件描述符用于监听客户端连接
  • 将lfd和IP  PORT进行绑定----bind()
  • 将lfd由主动变为被动监听----listen()
  • 接受一个新的连接,得到一个文件描述符cfd----accept()      ---该文件描述符是用于和客户端进行通信的
  • while(1)
    • {
    •     接收数据---read或者recv
    •      发送数据---write或者send
    •  }
  • 关闭文件描述符----close(lfd)  close(cfd);

客户端的开发流程

  • 创建socket, 返回一个文件描述符cfd---socket()    --该文件描述符是用于和服务端通信
  • 连接服务端---connect() 
  • while(1)
    •  {
    •         //发送数据---write或者send
    •         //接收数据---read或者recv
    • }
  • close(cfd)

 


本文链接: http://www.dtmao.cc/news_show_250052.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?