Socket概念

Socket本意为“插座”,在Linux下,用于表示进程间网络通信的特殊文件类型,本质为内核借助缓冲区形成的伪文件
既然是文件,那肯定就可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。对于管道缓冲区,读端和写端的文件描述符分别指向缓冲区的两端,该缓冲区的工作是单向半双工的,从一端写,另一端读。而套接字缓冲区,一个文件描述符指向两个缓冲区,两端都可以读和写,这样才能实现双向全双工通信方式。
套接字的通信原理简单示意图如下,左端通过文件描述符将数据写入发送端缓冲区,右端从接受端缓冲区接受数据,也可以是左端读数据,右端写数据。左右的缓冲区之间就是通过套接字连接。可以看出,socket在通信过程中一定是成对出现(接受端socket和发送端socket)。

IP地址:在网络环境中唯一标识一台主机
端口号:在主机中唯一标识一个进程
IP+端口号:在网络环境中唯一标识一个进程,对应一个socket,欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接,因此就可以用socket来描述网络连接的一对一关系。

网络字节序

内存中的多字节数据相对于内存地址有大端和小端之分,网络数据流同样有大小端之分,网络数据流的地址这样规定:先发出的数据是低地址,后发出的数据是高地址。
大端存储:即数据的高字节存储在低地址处,低字节存储在高字节处
小段存储:即数据的低字节存储在底地址处,高字节存储在高地址处
如何测试电脑的大小端存储
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。
TCP/IP协议规定,网络数据流(网络字节顺序)采用大端字节序,即低地址存高字节。那如果接受端主机或者发送端主机采用的是小端字节序,就要做相应的网络字节序和主机字节序之间的转换。需要使用的函数为:htonl、htons、ntohl、ntohs。

IP地址转换

一般我们习惯用点分十进制表示IP,但是数据在网络中传输就要将其转换为TCP/IP中规定好的一种数据表示格式(网络字节顺序),这时就用到inet_pton函数,相反,网络字节序转换成用点分十进制表示的IP,用inet_ntop函数。

sockaddr_in数据结构

命令 man 7 ip可以查看到sockaddr_in的数据结构

struct sockaddr_in {sa_family_t    sin_family; /* address family: AF_INET */in_port_t      sin_port;   /* port in network byte order */struct in_addr sin_addr;   /* internet address */
};
/* Internet address. */
struct in_addr {uint32_t       s_addr;     /* address in network byte order */
};

sin_family:协议类型,IPV4还是IPV6
sin_port:端口号
sin_addr:IP地址

Socket函数

(1)socket函数:创建套接字
#include <sys/socket.h>
int socket(int af, int type, int protocol);
成功返回指向该套接字的文件描述符,失败返回-1
af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字,如TCP) 和 SOCK_DGRAM(数据报套接字/无连接的套接字,如UDP)。
protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
(2)bind函数:绑定IP和端口号(struct sockaddr_in addr 初始化)
#include<sys/socket.h>
#include<sys/type.h>
int bind(int sockfd, const struct sockaddr * my_addr, socklen_t addrlen);
成功返回0,失败返回-1
sockfd 表示已经建立的socket编号(描述符)。
my_addr 是一个指向sockaddr结构体类型的指针。
addrlen表示my_addr结构的长度,可以用sizeof操作符获得。
(3)listen函数:指定同时支持的最大连接数
#include <sys/socket.h>
int listen( int sockfd, int backlog);
成功返回0,失败返回-1
sockfd表示文件描述符
backlog表示排队建立3次握手队列和刚刚建立3次握手队列的连接数之和
(4)accept函数:接受连接请求
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
成功返回一个新的socket文件描述符,用来和客户端通信,失败返回-1
sockfd表示文件描述符。
addr为传出参数,返回链接客户端地址信息,含IP地址和端口号。
addrlen为传入传出参数,传入sezeof(addr)的大小,函数返回时返回真正接受到地址结构体的大小。
(5)connect函数:建立与指定socket的连接
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
成功返回0,失败返回-1
s表示socket文件描述符。
addr为传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen为传入参数,传入sezeof(addr)的大小

Socket模型创建流程图

对于客户端来说不需要调用bind函数,因为没有调用bind函数,操作系统会自动分配一个IP和端口号,但是服务器端不能使用随机分配的,比如,学生上课,教室必须指定固定的一间,否则学生无法找到,但是学生的地址是随机的。

Server端实现

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <ctype.h>
#include <arpa/inet.h>#define SERV_PORT 6666 //这里需要定义大点的端口号防止和系统已使用的冲突
int main(void){int lfd, cfd;struct sockaddr_in serv_addr, clie_addr;socklen_t clie_addr_len;char buf[BUFSIZ];int n, i;lfd = socket(AF_INET, SOCK_STREAM, 0);serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);//主机字节序转网络字节序serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY是数字类型的IPbind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));listen(lfd, 128);//128为默认的上限值clie_addr_len = sizeof(clie_addr);cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);while(1){n = read(cfd, buf, sizeof(buf));for (i = 0; i < n; i++){buf[i] = toupper(buf[i]);}write(cfd, buf, n);}close(lfd);close(cfd);return 0;
}

编译:gcc socket_server.c -Wall -g
执行:./a.out

用nc命令测试 nc 127.0.0.1 6666

Client端实现

#include <sys/socket.h>
#include <stdlib.h>
#include <ctype.h>
#include <arpa/inet.h>#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666 //这里需要定义大点的端口号防止和系统已使用的冲突
int main(void){int cfd;struct sockaddr_in serv_addr;socklen_t serv_addr_len;char buf[BUFSIZ];int n;cfd = socket(AF_INET, SOCK_STREAM, 0);memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);//主机字节序转网络字节序inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);connect(cfd, (struct socketaddr *)&serv_addr, sizeof(serv_addr));while(1){fgets(buf, sizeof(buf),stdin);write(cfd,buf,strlen(buf));n = read(cfd,buf,sizeof(buf));write(STDOUT_FILENO, buf, n);}return 0;
}

总结

在server端,socket函数只是创建了套接字,并没有完成两个进程间通信,而是accept函数完成这件事,它返回一个套接字sfd,是一个文件描述符索引,read读sfd指向的缓冲区中的数据,write往sfd指向的缓冲区中写数据。在client端,通过fgets从标准输入缓冲区中读数据,然后通过write写到cfd指向的缓冲区中,然后通过IP+Port就能找到服务器端,服务器端的read就能读取到数据。详细描述为以下10个步骤:

注:该博文只是为了理解socket原理,所以代码中没有加函数返回正确或失败判断,实际编程中需要加上

Socket(套接字)详解 画图+实例相关推荐

  1. socket(套接字)详解一种通讯机制

    socket给提供给使用进程TCP/UDP等网络协议进行网络通讯手段. linux中网络编程通过socket接口实现: socket既是一种特殊的IO,提供对应的文件描述符.socket都有一个相关的 ...

  2. Linux下套接字详解(二)----套接字Socket

    在前面我们讲了TCP/IP.TCP和UDP的一些基本知识,但是协议只有一套,而我们系统多个TCP连接或多个应用程序进程必须通过同一个 TCP协议端口传输数据.为了区别不同的应用程序进程和连接,许多计算 ...

  3. 套接字详解(socket)

    用户认为的信息之间传输只是建立以两个应用程序上,实际上在TCP连接中是靠套接字来作为他们连接的桥梁. 那么什么是套接字呢? TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做 ...

  4. python socket.socket()函数 套接字详解及TCP、UDP程序示例(粘包等)

    文章目录 socket的定义 套接字的工作流程 socket函数使用 socket函数用法 服务端套接字函数 客户端套接字函数 公共用途的套接字函数 面向文件的套接字方法 打电话的流程演示 服务端.p ...

  5. Linux下套接字详解(四)----简单的TCP套接字应用(迭代型)

    前面我们已经将了TCP/UDP的基本知识,还说了并发服务器与迭代服务器的区别,我们大致了解大多数TCP服务器是并发的,大多数UDP服务器是迭代的 ,即我们在进行数据传送的时候,往往使用服务器与客户但之 ...

  6. Linux下套接字详解(七)----线程池accept处理高并发connect

    前言 服务器在调用listen和accept后,就会阻塞在accept函数上,accpet函数返回后循环调用accept函数等待客户的TCP连接. 我们知道服务器段listen套接字能处理的连接数与监 ...

  7. Linux下套接字详解(六)----基于pthread的多线程的TCP套接字(阻塞/同步/并发)

    上节我们实现了一个简单的多进程的服务器程序,这节,我们服务器的框架不做修改,只是将其修改为一个多线程的服务器程序. 直接上代码 server #include <stdio.h> #inc ...

  8. Linux下套接字详解(五)----基于fork多进程的TCP套接字(阻塞/同步/并发)

    简介 一个简单的改进方案是在服务器端使用多线程(或多进程).多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他的连接.具体使用多进程还是多线程,并没有 ...

  9. Linux下套接字详解(补充)--OSI七层与TCP/IP五层网络架构详解

    OSI七层模型 OSI是Open System Interconnect的缩写,意为开放式系统互联. OSI是Open System Interconnect的缩写,意为开放式系统互联. OSI七层参 ...

最新文章

  1. python使用循环嵌套显示数字金字塔_如何使用Python生成数字金字塔?
  2. 大数据时代:从1.0到3.0 | 专访清华社会学系教授罗家德
  3. “用手机就能访问卫星” 软件定义升级卫星智能
  4. linux nobody 用户,Linux CentOS7安装配置tomcat8(使用非root用户/nobody用户运行)
  5. FBL3N增加表字段
  6. boost::proto模块实现构建算术表达式的简单示例 带有占位符的评估器的测试程序
  7. BASIC-13 数列排序
  8. 深入理解计算机系统第六章家庭作业之6.35 6.36
  9. JavaScript生成树形菜单(递归算法)
  10. python中in,not in,比较运算符,格式化输出,编码
  11. matlab求定积分
  12. 软件设计师历年真题常错的题加解析——上午题
  13. pci-e串口卡linux 驱动下载,PCI/PCIe卡驱动
  14. 微信文件夹储存在什么位置?如何修改保存路径
  15. 百度BAE平台使用(一)注册开发者BAE环境
  16. An Introduction to Pairing-Based Cryptography学习笔记
  17. Chrome浏览器整个网页截图
  18. Linux主机安全加固方法使用开源软件fail2ban防护主机
  19. 【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )
  20. 一分钟了解“副词、系动词、情态动词、实意动词的先后位置关系”

热门文章

  1. Android 超清大尺寸图片压缩转Base64中卡顿/速度优化问题整理(在子线程压缩Bitmap卡的主线程进度条走不动了。。。)
  2. Win10局域网环境互相ping通
  3. 娜迦智能云更新,app开发者的成本节省
  4. 【Android】用Cubism 2制作自己的Live2D——android sdk样本的下载与Android studio编译!...
  5. Java Sleep的替换方法
  6. 字符串大小写转换html,javascript 字符串大小写转换的方法
  7. 小白鼠喝水问题------计算机思维 编码思想(自用)
  8. Makefile中patsubst函数使用方法
  9. 如何使用npm引入bootstrap
  10. php黑名单,PHP 数组黑名单/白名单实例代码详解