嵌入式Linux系统编程学习之三十四 Socket 编程
文章目录
- 一、使用 TCP 的流程图
- 1.1 头文件包含
- 1.2 socket 函数
- 1.3 bind 函数
- 1.4 listen 函数
- 1.5 accept 函数
- 1.6 recv 函数
- 1.7 send 函数
- 1.8 close 函数
- 客户端
- 1.9 connect 函数
- 二、使用 UDP 的流程图
- 2.1 sendto 函数
- 2.2 recvfrom 函数
- 三、设置套接口的选项 setsockopt 的用法
一、使用 TCP 的流程图
TCP 通信的基本步骤:
TCP 通信的基本步骤中服务器端的情况。
1.1 头文件包含
#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdio.h>#include <stdlib.h>
1.2 socket 函数
生成一个套接口描述符。
int socket(int domain, int type, int protocol);
参数 domain → {AF_INET:IPv4 网络协议 AF_INET6:IPv6 网络协议}
参数 type → {tcp:SOCK_STREAM udp:SOCK_DGRAM}
参数 protocol → 指定 Socket 所使用的传输协议编号,通常为 0 。
返回值:成功则返回套接口描述符,失败返回 -1 。
示例:
int sfd = socket(AF_INET, SOCK_STREAM, 0);if(sfd == -1){perror("socket");exit(-1);}
1.3 bind 函数
用来绑定一个端口号和 IP 地址,使套接口与指定的端口号和 IP 地址相关联。
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
参数 sockfd → 前面 socket 的返回值。
参数 my_addr → 结构体指针变量。
参数 addrlen → sockaddr 的结构体长度。通常计算 sizeof(struct sockadd);
返回值:成功则返回 0,失败返回 -1。
对于不同的 socket domain 定义了一个通用的数据结构:
struct sockaddr //不常用{unsigned short int sa_family; //调用 socket() 时的 domain 参数,即 AF_INET 值char sa_data[14]; //最多使用14个字符串长度};
此 sockaddr 结构会因使用不同的 socket domain 而有不同的结构定义,如使用 AF_INET domain,其 sockaddr 结构定义为:
struct aockaddr_in //常用{unsigned short int sin_family; //即为 sa_family → AF_INETuint16_t sin_port; //为使用的 port 编号struct in_addr sin_addr; //为 IP 地址unsigned char sin_zero[8]; //未使用};struct in_addr{uint32_t s_addr;};
示例:
struct sockaddr_in my_addr; //定义结构体变量memset(&my_addr, 0, sizeof(struct sockaddr)); //将结构体清空//或 bzero(&my_addr, sizeof(struct sockaddr));my_addr.sin_family = AF_INET; //表示采用IPv4网络协议my_addr.sin_port = htons(8888); //表示端口号为8888,通常是一个大于1024的值//htons()用来将参数指定的16位hostshort转换成网络字符顺序my_addr.sin_addr.s_addr = inet_addr("192.168.0.101");//inet_addr() 用来将IP地址字符串转换成网络所使用的二进制数字,如果INADDR_ANY,表示服务器自动填充本机IP地址if(bind(sfd, (struct sockaddr *)&my_addr, sizeof(struct socket)) == -1){perror("bind");close(sfd);exit(0);}
注意:通过将 my_addr.sin_port 置为 0,函数会自动为你选择一个未占用的端口号来使用。
同样,通过将 my_addr.sin_addr.a_addr 置为 INADDR_ANY,系统会自动填入本机 IP 地址。
1.4 listen 函数
使服务器的这个端口和 IP 处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接。
int listen(int sockfd, int backlog);
参数 sockfd 为前面 Socket 的返回值。
参数 backlog 指定同时能处理的最大连接要求,通常为 10 或者 5 。最大值可设至 128 。
返回值:成功则返回 0,失败返回 -1 。
示例:
if(listen(sfd, 10) == -1){perror("listen");close(sfd);exit(-1);}
1.5 accept 函数
接受远程计算机的连接请求,建立起与客户机之间的通信连接。
服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。
当 accept 函数接受一个连接时,会返回一个新的 Socket 标识符,以后的数据传输和读取就要通过这个新的 Socket 编号来处理,原来参数中的 Socket 也可以继续使用,继续监听其他客户机的连接请求。
类似于移动营业厅,如果有客户打电话给 10086,此时服务器就会请求连接,处理一些事务之后,就通知一个话务员接听客户的电话,后面的所有操作,此时已与服务器没有关系,而是话务员跟客户的交流。
对应过来,客户请求连接服务器,服务器先做了一些绑定和监听等操作之后,如果允许连接,则调用 accept 函数产生一个新的套接字,然后用这个新的套接字跟客户进行数据收发。
也就是说,服务器跟一个客户端连接成功,会有两个套接字。
int accept(int s, struct sockaddr *addr, int *addrlen);
参数 s 为前面 Socket 的返回值,即 sfd
参数 addr 为结构体变量,和 bind 的结构体是同种类型的,系统会把远程主机的信息(远程主机的地址和端口号信息)保存到这个指针所指的结构体中。
参数 addrlen 表示结构体的长度,为整型指针。
返回值:成功则返回新的 Socket 处理代码 cfd,失败返回 -1。
示例:
struct sockaddr_in clientaddr;memset(&clientaddr, 0, sizeof(struct sockaddr));int addrlen = sizeof(struct sockaddr);int cfd = accept(sfd, (struct sockaddr *)&clientaddr, &addrlen);if(cfd == -1){perror("accept");close(sfd);exit(-1);}printf("%s %d success connect\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
1.6 recv 函数
用新的套接字来接收远端主机传来的数据,并把数据存到由参数 buf 指向的内存空间。
int recv(int sockfd, void *buf, int len, unsigned int flags);
参数 sockfd 为前面 accept 的返回值,即 cfd,也就是新的套接字。
参数 buf 表示缓冲区。
参数 len 表示缓冲区的长度。
参数 flags 通常为 0。
返回值:成功则返回实际接收到的字符数,可能会少于你所指定的接收长度;失败返回 -1 。
示例:
char buf[512] = {0};if(recv(cfd, buf, sizeof(buf), 0) == -1){perror(recv);close(cfd);close(sfd);exit(-1);}puts(buf);
1.7 send 函数
用新的套接字发送数据给指定的远端主机。
int send(int s, const void *msg, int len, unsigned int flags);
参数 s 为前面 accept 的返回值,即 cfd。
参数 msg 一般为常量字符串。
参数 len 表示长度。
参数 flags 通常为 0 。
返回值:成功则返回实际传送出去的字符数,可能会少于你所指定的发送长度,失败返回 -1 。
示例:
if(send(cfd, "hello", 6, 0) == -1){perror("send");close(cfd);close(sfd);exit(-1);}
1.8 close 函数
当使用完文件后若已不再需要则可使用 close() 关闭该文件,并且 close() 会让数据写回磁盘,并释放该文件所占用的资源。
int close(int fd);
参数 fd 为前面的 sfd,cfd 。
返回值:若文件顺利关闭则返回 0,发生错误则返回 -1 。
示例:
close(cfd);close(sfd);
客户端
1.9 connect 函数
用来请求连接远程服务器,将参数 sockfd 的 Socket 连至参数 serv_addr 指定的服务器 IP 和端口号上去。
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
参数 sockfd 为前面 Socket 的返回值,即 sfd。
参数 serv_addr 为结构体指针变量,存储着远程服务器的 IP 与端口号信息。
参数 addrlen 表示结构体变量的长度。
返回值:成功则返回 0,失败返回 -1 。
示例:
struct sockaddr_in seraddr;memset(&seraddr, 0, sizeof(struct sockaddr));seraddr.sin_family = AF_INET;seraddr.sin_port = htons(8888);seraddr.sin_sddr.s_addr = inet_addr("192.168.0.101");if(connect(sfd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr)) == -1){perror("connect");close(sfd);exit(-1);}
二、使用 UDP 的流程图
UDP 通信流程图:
2.1 sendto 函数
原型为:
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
该函数比 send 函数多两个参数;
参数 to 表示目的机的 IP 地址和端口号信息;
参数 tolen 常常被赋值为 sizeof(struct sockaddr);
返回值:成功返回实际发送的数据字节长度,出现发送错误时返回 -1 。
2.2 recvfrom 函数
原型为:
int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
参数 from 表示连接机的 IP 地址和端口号信息;
参数 fromlen 常置为 sizeof(struct sockaddr);函数返回时,fromlen 包含实际存入 from 中的数据字节数;
返回值:成功返回接收到的字节数,出现错误时返回 -1,并置相应的 errno 。
三、设置套接口的选项 setsockopt 的用法
函数原型为:
#include <sys/types.h>#include <sys/socket.h>int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数 sockfd 表示一个套接口的描述字;
参数 level 选项定义的层次,支持 SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP 和 IPPROTO_IPV6;
参数 optname 需设置的选项;
参数 optval 指针,指向存放选项值的缓冲区;
参数 optlen 存放 optval 缓冲区的长度。
注意:以下操作全部必须放在 bind 之前执行,另外通常用于 UDP 的。
(1)如果在已经处于 ESTABLISHED 状态下的 Socket (一般由端口号和标志符区分)调用 closesocket(一般不会立即关闭而要经历 TIME_WAIT 的过程)后想继续重用该 Socket ,使用如下代码:
int reuse = 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(int));
(2)如果要已经处于连接状态的 Socket 在调用 closesocket 后强制关闭,不经历 TIME_WAIT 的过程,使用如下代码:
int reuse = 0;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(int));
(3)在 send() 和 recv() 过程中有时由于网络状况等原因,发收不能按照预期进行,需设置收发时限,使用如下代码:
int nNetTimeout = 1000; //1ssetsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout, sizeof(int)); //发送时限setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout, sizeof(int)); //接收时限
(4)在 send() 的时候,返回的是实际发送出去的字节(同步)或发送到 Socket 缓冲区的字节(异步),系统默认的状态发送和接收一次为 8688 字节(约为 8.5Bit);在实际的过程中发送数据和接收数据量比较大,可以设置 Socket 缓冲区,从而避免了 send() 和 recv() 的不断循环收发:
//接收缓冲区int nRecvBuf = 32*1024; //32Ksetsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char *)&nRecvBuf, sizeof(int));//发送缓冲区int nSendBuf = 32*1024; //32Ksetsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char *)&nSendBuf, sizeof(int));
(5)如果在发送数据时,希望不经历由系统缓冲区到 Socket 缓冲区的复制而影响程序的性能:
int nZero = 0;setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&nZero, sizeof(int));
(6)同上在 recv() 完成上述功能(默认情况是将 Socket 缓冲区的内容复制到系统缓冲区):
int sZero = 0;setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&sZero, sizeof(int));
(7)一般在发送 UDP 数据报的时候,希望该 Socket 发送的数据具有广播特性:
int bBroadcast = 1;setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char *)&bBoroadcast, sizeof(int));
嵌入式Linux系统编程学习之三十四 Socket 编程相关推荐
- 华为鸿蒙系统HarmonyOS学习之十四:方舟编译器
华为鸿蒙系统HarmonyOS学习之十四:方舟编译器 方舟编译器是华为自研的一个支持多种编程语言,多种芯片平台的联合编译编程平台,而经过方舟编译器编译适配后的APP,运行效率会大大提高,拥有更为流畅的 ...
- Linux的基本学习(十四)——进程管理(下)与SELinux
Linux的基本学习(十四)--进程管理(下)与SELinux 前言 进程这部分内容真是不少,来,我们继续跟着鸟哥学习. 特殊文件与进程 具有SUID/SGID权限的命令执行状态 SUID的权限其实与 ...
- VS2013/MFC编程入门之三十四(工具栏:工具栏资源及CToolBar类)
上一节中讲了菜单及CMenu类的使用,这一节讲与菜单有密切联系的工具栏. 工具栏简介 工具栏一般位于主框架窗口的上部,菜单栏的下方,由一些带图片的按钮组成.当用户用鼠标单击工具栏上某个按钮时,程序会执 ...
- Linux系统下一切皆文件,socket编程浅析
"一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...
- 网络编程学习笔记一:Socket编程
"一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...
- 嵌入式Linux系统编程学习之三十线程的同步
文章目录 一.条件变量 1.创建和注销 2.等待和激发 3.其他 二.信号灯 1.创建和注销 2.点灯和灭灯 3.获取灯值 4.其他 一.条件变量 条件变量是利用线程间共享的全局变量进行同步的一种 ...
- 嵌入式Linux系统编程学习之十四signal信号处理机制
可以用函数 signal 注册一个信号处理函数,原型为: #include <signal.h>typedef void(*sighandler_t)(int); //函数指针 voi ...
- OSG三维渲染引擎编程学习之三十六:“第四章:OSG几何体绘制” 之 “4.1 OSG基础绘图类”
目录 第四章 OSG几何体绘制 4.1 OSG基础绘图类 4.1.1 Drawable可绘制类 4.1.2 PrimitiveSet图元集 第四章 OSG几何体绘制
- 嵌入式linux系统开发概述之三----工具链(Toolchain)
3 工具链(Toolchain) 设置工具链在主机机器上创建一个用于编译将在目标上运行的内核和应用程序的构建环境---这是因为目标硬件可能没有与主机兼容的二进制执行级别. 工具链由一套用于编译.汇编 ...
最新文章
- 机器人建模中移动关节如何建立坐标系_机器人工程师进阶之路(八)指数积(PoE)建立机械臂模型及正运动...
- php过滤特殊字符mysql攻击_php – MySQL在特殊字符处切断字符串
- 百度云网盘 360云盘 金山快盘 等 + Git GUI 实现代码版本管理-个人篇
- char和vchar
- C#中使用Newtonsoft.Json实现对Json字符串的解析
- Java单例的常见形式
- Android开发中调用系统窗口的方法
- MFC控件编程之组合框跟列表框
- 如果我是推荐算法面试官,我会问哪些问题?
- 联想提取exe文件中的bios文件_3900不带X能干过99K吗?品牌机做视频渲染可以?联想刃7000P评测...
- atitit 解决教学记忆问题 压缩算法原理 哈夫曼 LZ77 gzip zlib deflate算法.docx 目录 1. 压缩理论	1 1.1. 柯氏复杂性	1 2. 1 RLE	1
- powerbi嵌入到HTML5,如何把Power BI嵌入到Web應用中
- 基于python的网络爬虫系统的设计与实现
- 内网通过计算机名查询IP地址
- FBReader导入eclipse 和Androidstudio相关问题
- 居家也可以减肥?懒人瘦身也能行-987减肥网
- UE4 解决半透明材质显示错误
- 关系模式判断候候选关键字 与 函数依赖无损连接
- 关于使用条码打印机指令打印汉字的问题
- 计算机 发声原理,模拟电子琴演奏程序设计。微机中扬声器控制发声原理如练习图10.1所示,其中用到8255与8253两个芯片。...
热门文章
- 解决django需要手动调整数据库,避免manage.py各种报错
- django配置mysql,并解决NameError: name ‘_mysql‘ is not defined报错
- pytorch 实现 LSTM AutoEncoder 与案例
- python 灰色系统预测GM
- php 查看引入文件,include - 引入文件
- mysql使用cmd命令连接_通过cmd命令连接mysql
- Django 模板中 变量 过滤器 标签 的使用方法
- [卷积核]空洞卷积(转)
- 第一篇:容易遗忘的“枚举”
- java APIs for database -------- JDBC (1)connection