Linux系统编程—socket网络编程

news2024/11/26 22:36:50

Linux系统编程—socket网络编程

  • 理论概念
    • 1. TCP与UDP对比
    • 端口号作用
  • socket开发过程
  • 服务端
    • 1. socket 创建套接字
    • 2. bind 绑定IP+端口
    • 3. listen 监听客户端
    • 4. accept 接收客户端
    • 5. read / write 数据传输
  • 客户端
    • 1. socket 创建套接字
    • 2. connect 连接服务
    • 3. read / write 数据传输
  • server.c与client.c程序案例

理论概念

1. TCP与UDP对比

  • TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需 要建立连接

  • TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

  • TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
    UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

  • 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

  • TCP首部开销20字节;UDP的首部开销小,只有8个字节

  • TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

端口号作用

  • 一台拥有IP地址的主机可以提供许多服
    ,比如Web服务、FTP服务、SMTP服务等

  • 通过“IP地址+端口号”来区 分不同的服务,端口提供了一种访问通道,服务器一般都是通过端口号来识别的。

socket开发过程

在这里插入图片描述

服务端

1. socket 创建套接字

函数原型

	#include <sys/types.h>          
	#include <sys/socket.h>
	
	int socket(int domain, int type, int protocol);

返回值: 成功返回网络标识符,失败返回 -1
参数分析
domain: 指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族):

  • AF_INET IPv4因特网域
  • AF_INET6 IPv6 因特网域
  • AF_UNIX Unix 域
  • AF_ROUTE 路由套接字
  • AF_KEY 密钥套接字
  • AF_UNSPEC 未指定

type: 指定socket类型

  • SOCK_STREAM
      流式套接字提供可靠的、面向连接的通信流,它使用 TCP 协议,从而保证了数据传输的正确性和顺序性
  • SOCK_DGRAM
      数据报套接字定义了一种无连接的数据,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP
  • SOCK_RAW
      允许程序使用低层协议,原始套接字允许对底层协议如 IP 或ICMP 进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

protocol:
通常赋值 0

  • 0 选择 type 类型对应的默认协议
  • IPPROTO_TCP TCP 传输协议
  • IPPROTO_UDP UDP 传输协议
  • IPPROTO_SCTP SCTP 传输协议
  • IPPROTO_TIPC TIPC 传输协议

2. bind 绑定IP+端口

  • 功能:用于绑定IP地址和端口号到sockfd

函数原型

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

       int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
       //成功返回0,失败返回-1

参数分析

参数类型/值
sockfd服务端网络标识符,即 socket 的返回值
struct sockaddr *addr绑定服务器端 网络协议、IP 地址和端口号的地址结构指针
socklen_t addrlen结构体大小
  • addr
      一个指向包含有本机 IP 地址及端口号等信息的 sockaddr 类型的指针,指向要绑定给 sockfd 的协议地址结构,这个地址结构根据地址创建socket 时的地址协议族的不同而不同

注:
IPV4的实际结构体如下

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_port;      //IP地址结构体
		 unsigned char     sin_zero[8];   //填充 无实际意义
		};

struct sockaddr_in 结构体已在Linux内核系统中定义,成员参数配置如下;

  • sa_family_t  sin_family
    协议族, TCP协议族,AF_INET

  • in_port_t    sin_port
    端口号, 类型为网络字节序,需利用htons函数进行转化为网络字节数
    例:s_addr.sin_port = htons(8888);

  • struct in_addr    sin_port
    IP地址结构体,struct in_addr 类型结构体
    系统中定义的struct in_addr 结构体

    	struct in_addr {
    	        __be32  s_addr;
    	};
    

    将IP地址转化为网络能识别的格式函数原型:

    	#include <netinet/in.h>
    	#include <arpa/inet.h>
    	
    	int inet_aton(const char* straddr,struct in_addr *addrp);
    

    转化典例

    	inet_aton("127.0.0.1", &s_addr.sin_addr );
    

    地址转化API

    	 int inet_aton(char *straddr,struct in_addr *addr)
    	 //将字符串形式的"127.0.0.1"转化为网络能识别的格式
    	 
    	 char *inet_ntoa(struct in_addr inaddr);
    	 //把网络格式的ip地址转化为字符串形式
    

结构体配置及bin函数整合实例

	struct sockaddr_in s_addr;		//结构体变量定义
	s_addr.sin_family = AF_INET;	//配置网络协议族,TCP协议
	inet_aton("127.0.0.1", &s_addr.sin_addr );	//配置结构体成员IP地址
	s_addr.sin_port = htons(8888);				//配置结构体成员端口号
	bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));	//绑定IP+端口号

3. listen 监听客户端

函数原型

	#include <sys/types.h>          /* See NOTES */
	#include <sys/socket.h>
	
	int listen(int sockfd, int backlog);

参数分析

参数类型/值
sockfd服务端网络标识符
backlog允许的最大客户单请求个数

功能

  • 设置能处理的最大连接数,listen()并未开始接受连线,只是设置 socket 的 listen 模式,listen 函数只用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数。
  • 内核为任何一个给定监听套接字维护两个队列:
    • 未完成连接队列,每个这样的 SYN 报文段对应其中一项:已由某个客户端发出并到达服务器,而服务器正在等待完成相应的 TCP 三次握手过程。这些套接字处于SYN REVD 状态
    • 已完成连接队列,每个已完成 TCP 三次握手过程的客户端对应其中一项。这些套接字处于ESTABLISHED 状态

4. accept 接收客户端

函数原型

	#include <sys/types.h>          /* See NOTES */
	#include <sys/socket.h>
	
	int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

返回值: 成功返回接入的客户端网络标识符,失败返回-1
参数分析

参数类型/值
sockfd服务端网络标识符
struct sockaddr *addrstruct sockaddr 结构体类型地址,用来返回已连接的对端(客户端)的协议地址
socklen_t *addrlen客户端地址长度,但为地址,需先取长度,后取地址

例:

	addrlen = sizeof(struct sockaddr_in);
	c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &addrlen);

5. read / write 数据传输

	#include <unistd.h>
	ssize_t read(int c_fd, void *buf, size_t count);
	ssize_t write(int c_fd, const void *buf, size_t count);  
  • 函数使用方法同Linux文件编程用法,参数主要为:网络标识符、读写Buf以及读写字节数
  • 详情请参考博文:Linux系统编程—文件API编程

客户端

1. socket 创建套接字

函数原型

	#include <sys/types.h>          
	#include <sys/socket.h>
	
	int socket(int domain, int type, int protocol);

返回值: 成功返回网络标识符,失败返回 -1
参数分析

  • domain: 指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族):
  • type: 指定socket类型,SOCK_STREAM 流式套接字提供可靠的、面向连接的通信流,它使用 TCP 协议,
  • protocol: 通常赋值 0 ,选择 type 类型对应的默认协议

2. connect 连接服务

  • 功能:该函数用于绑定之后的client 端(客户端),与服务器建立连接

函数原型

	#include <sys/types.h>          /* See NOTES */
	#include <sys/socket.h>
	
	int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
	//成功返回0,失败返回-1

参数分析

参数类型/值
sockfd服务端网络标识符
struct sockaddr *addr服务器端的 IP 地址和端口号的地址结构指针
socklen_t addrlen地址长度常被设置为 sizeof(struct sockaddr_in)

结构体实际配置同服务端,案例如下

    struct sockaddr_in c_addr;		//定义结构体变量
    c_addr.sin_family = AF_INET;	//配置成员网络协议族
    inet_aton("127.0.0.1", &c_addr.sin_addr );	//配置结构体成员IP地址
    c_addr.sin_port = htons(8888);				//配置结构体成员端口号
    connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in));	//绑定IP+端口号

3. read / write 数据传输

	#include <unistd.h>
	ssize_t read(int c_fd, void *buf, size_t count);
	ssize_t write(int c_fd, const void *buf, size_t count);  
  • 函数使用方法同Linux文件编程用法,参数主要为:网络标识符、读写Buf以及读写字节数

server.c与client.c程序案例

server.c

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
        int s_fd, c_fd;		//定义网络标识符
        int nread;			//读取字节数
        char readBuf[128]={0};	//读取缓存
        char msg[]="Return from server: I got your message!";	//发送缓存

		//定义网络结构体并初始化
        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;
        memset(&s_addr, 0, sizeof(struct sockaddr_in));
        memset(&c_addr, 0, sizeof(struct sockaddr_in));

        //1. socket 创建套接字
        s_fd  = socket(AF_INET, SOCK_STREAM, 0);	//创建套接字
        if(s_fd == -1){
                printf("socket error!\n");
                exit(-1);
        }

        //2.bind   绑定IP+端口号
        s_addr.sin_family = AF_INET;				//配置网络协议
        inet_aton("127.0.0.1", &s_addr.sin_addr );	//配置IP地址
        s_addr.sin_port = htons(8888);				//配置端口号
        bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));	//绑定IP+端口号

        //3.linsten  监听客户端接入
        listen(s_fd, 10);
        printf("listing......\n");
        //4.accept  接收客户端接入
        int addrlen = sizeof(struct sockaddr_in);
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &addrlen);
        if(c_fd == -1){
                printf("c_fd error!\n");
                exit(-1);
        }
        else
                printf("connect client: %s\n",inet_ntoa(c_addr.sin_addr));

        //5.read 
        memset(readBuf, '\0', 128);
        nread = read(c_fd, readBuf, 128);	//从客户端读取128字节数据到readBuf缓存
        if(nread == -1)
        {
                printf("nread error\n");
                exit(-1);
        }
        printf("Receive: %d Byte context:%s\n",nread, readBuf);

        //6.write
        write(c_fd, msg, strlen(msg));	//应答客户端
        memset(msg,'\0',128);

        return 0;
}

client.c

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
        int c_fd;
        int nread;
        int addrlen;
        char readBuf[128]={0};
        char msg[]="hello world";
        struct sockaddr_in c_addr;
        memset(&c_addr, 0, sizeof(struct sockaddr_in));

        //1. socket
        c_fd  = socket(AF_INET, SOCK_STREAM, 0);
        if(c_fd == -1){
                printf("socket error!\n");
                exit(-1);
        }

        //2. connect 与服务器建立连接
        c_addr.sin_family = AF_INET;
        inet_aton("127.0.0.1", &c_addr.sin_addr );
        c_addr.sin_port = htons(8888);
        if( connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1)
        {
                printf("connect error\n");
                exit(-1);
        }

        //3.write
        write(c_fd, msg, strlen(msg));
        memset(msg, '\0', 128);

        //4.read
        memset(readBuf,'\0',128);
        nread = read(c_fd, readBuf, 128);
        if(nread == -1){
                printf("nread error\n");
                exit(-1);
        }
        printf("read from server: %d Byte  context:\n%s\n",nread, readBuf);
        
        return 0;
}

试问:如何实现双方自由聊天

  • 服务器通过创建子进程用于和每个不同的客户端进行数据交互,父进程负责接收后续接入的客户端
  • 子进程内部再次创建子进程用来发送数据,父进程则实时接收来自服务器的数据

server.c 核心代码

//4.accept
while(1)
{
		//父进程用于接收其他接入的客户端
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &addrlen);
        if(c_fd == -1){
                printf("c_fd error!\n");
                exit(-1);
        }
        else
                printf("connect client: %s\n",inet_ntoa(c_addr.sin_addr));
        cnt++;

        if(fork() == 0)			//创建进程,子进程用于和客户端进行数据交互
        {

                if(fork() == 0)		//子进程用于向客户端发送数据
                {
                        //write
                        while(1)
                        {
                                printf("Inputs: ");
                                scanf("%s",msg);
                                write(c_fd, msg, strlen(msg));
                                memset(msg,'\0',128);
                        }
                }
                else		//父进程用于接收来自客户端发送的数据
                {
                        //read
                        while(1)
                        {
                                memset(readBuf, '\0', 128);
                                nread = read(c_fd, readBuf, 128);
                                if(nread == -1)
                                {
                                        printf("nread error\n");
                                        exit(-1);
                                }
                                printf("Receive: %d Byte context:%s\n",nread, readBuf);
                        }
                }
        }
}

client.c 核心代码

while(1)
{
        if(fork() == 0)			//子进程用于向服务器发送数据
        {
                while(1)        //write
                {
                        printf("Input: ");
                        scanf("%s",msg);
                        //gets(msg);
                        write(c_fd, msg, strlen(msg));
                        memset(msg, '\0', 128);
                }
        }
        else					
        {						//父进程用于接收服务器发送的数据
                while(1)    	//read    
                {
                		
                        memset(readBuf,'\0',128);
                        nread = read(c_fd, readBuf, 128);
                        if(nread == -1){
                                printf("nread error\n");
                                exit(-1);
                        }
                        printf("read from server: %d Byte  context:\n",nread);
                        printf("%s\n",readBuf);
                }
}  

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

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

相关文章

合宙Air724UG LuatOS-Air LVGL API控件--下拉框 (Dropdown)

下拉框 (Dropdown) 在显示选项过多时&#xff0c;可以通过下拉框收起多余选项。只为用户展示列表中的一项。 示例代码 -- 回调函数 event_handler function(obj, event)if (event lvgl.EVENT_VALUE_CHANGED) thenprint("Option:", lvgl.dropdown_get_symbol(obj)…

稀疏数组的实现

文章目录 目录 文章目录 前言 一 什么是稀疏数组? 二 稀疏数组怎么存储数据? 三 稀疏数组的实现 总结 前言 大家好,好久不见了,这篇博客是数据结构的第一篇文章,望大家多多支持! 一 什么是稀疏数组? 稀疏数组&#xff08;Sparse Array&#xff09;是一种数据结构&a…

Elastic-job分布式调度系统

一、定时任务实现方式 1、Thread方式 final int timeInterval 1000;Thread thread new Thread(new Runnable() {Overridepublic void run() {while (true){try {//每一秒执行一次Thread.sleep(timeInterval);System.out.println("run...");} catch (InterruptedE…

数据结构和算法(1):开始

算法概述 所谓算法&#xff0c;即特定计算模型下&#xff0c;旨在解决特定问题的指令序列 输入 待处理的信息&#xff08;问题&#xff09; 输出 经处理的信息&#xff08;答案&#xff09; 正确性 的确可以解决指定的问题 确定性 任一算法都可以描述为一个由基本操作组成的序…

SpringBoot核心原理与实践

第一章、SpringBoot简介 1、入门案例 2、官网创建压缩包程序 注意使用的版本pom文件中java --> 1.8、 springboot --> 2.5.0 3、SpringBoot快速启动 运行程序--找引导类 换技术、加技术--加starter 第二章、基础配置 1、配置文件格式 《1、端口号配置》 《2、将目录文…

React原理 - React Hooks

目录 扩展学习资料 React Hooks 编写函数组件 Hooks使命 Hooks解决了什么问题 Hooks原理 useState源码解析 mountState源码解析 Hooks应用 Hooks 实践 倒计时组件 练习 扩展学习资料 名称 链接 React Hooks 官方文档 Introducing Hooks – React useEffect 完整…

PYTHON知识点学习-列表和元组

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由 Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

把一般数据转换成因子数据格式,做单因子、债券对历史数据回测+获取curl命令+垃圾数据转换成标准行情数据(bardata)

下载curl软件&#xff0c;地址&#xff1a; curl for Windows for 64-bit下载好后解压到文件夹&#xff0c;将里面的bin文件添加到环境变量中&#xff0c;bon文件地址为&#xff1a;C:\Users\59980\curl-8.2.1_7-win64-mingw\bin 打开cmd&#xff0c;输入curl --help,出现下…

软考:中级软件设计师:程序语言基础:表达式,标准分类,法律法规,程序语言特点,函数传值传址

软考&#xff1a;中级软件设计师:程序语言基础&#xff1a;表达式 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都…

ssm民宿管理系统源码和论文

ssm民宿管理系统源码和论文110 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&…

SSM整合~

构建并配置项目&#xff1a; 第一步&#xff1a;创建maven项目 第二步&#xff1a;配置pom.xml文件 设置打包方式&#xff1a; <packaging>war</packaging>设置版本号为自定义属性&#xff1a; <properties><!--将版本号通过自定义属性配置--><…

跨站请求伪造(CSRF)攻击与防御原理

跨站请求伪造&#xff08;CSRF&#xff09; 1.1 CSRF原理 1.1.1 基本概念 跨站请求伪造&#xff08;Cross Site Request Forgery&#xff0c;CSRF&#xff09;是一种攻击&#xff0c;它强制浏览器客户端用户在当前对其进行身份验证后的Web 应用程序上执行非本意操作的攻击&a…

差异化竞争阵地的所在【周技术进阶】-从BS 项目C#最基础截取字符串方法开始

效果 代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleAppNumberOneHelloWorld {class Program{static void Main(string[] args){Console.WriteLine("hello world&#xf…

TCP机制之确认应答及超时重传

TCP因为其可靠传输的特性被广泛使用,这篇博客将详细介绍一下TCP协议是如何保证它的可靠性的呢?这得主要依赖于其确认应答及超时重传机制,同时三次握手四次挥手也起到了少部分不作用,但是主要还是由确认应答和超时重传来决定的;注意:这里的可靠传输并不是说100%能把数据发送给接…

JVM学习(五)--方法区

概念&#xff1a; 方法区就是存和类相关的东西&#xff0c;成员方法&#xff0c;方法参数&#xff0c;成员变量&#xff0c;构造方法&#xff0c;类加载器等&#xff0c;逻辑上存在于堆中&#xff0c;但是不同的虚拟机对它的实现不同&#xff0c;oracle的hotsport vm在1.6的时…

事务(SQL)

事务概述 事务是一组操作的集合&#xff0c;他是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向西永提交或撤销操作请求。这组操作&#xff0c;要么全部执行成功&#xff0c;要么全部执行失败。 事务操作 查看/设置事务提交方式 -- 查看/设置事务…

9.1.tensorRT高级(4)封装系列-自动驾驶案例项目self-driving-道路分割分析

目录 前言1. 道路分割总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 高级-自动驾驶案例项目self-driving-道路分…

Linux入门之多线程|线程|进程基本概念及库函数

目录 一、线程 1.线程的概 补充知识点&#xff1a;页表 2.线程的优点 3.线程的缺点 4.线程异常 5.线程用途 二、线程与进程的区别与联系 三、关于进程线程的问题 0.posix线程库 1.创建线程 2.线程终止 3.取消线程 4.线程等待&#xff08;等待线程结束&#xff09;…

02|李沐动手学深度学习v2(笔记)

基础优化算法 导航 基础优化算法梯度下降1.1 小批量随机梯度下降1.2 小结 线性回归实现1. 处理数据1.3 生成大小为batch_size的小批量 2. 处理模型3. 模型评估4. 训练过程 梯度下降 针对我们的模型没有显示解。&#xff08;生活中很少能有完全符合的线性模型&#xff0c;大多数…

用户中心笔记-leovany

1. 安装 官方地址&#xff1a;https://pro.ant.design/zh-CN/docs/getting-started 1.1 Mac系统 1.1.1 安装yarn 安装yarn brew install yarn查看版本 brew -v 1.1.2 安装node // 安装node brew install node // 关联 brew unlink node && brew link node // 查看版…