百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Linux C语言socket网络编程

off999 2025-03-12 19:30 16 浏览 0 评论

Linux C语言socket网络编程


需要Linux C 服务器开发视频学习资料的朋友请后台私信【架构】获取

注意:本文是按照 TCP、UDP的工作过程进行总结的

  1. TCP套 socket 接口编程: 基于TCP的 客户/服务器(C/S)模式的工作过程如下:

服务器进程中的一些函数:


  1. socket():

/* 函数所需头文件及其原型 */

#include <sys/socket.h>

int socket( int family, int type, int protocol);

socketfd = soket(AF_INET, SOCK_STREAM, 0);

/* socketfd 作为返回值,可以记作描述符。

若 socketfd 非负则表示成功,为负则表示失败。

参数:

family -> 指明协议族

type -> 字节流类型

protocol -> 一般置0.

参数 family 的取值范围是:

AF_LOCAL UNIX 协议族

AF_ROUTE 路由器接口

AF_INET IPv4 协议

AF_INET6 IPv6 协议 AF_KEY 密钥套接口

参数 type 的取值范围:

SOCK_STREAM TCP 套接口

SOCK_DGRAM UDP 套接口

SOCK_PACKET 支持数据链路访问

SOCK_RAM 原始套接口

*/

生成套接口描述字(套接字)后,要为套接口的地址数据结构进行赋初值。

通用套接口地址的数据结构中,struct sockaddr_in 需要掌握:

struct in_addr {

in_addr_t s_addr;

/*32 位 IP 地址,网络字节序*/

};

struct sockaddr_in {

uint8 sin_len;

sa_family_t sin_family;

in_port_t sin_port;

/*16 位端口号,网络字节序*/

struct in_addr sin_addr;

char sin_zero[8];

/*备用的域,未使用*/

};

PS:需要注意的是,一般在 socket() 之后,我们会填写 sockaddr 的相关内容。

/* Fill the local socket address struct */

memset (&servaddr,0,sizeof(servaddr));

servaddr.sin_family = AF_INET; // Protocol Family

servaddr.sin_port = htons (PORT); // Port number

servaddr.sin_addr.s_addr = htonl (INADDR_ANY); // AutoFill local address

  1. bind():
 // 函数原型:
 #include <sys/socket.h>  
 int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
 /*
     参数 sockfd :套接字描述符。 
     参数 my_addr:指向 sockaddr 结构体的指针(该结构体中保存有端口和 IP 地址 信息)。 
     参数 addlen:结构体 sockaddr 的长度。
        
        返回:0──成功, -1──失败 
 */
 ret = bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)); 

 /*  功能:当调用 socket 函数创建套接字后,该套接字并没有与 本机地址和端口等 信息相连,
     bind 函数将完成这些工作。
 */
  1. listen():
 //  函数原型:
 #include <sys/socket.h>  
 #include<sys/types.h>
 //  #define BACKLOG 10
 int listen(int sockfd,int backlog);

 /*
     参数 sockfd :套接字描述符。
     参数 backlog :规定内核为此套接口排队的最大选择个数。 
 */
 ret = listen(sockfd,BACKLOG);
 
 // 通常采用一下的异常处理:
 if(listen(listenfd,BACKLOG) == -1){  
     printf("ERROR: Failed to listen Port %d.\n", PORT);
     return (0);
 }
 else{
     printf("OK: Listening the Port %d sucessfully.\n", PORT);
 }

处在监听模式下后,程序就需要一个循环来实现挂起等待客户机请求。所以接下来的一步就是 接受客户机的请求。

  1. accept():

先来了解一下 accept() 这个函数:

//   函数原型:
#include <sys/socket.h>  
#include<sys/types.h> 
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);

/*
 sockfd  参数:监听的  套接字描述符。 
 cliaddr 参数:指向结构体 sockaddr 的指针。  
 addrlen 参数:cliaddr 参数指向的内存空间的长度。 
*/

sin_size = sizeof(struct sockaddr_in);  
connect_fd = accept(sockfd,( struct sockaddr *)&their_addr,&sin_size); 

accept() 函数用于面向连接类型的套接字类型。 accept() 函数将从连接请求队列中获得连接信息,创建新的套 接字,并返回该套接字的文件描述符。 新创建的套接字用于服务器与客户机的通信,而原来的套接字仍然处于监听状态。 它们的区别在于:监听套接口描述字 只有一个,而且一直存在, 每一个连接都有一个已连接套接口描述字,当连接断开 时就关闭该描述字。

注意:bind 函数和 accept 函数的第三个参数是不一样的。

  1. close():
//   函数原型:
#include   
int close(int sockfd); 
//   成功则返回 0,否则返回-1。 
//   功能:关闭套接口 其中参数 sockfd 是关闭的套接口描述字。 
//   当对一个套接口调用 close()时, 关闭该套接口描述字,并停止连接。

    以后这个套接口不能再使用,也不能再执行 任何读写操作,但关闭时已经排队准备发送的数据仍会被发出 使用完一个套接口后,一定要记得将它关掉,任何一个文件读写操作完毕之后, 都要关闭它的描述字。 

客户机进程中的一些函数:


  1. socket(): 这个函数前面提过,这里不必多说。 创建套接字后,同理,也需要对套接口进行设置: (这是在客户端填充的服务器 端的资料)...... bzero(&server_addr,sizeof(server_addr)); // 初始化,置 0 server_addr.sin_family=AF_INET; // IPV4 server_addr.sin_port=htons(portnumber); // (将本机器上的 short 数据转化为网络上的 short 数据)端口号,与服务器端 的端口号相同 server_addr.sin_addr=*((struct in_addr *)host->h_addr_list); // IP 地址
  2. connect():
 connect(sockfd,(struct sockaddr *)(&server_addr), sizeof(structsockaddr)); 
    函数原型:
    #include <sys/types.h>  
    #include <sys/socket.h>  
    int connect(int sockfd,const struct sockaddr *serv_addr,int addrlen); 

 /*
     返回值:成功:返回 0 错误:返回-1,并将全局变量 errno 设置为相应的错误号。 
     参数 sockfd :数据发送的套接字,解决从哪里发送的问题,ockfd 是先前 socket 返回的值 
     参数 serv_addr:据发送的目的地,也就是服务器端的地址 
     参数 addrlen:指定 server_addr 结构体的长度 
 */

 函数功能:
 创建了一个套接口之后,使客户端和服务器连接。其实就是完成一个 有连接协议 的连接过程,
 对于 TCP 来说就是那个三段握手过程。

关于三段握手:( 《计算机网络》谢希仁编著 第七版中 将其定名为:" 三报文握手 "):

客户端先用 connect() 向服务器发出一个要求连接的信号 SYN1;

服务器 进程接收到这个信号后,发回应答信号 ack1,同时这也是一个要求回答的信号 SYN2;

客户端收到信号 ack1SYN2 后,再次应答 ack2; 服务器收到应答信号 ack2,一次连接才算建立完成。

从上面过程可以看出,服务器会收到两次信 号 SYN1ack2,因此服务器进程需要两个队列保存不同状态的连接。刚接收 到 SYN1 信号时,连接还未完成,这时的连接放在一个名为“未完成连接”的队列中。接收到 ack2 信号后,三段握手完成,这时的连接放在名为“已完成连接” 的队列中,等待 accept() 调用。

关于 recv() 、send() 和 recvfrom() 、sendto() :


  1. 先说前两个:

recv()send() 都是基于 TCP 协议。

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。

客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

同样,不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。

// 函数原型:

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

int recv( SOCKET s, char *buf, int len, int flags );

(1)recv 先等待 s 的发送缓冲中的数据被协议传送完毕,如果协议在传送 s 的发送缓冲中的数据时出现网络错误,那么recv函数返回 SOCKET_ERROR

(2)如果 s 的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv 先检查套接字 s 的接收缓冲区,如果 s 接收缓冲区中没有数据或者协议正在接收数据,那么 recv 就一直等待,直到协议把数据接收完毕。

当协议把数据接收完毕,recv 函数就把 s 的接收缓冲中的数据 copybuf 中(注意协议接收到的数据可能大于 buf 的长度,所以在这种情况下要调用几次 recv 函数才能把s的接收缓冲中的数据 copy 完。recv 函数仅仅是 copy 数据,真正的接收数据是协议来完成的);

其中,recv 函数返回其实际 copy 的字节数。如果 recvcopy 时出错,那么它返回 SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回 0。

  1. 然后是后两个:

recvfrom()sendto() 都是基于 UDP 协议。

不同于 TCP 协议,UDP 提供的是一种无连接的、不可靠的数据包协议。它不对数据进行确认、出错重传、排序等可靠性处理,但是它却具有代码小、速度快和系统开销小等优点。对于某些应用程序,使用 UDP 来实现,将带来更大效率。

与基于 TCP 协议的客户机/服务器模式的工作流程图相比较,它们的主要区别 在于:

使用 TCP 套接口必须先建立连接(例如客户进程的 connect() ,服务器进程 的 **listen() **和 accept() ) 。

而 UDP 套接口不需预先连接,它在调用 socket()生成一个套接口后,

-> 在服务器端调用 bind() 绑定众所周知的端口后, 服务器阻塞于 recvfrom() 调用,

-> 客户端调用 sendto() 发送数据请求,阻塞于 recvfrom() 调用,

-> 服务器端调用 recvfrom() 接收数据,服务器端也调用 sendto() 向客户发送数据作为应答,然后阻塞于 recvfrom() 调用,

-> 客户端 调用 recvfrom() 接收数据......

当数据传输完成以后,UDP 套接口中的客户端调用 close() 断开连接,而 TCP 套接口中的客户端不必再发出“断开连接信号”来通知服务器端关闭连接。

一些重要的应用程序,如域名服务系统 DNS、网络文件 系统 NFS 都使用 UDP 套接口。

// 函数原型:

#include <sys/socket.h>

int recvfrom(int sockfd, void *buff, int len,int flags, struct sockaddr *fromaddr, int *addrlen);

/*

参数 sockfd 为套接口描述字;

参数 buff 为指向读缓冲的指针;

参数 len 为读的字节数;

参数 flags 一般设置为 0;

参数 fromaddr 为指向数据接收的套接口地址结构的指针;

参数 addrlen 为套接口结构长度。

函数返回实际读的字节数,可以为 0,如果出错,则返回-1。

*/

int sendto(int sockfd, void *mes,int len, int flags, struct sockaddr *toaddr, int *addrlen);

/*

参数 mes 为指向写缓冲的指针;

参数 toaddr 为指向数据发送的套接口地址结构的指针;

函数返回实际写的字节数,可以为 0,如果出错,则返回-1。

*/

当真正开始学习的时候难免不知道从哪入手,学习时频繁踩坑,导致效率低下影响继续学习的信心,最终浪费大量时间。为了让学习变得轻松、高效!今天给大家免费分享一套教学资源,帮助大家在成为架构师的道路上披荆斩棘。查看我的主页即可~

分享主要有C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK技术,面试技巧方面的资料技术讨论。

相关推荐

Python钩子函数实现事件驱动系统(created钩子函数)

钩子函数(HookFunction)是现代软件开发中一个重要的设计模式,它允许开发者在特定事件发生时自动执行预定义的代码。在Python生态系统中,钩子函数广泛应用于框架开发、插件系统、事件处理和中...

Python函数(python函数题库及答案)

定义和基本内容def函数名(传入参数):函数体return返回值注意:参数、返回值如果不需要,可以省略。函数必须先定义后使用。参数之间使用逗号进行分割,传入的时候,按照顺序传入...

Python技能:Pathlib面向对象操作路径,比os.path更现代!

在Python编程中,文件和目录的操作是日常中不可或缺的一部分。虽然,这么久以来,钢铁老豆也还是习惯性地使用os、shutil模块的函数式API,这两个模块虽然功能强大,但在某些情况下还是显得笨重,不...

使用Python实现智能物流系统优化与路径规划

阅读文章前辛苦您点下“关注”,方便讨论和分享,为了回馈您的支持,我将每日更新优质内容。在现代物流系统中,优化运输路径和提高配送效率是至关重要的。本文将介绍如何使用Python实现智能物流系统的优化与路...

Python if 语句的系统化学习路径(python里的if语句案例)

以下是针对Pythonif语句的系统化学习路径,从零基础到灵活应用分为4个阶段,包含具体练习项目和避坑指南:一、基础认知阶段(1-2天)目标:理解条件判断的逻辑本质核心语法结构if条件:...

[Python] FastAPI基础:Path路径参数用法解析与实例

查询query参数(上一篇)路径path参数(本篇)请求体body参数(下一篇)请求头header参数本篇项目目录结构:1.路径参数路径参数是URL地址的一部分,是必填的。路径参...

Python小案例55- os模块执行文件路径

在Python中,我们可以使用os模块来执行文件路径操作。os模块提供了许多函数,用于处理文件和目录路径。获取当前工作目录(CurrentWorkingDirectory,CWD):使用os....

python:os.path - 常用路径操作模块

应该是所有程序都需要用到的路径操作,不废话,直接开始以下是常用总结,当你想做路径相关时,首先应该想到的是这个模块,并知道这个模块有哪些主要功能,获取、分割、拼接、判断、获取文件属性。1、路径获取2、路...

原来如此:Python居然有6种模块路径搜索方式

点赞、收藏、加关注,下次找我不迷路当我们使用import语句导入模块时,Python是怎么找到这些模块的呢?今天我就带大家深入了解Python的6种模块路径搜索方式。一、Python模块...

每天10分钟,python进阶(25)(python进阶视频)

首先明确学习目标,今天的目标是继续python中实例开发项目--飞机大战今天任务进行面向对象版的飞机大战开发--游戏代码整编目标:完善整串代码,提供完整游戏代码历时25天,首先要看成品,坚持才有收获i...

python 打地鼠小游戏(打地鼠python程序设计说明)

给大家分享一段AI自动生成的代码(在这个游戏中,玩家需要在有限时间内打中尽可能多的出现在地图上的地鼠),由于我现在用的这个电脑没有安装sublime或pycharm等工具,所以还没有测试,有兴趣的朋友...

python线程之十:线程 threading 最终总结

小伙伴们,到今天threading模块彻底讲完。现在全面总结threading模块1、threading模块有自己的方法详细点击【threading模块的方法】threading模块:较低级...

Python信号处理实战:使用signal模块响应系统事件

信号是操作系统用来通知进程发生了某个事件的一种异步通信方式。在Python中,标准库的signal模块提供了处理这些系统信号的机制。信号通常由外部事件触发,例如用户按下Ctrl+C、子进程终止或系统资...

Python多线程:让程序 “多线作战” 的秘密武器

一、什么是多线程?在日常生活中,我们可以一边听音乐一边浏览新闻,这就是“多任务处理”。在Python编程里,多线程同样允许程序同时执行多个任务,从而提升程序的执行效率和响应速度。不过,Python...

用python写游戏之200行代码写个数字华容道

今天来分析一个益智游戏,数字华容道。当初对这个游戏颇有印象还是在最强大脑节目上面,何猷君以几十秒就完成了这个游戏。前几天写2048的时候,又想起了这个游戏,想着来研究一下。游戏玩法用尽量少的步数,尽量...

取消回复欢迎 发表评论: