linux 网络编程socket

news2024/11/26 20:37:46

前言

        socket(套接字)是linux下进程间通信的一种方式,通常使用C-S(客户端-服务端)的方式通信,它可以是同一主机下的不同进程间通信或者不同主机的进程通信。

        socket是夹在应用层和TCP/UDP协议层间的软件抽象,向应用层开发人员提供API接口,向下隐藏协议层的具体细节,大大方便了我们开发人员。很多平台都实现了BSD scoket标准scoket接口,增强了可移植性。

        在进行socket网络编程之前,有必要对计算机网络有个大概的了解,这里推荐一篇博文,链接如下:TCP/IP协议族之TCP、UDP协议详解

1. socket概述

1.1 表示方法

Socket=(IP地址:端口号),套接字的表示方法是点分十进制的lP地址后面加上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点所确定。

1.2 socket主要类型

 流套接字(SOCK_STREAM):用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复送,并按顺序接收。

 数据报套接字(SOCK_DGRAM):提供一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。

 原始套接字(SOCK_RAW):原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送的数据必须使用原始套接。

2. socket-API接口

2.1 socket函数

socket函数用于初始化创建一个通信端点,调用成功返回一个socket描述符,类似于open函数的文件描述符;

可以使用close函数来关闭socket,释放占用的资源。

头文件:

#include <sys/types.h>

#include <sys/socket.h>

函数原型:

int socket(int domain, int type, int protocol)

参数:

domain:指示通信协议族;

        AF_UNIX或 AF_LOCAL:Local communication本地通信;

        AF_INET:IPv4 Internet protocols,就是我们通常用的ipv4地址;

        AF_INET6:IPv6 Internet protocols,ipv6地址;

        AF_NETLINK:Kernel user interface device;

        其它;

type:指定套接字的类型;

        SOCK_STREAM:提供有序的、可靠的、双向的、基于连接的字节流,默认TCP协议;

        SOCK_DGRAM:固定长度的、无连接的、不可靠的报文传递,默认UDP协议;

        SOCK_RAW:允许应用程序访问网络层的原始数据包,原始套接字;

protocol:通常设置为0,表示为给定的通信域和套接字类型选择默认协议。

返回值:

成功:返回非负数socket描述符;

失败:返回 -1。

2.2  bind函数

bind函数通常用于服务端,将服务端的socket文件与网络中的进程地址(IP地址+端口号)绑定;

指向sockaddr_in的结构体指针也可以指向sockaddr的结构体,所以经常使用sockaddr_in替代。

头文件:

#include <sys/types.h>

#include <sys/socket.h>

函数原型:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

参数:

sockfd:socket描述符;

addr:地址(ip地址+端口号);

addrlen:第二个参数addr长度;

返回值:

成功:返回 0;

失败:返回 -1。

struct sockaddr { 
    sa_family_t sa_family; 
    char sa_data[14]; 
}


struct sockaddr_in {
    sa_family_t sin_family;  //协议族
    in_port_t sin_port;      //端口号
    struct in_addr sin_addr; // IP 地址
    unsigned char sin_zero[8]; 
};

2.3 listen函数

listen函数调用进入监听状态,通常用于服务端,服务端调用listen函数进入监听状态,等待客户端的连接请求。

参数backlog表明连接等待队列的上限,如果该服务端已经连接上一个客户端,之后其它客户端的连接请求将进入连接等待队列,同时该队列会有个上限值,不可能无限大。

头文件:

#include <sys/types.h>
#include <sys/socket.h>

函数原型:

int listen(int sockfd, int backlog)

参数:

backlog:连接等待队列上限值;

返回值:

成功:返回  0;

失败:返回 -1。

2.4 accept函数

accept()函数用于获取客户端的连接请求并建立连接,通常用于服务端;它是一个阻塞函数,如果没有客户端的连接请求,则会阻塞等待;accept()函数返回的套接字连接到调用connect()的客户端,服务端通过该套接字与客户端进行数据交互。

头文件:

#include <sys/types.h>
#include <sys/socket.h>

函数原型:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

参数:

sockfd:socket()函数返回的描述符;

addr:struct sockaddr指针变量,是一个传出参数,用于存放客户端的连接信息,可设为NULL,表示不关心客户端信息;

addrlen:表明传出参数addr的字节长度;可设为NULL;

返回值:

成功:返回一个新的socket描述符;

失败:返回 -1 。

2.5 connect函数

通常用于客户端,调用connect函数请求与服务端连接;常说的三次握手就是由connect触发的。

函数原型:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

参数:

sockfd:调用socket生成的描述符;

addr:指定服务端的IP地址以及端口号等信息;

addrlen:表明addr信息长度;

返回值:

成功:返回 0;

失败:返回 -1。

2.6 发送函数

2.6.1 send函数

send函数通常用于TCP数据包的发送。

send()成功返回只能表示数据包已经发送出去了,并不能代表接收端已经接收到数据了。

函数原型:

ssize_t send(int sockfd, const void *buf, size_t len, int flags)

参数:

sockfd:socket函数返回值;

buf:发送的数据缓冲区;

len:发送的数据大小;

flags:标志位,通常设为0;

        MSG_DONTWAIT:允许非阻塞操作;

        MSG_MORE:延迟发送数据包允许写更多数据;

        其它......

返回值:

失败:返回 -1;

2.6.2 sendto函数

常用于UDP数据发送,因为UDP是无连接的,因此需要指定目的地址。

函数原型:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen)

参数:

dest_addr:数据发送的目的地址;

addrlen:目的地址长度;

2.7 接收函数

2.7.1 recv函数

用于TCP数据的接收;阻塞模式下,直到有数据才返回。

函数原型:

ssize_t recv(int sockfd, void *buf, size_t len, int flags)

参数:

buf:数据接收缓冲区;

flags:一般设为0;

        MSG_DONTWAIT:启动非阻塞操作;

        MSG_PEEK:返回数据包内容而不真正取走数据包;

        其它......

返回值:

成功:返回接收的数据长度;

失败:返回 -1。

2.7.2 recvfrom函数

一般用于UDP数据的接收。

函数原型:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen)

参数:

src_addr:指向数据源端地址。

2.8 IP地址格式转化

2.8.1 inet_ntop函数

用于二进制形式字符串转为十进制形式字符串。

头文件:

#include <arpa/inet.h>

函数原型:

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)

参数:

af:为AF_INET(ipv4)或AF_INET6(ipv6);

src:需要转化的源字符串;一般为struct in_addr结构体的对象;

dst:转换生成的字符串存放的缓冲区;

size:缓冲区的大小;

返回值:

成功:返回指向dst的指针;

失败:返回NULL。

代码示例:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    struct in_addr src_addr;
    char dst_addr[32];
    src_addr.s_addr = 0x6401a8c0;
    inet_ntop(AF_INET,&src_addr,dst_addr,sizeof(dst_addr));
    printf("dst addr:%s\n",dst_addr);
    return 0;
}

2.8.2 inet_pton函数

用于十进制形式字符串转为二进制形式字符串,返回值为大端存储。

头文件:

#include <arpa/inet.h>

函数原型:

int inet_pton(int af, const char *src, void *dst)

返回值:

成功:返回1;

格式无效:返回0;

失败:返回-1。

struct in_addr {
    __be32  s_addr;
};

示例代码:

#include <stdio.h>
#include <arpa/inet.h>

#define IP_ADDR "192.168.1.100"
int main()
{
    struct in_addr dst_addr;
    inet_pton(AF_INET,IP_ADDR,&dst_addr);
    printf("dst addr:%x\n",dst_addr.s_addr);
    return 0;
}

2.9 存储顺序

host:主机字节序(小端存储);

network:网络字节序(大端存储)。

2.9.1 主机转网络存储顺序

头文件:

#include <arpa/inet.h>

函数原型:

uint32_t htonl(uint32_t hostlong);        //32位IP地址

uint16_t htons(uint16_t hostshort);      //16位端口号

INADDR_ANY指定地址为0.0.0.0地址,表示监听所有的IP地址

2.9.2 网络转主机存储顺序

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

3.  socket工作流程

3.1  TCP工作流程

3.2 UDP工作流程

4. TCP通信测试代码

4.1 服务端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define PORT 6666

int main()
{
    int socket_fd,fd;
    int ret;
    char send_buff[64] = "Data was send by server.";    //连接上发给客户端的数据
    char recv_buff[64];
    char client_ip[32];
    struct sockaddr_in client_msg;
    int msg_len = sizeof(client_msg);
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    socket_fd = socket(AF_INET,SOCK_STREAM,0);
    if(socket_fd < 0){
        perror("socket");
        exit(0);
    }
    ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    if(ret == -1){
        perror("bind");
        close(socket_fd);
        exit(0);
    }

    ret = listen(socket_fd,32);
    if(ret == -1){
        perror("listen");
        close(socket_fd);
        exit(0);
    }
    printf("listen...\n");
    fd = accept(socket_fd,(struct sockaddr *)&client_msg,&msg_len);
    inet_ntop(AF_INET,&client_msg.sin_addr.s_addr,client_ip,sizeof(client_ip));
    printf("client ip addr:%s,port:%d\n",client_ip,client_msg.sin_port);
    send(fd,send_buff,sizeof(send_buff),0);
    while(1)
    {
        memset(recv_buff,0,sizeof(recv_buff));
        recv(fd,recv_buff,sizeof(recv_buff),0);    //阻塞接收客户端的数据
        printf("recv buff :%s\n",recv_buff);
        if(strncmp("quit",recv_buff,4) == 0){
            printf("socket closer.\n");
            close(fd);
            close(socket_fd);
            exit(0);
        }
    }
    return 0;
}

4.2 客户端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
    int socket_fd;
    int ret;
    char send_buff[] = "From client datas.";
    char recv_buff[64];
    struct sockaddr_in client_msg;

    if(argc < 3){
        exit(0);
    }
    printf("ip:%s,port:%s\n",argv[1],argv[2]);
    client_msg.sin_family = AF_INET;
    inet_pton(AF_INET, argv[1], &client_msg.sin_addr);    //传入的ip地址
    client_msg.sin_port = htons(atoi(argv[2]));    //传入的端口号
    htonl(client_msg.sin_addr.s_addr);

    socket_fd = socket(AF_INET,SOCK_STREAM,0);
    if(socket_fd == -1){
        perror("socket");
        exit(0);
    }
    printf("socket_fd :%d\n",socket_fd);
    ret = connect(socket_fd,(struct sockaddr *)&client_msg,sizeof(client_msg));  //发起连接
    if(ret == -1){
        perror("connect");
        close(socket_fd);
        exit(0);
    }
    recv(socket_fd,recv_buff,sizeof(recv_buff),0);
    printf("recv buff : %s\n",recv_buff);

    while(1)
    {
        memset(send_buff,0,sizeof(send_buff));
        printf("client data input: ");
        fgets(send_buff,sizeof(send_buff),stdin);    //从键盘输入数据信息
        send(socket_fd,send_buff,sizeof(send_buff),0);
        if(strncmp("quit",send_buff,4) == 0){
            close(socket_fd);
            exit(0);
        }
    }
    return 0;
}

4.3 测试结果

在Ubuntu下打开两个shell终端,分别运行服务端程序和客户端程序。

客户端传入服务端ip地址和端口号的参数,然后向服务端发送数据信息测试,结果如下图。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/180884.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器自动翻译古文拼音 - 将进酒拼音版本,译文拼音版本

写了一个程序&#xff0c;用来给佛经和古诗加上拼音&#xff0c;并处理多音字和排版&#xff0c;顺便加上翻译。 定期翻译一些&#xff0c;给老人和小孩子用。 将进酒 君不见&#xff0c;黄河之水天上来&#xff0c;奔流到海不复回。 君不见&#xff0c;高堂明镜悲白发&…

Servlet介绍及其概念

Servlet介绍及其概念一、Web基础二、编写HTTP Server&#xff0c;打印Hello,World三、Servlet的出现1. 思考上述HTTP服务器的问题2. 实现代码重用&#xff0c;简化开发过程3. 实现最简单的Servlet4. 导入依赖5. pom.xml文件6. Servlet版本问题7. 整个Servlet工程结构四、运行Se…

Windows卸载与清除工具 “ Geek 与 CCleaner ”

前言 &#x1f4dc;“作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴 目录 前言 一、Geek的简介 1、大概介绍 2、详细介绍 二、Geek的下载 1、…

千峰Ajax【fetch和promise】

promise基础 <script>// Promise构造函数var q new Promise(function (resolve, reject) {//异步setTimeout(function () {// 成功// resolve(["111", "222", "333"]);// 失败reject("error");}, 2000);});// q是promise对象q…

利用系统函数与堆栈快速定位app关键代码

string.trim 这个还是比较关键的&#xff0c;没准可以从这里得到加密方式&#xff0c;或者挖到sql注入&#xff0c;文件上传等漏洞。进一步利用可以打印堆栈来用 Java.perform(function(){function showStack(){console.log(Java.use("android.util.Log").getStack…

TCP/IP网络编程——基于 TCP 的服务端/客户端(下)

完整版文章请参考&#xff1a; TCP/IP网络编程完整版文章 文章目录第 5 章 基于 TCP 的服务端/客户端&#xff08;2&#xff09;5.1 回声客户端的完美实现5.1.1 回声服务器没有问题&#xff0c;只有回声客户端有问题&#xff1f;5.1.2 回声客户端问题的解决办法5.1.3 如果问题不…

chrome插件开发时使用import

问题描述 在进行chrome插件开发时&#xff0c;我们有时会希望把一些公共的方法包装成一个模块&#xff0c;例如发送网络请求的方法&#xff0c;然后在其他js文件中import然后调用&#xff0c;但是在实际操作时&#xff0c;遇到了这样的问题&#xff1a; 控制台报错cannot use …

Vistual Studio Code 安装与配置C/C++环境

1. 下载VScode 2. 安装cpptools工具 3. 下载MinGW 4. 配置环境变量 5. 使用简单的.cpp文件配置C环境 6. 运行 注&#xff1a;本文所有的地址配置要根据读者的实际情况来&#xff0c;不要照文章复制&#xff01;&#xff01;&#xff01; 下载VScode 下载链接&#xff1a;https…

浏览器调用本地DLL的方法

要在浏览器中调用本地DLL&#xff0c;常见的方法是使用插件。但是为了安全&#xff0c;现在有的浏览器对插件开发做了限制&#xff0c;不让插件调用外部DLL。比如说Chrome&#xff0c;为了调用外部的DLL&#xff0c;我们只能使用早期的chrome版本。 还有一种方法就是在电脑上安…

linux编辑器的使用(gcc,g++)

前言 gcc/g是一个编译器。 我们程序的翻译有四个步骤1.预处理(头文件展开&#xff0c;条件编译&#xff0c;宏替换&#xff0c;去注释)2.编译(c语言汇编语言)3.汇编(汇编->可重定位目标二进制文件&#xff0c;不可以被执行的&#xff0c;bin.obj)----只是把我们自己的代码进…

下载Windows ISO镜像的方法 (超详细 适合新手入门)

前言 &#x1f4dc;“作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴 目录 前言 一、镜像介绍 1、大概介绍 2、详细介绍 二、下载Window…

Solon 1.12.4 发布

一个更现代感的 Java "生态型"应用开发框架&#xff1a;更快、更小、更自由。不是 Spring&#xff0c;没有 Servlet&#xff0c;也无关 JavaEE&#xff1b;新兴独立的开放生态 &#xff08;已有150来个生态插件&#xff09; 。主框架仅 0.1 MB。 相对于 Spring Boot…

JVM学习总结,全面介绍运行时数据区域、各类垃圾收集器的原理使用、内存分配回收策略

参考资料&#xff1a;《深入理解Java虚拟机》第三版 文章目录一&#xff0c;运行时数据区域&#xff08;基础重中之重&#xff09;二&#xff0c;垃圾收集器与内存分配策略1&#xff09;对象已死2&#xff09;再谈引用3&#xff09;对象回收4&#xff09;内存分代收集理论&…

【HBase入门】10. HBase高可用、HBase架构、常见问题汇总

HBase高可用 考虑关于HBase集群的一个问题&#xff0c;在当前的HBase集群中&#xff0c;只有一个Master&#xff0c;一旦Master出现故障&#xff0c;将会导致HBase不再可用。所以&#xff0c;在实际的生产环境中&#xff0c;是非常有必要搭建一个高可用的HBase集群的。 HBase…

【Maven】聚合与继承

目录 1. 聚合工程 2. 聚合工程开发 3. 继承关系 4. 继承关系开发 5. 聚合与继承的区别 1. 聚合工程 什么叫聚合&#xff1f; 聚合&#xff1a;将多个模块组织成一个整体&#xff0c;同时进行项目构建的过程称为聚合 聚合工程&#xff1a;通常是一个不具有业务功能的”空…

如何使用Excel列提取合并器提取多个表格中的一列数据然后合并到一个文件

在我们日常工作中&#xff0c;你可能经常遇到有几十个或更多的Excel文件&#xff0c;每个文件中都包含了相同类型的信息例如姓名、邮箱、地址等等&#xff0c;但它们却在不同文件中不同的列。当你想进行数据汇总或合并的时候&#xff0c;把不同表中同样类型的数据合并在一起&am…

一款用于PE文件绑定免杀的工具: Shellter

简介 Shellter是一种动态二进制程序壳程序&#xff0c;它可以在现有的可执行文件中隐藏恶意软件。它使用动态链接库技术来实现恶意代码的注入&#xff0c;并且可以在不修改现有的可执行文件的情况下进行注入。这使得它非常难以检测&#xff0c;因为它不会改变文件的哈希值或数…

[数据结构基础]排序算法第二弹 -- 选择排序、堆排序和冒泡排序

目录 一. 选择排序 1.1 选择排序的实现思路 1.2 选择排序函数代码 1.3 选择排序的时间复杂度分析 二. 堆排序 2.1 堆排序的实现思路 2.2 堆排序函数代码 2.3 堆排序的时间复杂度分析 三. 冒泡排序 3.1 冒泡排序的基本思想 3.2 冒泡排序函数代码 3.3 冒泡排序的时间…

【微服务】Gateway统一网关

更多内容点击查看微服务学习专栏 一.引入 我们为什么需要网关&#xff1f; 当我们所有的服务摆在那里允许任何人发送请求访问是不是不太安全&#xff1f; 不是所有的业务都是对外公开的&#xff01; Gateway网关是我们服务的守门神&#xff0c;是所有微服务的统一入口&…

机器自动翻译古文拼音 - 十大宋词 - 桂枝香 金陵怀古 王安石

桂枝香金陵怀古 北宋王安石 登临送目&#xff0c;正故国晚秋&#xff0c;天气初肃。 千里澄江似练&#xff0c;翠峰如簇。 归帆去棹斜阳里&#xff0c;背西风&#xff0c;酒旗斜矗。 彩舟云淡&#xff0c;星河鹭起&#xff0c;画图难足。 念往昔、繁华竞逐&#xff0c;叹门外…