Linux C语言socket网络编程

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

TCP套 socket 接口编程:

基于TCP的 客户/服务器(C/S)模式的工作过程如下:

image

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

socket():

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

#include

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

bind():

// 函数原型:

#include

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 函数将完成这些工作。

*/

listen():

// 函数原型:

#include

#include

// #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);

}

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

accept():

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

// 函数原型:

#include

#include

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 函数的第三个参数是不一样的。

close():

// 函数原型:

#include

int close(int sockfd);

// 成功则返回 0,否则返回-1。

// 功能:关闭套接口 其中参数 sockfd 是关闭的套接口描述字。

// 当对一个套接口调用 close()时, 关闭该套接口描述字,并停止连接。

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

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

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 地址

connect():

connect(sockfd,(struct sockaddr *)(&server_addr), sizeof(structsockaddr));

函数原型:

#include

#include

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;

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

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

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

先说前两个:

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

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

然后是后两个:

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

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。

*/

拓展补充 ... 推荐博客地址:

socket 网络编程 - **CSDN **博主:奔跑中的小兔子

linux c socket 完全端口,浅谈 Linux C语言 socket 网络编程相关推荐

  1. Linux先发送条件变量,浅谈Linux条件变量的使用

    Linux线程同步之间存在多种机制,条件变量是一种类似操作系统里提到的生产者-消费者算法的同步机制,允许线程以无竞争的方式等待特定条件的发生. 示例伪代码: void* Thread1(void){ ...

  2. linux的i o模型,浅谈Linux 网络 I/O 模型简介(图文)

    1.介绍 Linux 的内核将所有外部设备都看做一个文件来操作(一切皆文件),对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符).而对一个sock ...

  3. linux中管道的概念,浅谈Linux管道

    管道(pipe)是一个我们在学习Linux命令行的时候就会引入的一个很重要的概念.管道是UNIX环境中历史最悠久的进程间通信方式,从本质上说,管道也是一种文件,也是遵循UNIX的"一切皆文件 ...

  4. linux中initrd的含义,浅谈linux启动的那些事(initrd.img)

    本文记录下linux系统的启动过程,分析下每一步都作了什么.linux 1.linux系统的启动过程小程序 1.加载BIOS,上电自检.由于BIOS中包含了CPU的相关信息.设备启动顺序信息.硬盘信息 ...

  5. linux下反删除软件,浅谈Linux文件系统反删除方法(二)

    列出的档有很多(这里找到2692个),第一字段是档节点号,第二字段是档所有者,第三字段是读写权限,接下来是档大小,占用块数,删除时间.然后就可以根据档大小和删除日期判断那些是我们需要的.比如我们要恢复 ...

  6. 浅谈 Linux 系统中的 SNMP Trap 【转】

    文章来源:浅谈 Linux 系统中的 SNMP Trap 简介 本文讲解 SNMP Trap,在介绍 Trap 概念之前,首先认识一下 SNMP 吧. 简单网络管理协议(Simple Network ...

  7. 浅谈Linux中ldconfig和ldd的用法

    ldd 查看程序依赖库 ldd 作用:用来查看程式运行所需的共享库,常用来解决程式因缺少某个库文件而不能运行的一些问题. 示例:查看test程序运行所依赖的库: /opt/app/todeav1/te ...

  8. linux中sh+$0,浅谈linux中shell变量$#,$@,$0,$1,$2的含义解释

    摘抄自:ABS_GUIDE 下载地址:http://www.tldp.org/LDP/abs/abs-guide.pdf linux中shell变量$#,$@,$0,$1,$2的含义解释: 变量说明: ...

  9. linux 易语言窗口程序_浅谈Linux入门的基本知识

    浅谈Linux入门的基本知识 图形模式与文字模式的切换方式Linux预设提供了六个命令窗口终端机让我们来登录. 默认我们登录的就是第一个窗口,也就是tty1,这个六个窗口分别为tty1.tty2 - ...

  10. shell for循环1到100_浅谈Linux下shell 编程的for循环常用的6种结构

    浅谈Linux下shell 编程的for循环常用的6种结构 1. 常用for循环结构 (1) for 变量 in 值1 值2 值3... do 程序块儿 done (2) for 变量 in `命令` ...

最新文章

  1. eclipse中导入web项目详细配置
  2. 【NOIP校内模拟】T2 华莱士(环套树)
  3. HDU 3646 DP + 二分
  4. 腾讯的迷你门户首页新闻用到的Silverlight技术引用
  5. mongodb数据库导出备份
  6. 【Python】对象、类、元类
  7. [深度学习-优化]梯度消失与梯度爆炸的原因以及解决方案
  8. (转)RabbitMQ学习之spring整合发送同步消息
  9. 安全是什么意思_进衡水火车站要转着圈找门!这是什么意思……清扫车路边倒水 既浪费又不安全...
  10. 线上风控与数据埋点三部曲(一)——流量江湖不是处女地,投放渠道需谨慎
  11. Leetcode每日一题:134.gas-station(加油站)
  12. hihocoder第196周
  13. Flask: wsgi接口
  14. 单词吸血鬼源代码 二叉树操作
  15. CC2530基础实验二:电源管理与睡眠定时器
  16. C语言程序中数字字符是什么,C语言中如何识别字符与数字
  17. python代码范文_如何给你的Python代码“减负”,这里有一份指南请查收
  18. Moore-Penrose 广义逆/伪逆 (The Moore-Penrose Pseudoinverse)
  19. hrbust 1699 矩阵游戏【枚举找规律】
  20. Win10下次使用debug进入DOS进行汇编开发

热门文章

  1. bzoj_3529 数表
  2. 性能分析:处理器、磁盘I/O、进程、网络分析方法 http://www.cnblogs.com/fnng/archive/2012/10/30/2747246.html...
  3. c++ 接口继承和实现继承
  4. Requirement-Driven Linux Shell Programming
  5. dubbo源码 -- 服务导出
  6. Spring整合MyBatis原理之Mapper接口代理对象的产生以及调用 (三)
  7. 在浏览器的地址栏输入网址的背后
  8. React.js和Vue.js有感—前端开发组件化思想的局部要点理解
  9. FreeDOS 24 周年,创始人分享常用 DOS 命令备忘表
  10. 数据库优化-水平拆分 垂直拆分