一、Socket到底是什么

socket 这个英文单词的原意是“插口”“插槽”, 在网络编程中,它的意思是可以通过插口接入的方式,快速完成网络连接和数据收发。你可以把它想象成现实世界的电源插口,或者是早期上网需要的网络插槽,所以 socket 也可以看做是对物理世界的直接映射。

在Linux中socket是一种文件类型,伪文件,不占用存储空间,可进行IO操作,可间接看做文件描述符使用。

二、如何理解Socket

首先,我们来看一张图

这张图表达的是网络编程中,客户端和服务器工作的核心逻辑。

我们先从右侧的服务器端开始看,因为在客户端发起连接请求之前,服务器端必须初始化好。右侧的图显示的是服务器端初始化的过程,首先初始化 socket,之后服务器端需要执行 bind 函数,将自己的服务能力绑定在一个众所周知的地址和端口上,紧接着,服务器端执行 listen 操作,将原先的 socket 转化为服务端的 socket,服务端最后阻塞在 accept 上等待客户端请求的到来。

此时,服务器端已经准备就绪。客户端需要先初始化 socket,再执行 connect 向服务器端的地址和端口发起连接请求,这里的地址和端口必须是客户端预先知晓的。这个过程,就是著名的TCP 三次握手。

一旦三次握手完成,客户端和服务器端建立连接,就进入了数据传输过程。

具体来说,客户端进程向操作系统内核发起 write 字节流写操作,内核协议栈将字节流通过网络设备传输到服务器端,服务器端从内核得到信息,将字节流从内核读入到进程中,并开始业务逻辑的处理,完成之后,服务器端再将得到的结果以同样的方式写给客户端。可以看到,一旦连接建立,数据的传输就不再是单向的,而是双向的,这也是 TCP 的一个显著特性

讲这幅图的真正用意在于引入 socket 的概念,请注意,以上所有的操作,都是通过 socket 来完成的。无论是客户端的 connect,还是服务端的 accept,或者 read/write 操作等,socket 是我们用来建立连接,传输数据的唯一途径

更直观的理解Socket

在极客时间的网络编程实战中,把Socket比作打电话,可以更直观的理解这个过程:

你可以把整个 TCP 的网络交互和数据传输想象成打电话,顺着这个思路想象,socket 就好像是我们手里的电话机,connect 就好比拿着电话机拨号,而服务器端的 bind 就好比是去电信公司开户,将电话号码和我们家里的电话机绑定,这样别人就可以用这个号码找到你,listen 就好似人们在家里听到了响铃,accept 就好比是被叫的一方拿起电话开始应答。至此,三次握手就完成了,连接建立完毕。

接下来,拨打电话的人开始说话:“你好。”这时就进入了 write,接收电话的人听到的过程可以想象成 read(听到并读出数据),并且开始应答,双方就进入了 read/write 的数据传输过程。

最后,拨打电话的人完成了此次交流,挂上电话,对应的操作可以理解为 close,接听电话的人知道对方已挂机,也挂上电话,也是一次 close。

在整个电话交流过程中,电话是我们可以和外面通信的设备,对应到网络编程的世界里,socket 也是我们可以和外界进行网络通信的途径。

Socket套接字支持网络上两台以上的设备进行通信,因为Socket有双个缓冲区,进行的是双全工通信。

三、如何利用套接字进行读写交流

  1. 服务器通过accept函数返回值可以获得客服端的套接字,我们就可以对客服端进行IO(读写)操作
  2. 客服端通过connect函数的第一个参数绑定服务器,我们就可以对服务器进行IO(读写)操作
  3. 将套接字看做文件描述符使用,更好理解
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

四、网络套接字函数详解

4.1 Socket( )函数

作用:用于服务器和客户端,创建套接字,返回一个可操作的文件描述符

参数使用:

int socket(int domain,int type,int protocal);
  • 参数一:表示ip地址类型,常用的有两种

    其中AF_INET表示IPv4地址,比如127.0.0.1,这是一个本机测试ip
    其中AF_INET6表示IPv6地址,比如2001:3CA1:10F:1A:121B:0:0:10

  • 参数二:表示数据传输方式/套接字类型,常见两种

    SOCK_STREAM(流格式套接字/面向连接的套接字)
    SOCK_DGRAM (数据报套接字/无连接的套接字)

  • 参数三:表示传输协议,理论上前两个参数已经可以推演出采用哪种协议主要是为了解决,两种不同的协议支持同一种地址类型和数据型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。如果两种情况只有一个协议满足条件,可以将protocol 的值设为 0,系统自动推演出采用哪种协议
  • 返回值:返回一个套接字(文件描述符fd)

4.2bind( )函数

作用:用于服务器,给sockfd套接字绑上本机地址和使用端口,确定了服务器的身份

参数使用:

Int bind(int sockfd,const struct sockaddr*addr,socklen_t addrlen);
  • 参数一:套接字的fd(文件描述符),socket()函数的返回值
  • 参数二:结构体 ip+port(端口)

struct sockaddr_in{ (涉及强制转换sockaddr_in ->sockaddr  参考)short int sin_family;              //地址族unsigned short int sin_port;       //端口号struct in_addr sin_addr;           //IP地址
}
struct in_addr {__be32 s_addr;
};
  • 参数三:结构体的字节长度
  • 返回值:判断成功失败

4.3listen( )函数

函数作用:用于服务器,使socket处于监听模式,监听时候有客户端连接,并放入队列(也可以说,设置同时与服务器建立连接的上限)(同时进行3次握手连接的客户端数量)

参数使用:

int listen(int sockfd,int backlog);
  • 参数一:bind绑定ip和端口的套接字
  • 参数二:请求链接客户端队列的最大存放数目
  • 返回值:判断成功失败

4.4 accept( )函数

函数作用:用于服务器,接收一个客户端的连接请求,并返回连接客户端的套接字便于IO操作,如果没有客户连接会阻塞等待。

参数使用:

int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen)
  • 参数一:服务器的套接字(也叫监听套接字),表明了自己的身份
  • 参数二:传出参数,跟我建立连接的客户端的结构体(内含客户端ip+端口)
  • 参数三: 结构体长度的指针 &sizeof()
  • 返回值:连接客户端的套接字

4.5 connect( )函数

函数作用:用于客户端,函数可以和自动与远端服务器建立连接
参数使用:

int connect(int sockfd,struct sockaddr*serv_addr,int addrlen)
  • 参数一:传入参数,文件描述符绑定连接成功的服务器套接字便于在客户端对服务器进行IO操作
  • 参数二:绑定我要链接服务器的结构体(需要初始化绑上ip和断口),表明目的
  • 参数三:结构体的长度

五、代码实例

5.1实现客户端与服务器消息互发

服务端

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SER_PORT 8000
int main(void)
{int sockfd,connfd;//int len;char wbuf[1024];char rbuf[1024];struct sockaddr_in serveraddr,clientaddr; //两个结构体 一个用于绑定身份到套接字  一个用于接收客服端的结构体//1.创建监听套接字sockfd = socket(AF_INET,SOCK_STREAM,0);//2.bind(通信需要套接字 把家的地址 门牌号绑上去 ip和端口)bzero(&serveraddr,sizeof(serveraddr)); //类似memset 清空结构体//地址族协议,选择IPV4serveraddr.sin_family = AF_INET;     //属于ipv4还是ipv6//IP地址 本机任意可用ip地址serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_port = htons(SER_PORT);//端口号    bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//3.监听 和服务器连接的总和listen(sockfd,128);  int size = sizeof(clientaddr);//4.accept 阻塞监听 客服端链接的请求connfd = accept(sockfd,(struct sockaddr *)&clientaddr,&size);   //输出客服端的ip和端口char ipstr[128];printf("client ip%s ,port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),ntohs(clientaddr.sin_port));//5.处理客户端请求//读和写while(1){memset(wbuf,0,sizeof(wbuf));//清空memset(rbuf,0,sizeof(wbuf)); //接收消息  int len = read(connfd,rbuf,sizeof(rbuf));if(len==0)//表示断开连接{printf("client is close....\n");}printf("receive from client:%s",rbuf);//发送消息printf("send to client:");fgets(wbuf,sizeof(wbuf),stdin);write(connfd,wbuf,strlen(wbuf)); }close(connfd);close(sockfd);return 0;
}

客户端

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SER_PORT 8000
int main(void)
{int sockfd;struct sockaddr_in serveraddr;int len;char wbuf[1024],rbuf[1024];//1、socket 通信用套接字,创建一个sockfdsockfd = socket(AF_INET,SOCK_STREAM,0);char ipstr[]="127.0.0.1";//2、编辑要连接的服务器地址,并绑定bzero(&serveraddr,sizeof(serveraddr));serveraddr.sin_family = AF_INET;            //设置地址族协议serveraddr.sin_port = htons(SER_PORT);      //设置端口号inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);//设置ip地址   点分十进制转成网络字节序//2、connect 连接服务器 sockfd传出服务器套接字connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));//3、读写while(1){memset(wbuf,0,sizeof(wbuf));memset(rbuf,0,sizeof(rbuf));//发送消息printf("send to server:");fgets(wbuf,sizeof(wbuf),stdin);write(sockfd,wbuf,strlen(wbuf));//接收消息len=read(sockfd,rbuf,sizeof(rbuf));if(len==0)//表示断开连接{printf("server is close....\n");}printf("receive from server:%s",rbuf);}//4、closeclose(sockfd);return 0;
}

5.2回射服务器

  • 简单的说就是即从客户端收到什么数据,就发送什么数据回去

    前提须知:read读取不到信息会阻塞等待!
    执行过程: 1 ->2->5->6->3->4

         /* 客服端部分 */1  scanf("%s",wbuf);//等待键盘输入 2  write(sfd,wbuf,strlen(wbuf));//写入服务器3  read(sfd,rbuf,sizeof(rbuf));//等待客服端写会4  printf("%s\n",rbuf);//打印内容----------------------分割线------------------------------/*服务器部分*/5  read(confd,buf,sizeof(buf)); //阻塞等待6  write(confd,buf,len); //写会客服端

流程

  • 首先客服端与服务器建立连接
  • 客服端等待键盘输入(scanf 或者 fget)
  • 如果此时键盘输入回车确认 客服端write写入服务器 服务器read读到了信息
  • 服务器再将read读到的信息write写回客服端 客服端read接受到信息并打印

服务端:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{int sockfd,confd;char ipstr[128];int size;char buf[1024];int i;int len;//两个结构体 一个用于绑定身份到套接字  一个用于接收客服端的结构体struct sockaddr_in serveraddr,clientaddr; //1.创建监听套接字sockfd = socket(AF_INET,SOCK_STREAM,0);//2.bind(通信需要套接字 我把我家的地址 门牌号绑上去 ip和端口)bzero(&serveraddr,sizeof(serveraddr)); //类似memset 清空结构体//地址族协议,选择IPV4serveraddr.sin_family = AF_INET;     //属于ipv4还是ipv6//IP地址 本机任意可用ip地址serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_port = htons(8002);//端口号bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//3.监听 128为服务器连接的总和listen(sockfd,128);size = sizeof(clientaddr);//4.accept 阻塞监听 客服端链接的请求//参数二结构体的转换confd = accept(sockfd,(struct sockaddr *)&clientaddr,&size); //输出客服端的ip和端口printf("client ip%s ,port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),ntohs(clientaddr.sin_port));//5.处理客户端请求//读和谐while(1){len=read(confd,buf,sizeof(buf)); //接受不到 阻塞等待#if 0  //if 0 endif   之间的大小写转换代码已经屏蔽i=0;while(i<len){buf[i]=toupper(buf[i]);//小写转大写的操作i++; }#endifwrite(confd,buf,len);memset(buf,0,1024);}close(confd);close(sockfd);return 0;
}

客户端:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc,char* argv[])
{int sfd;struct sockaddr_in sfdaddr; //指定要连接服务器的结构体 ip 端口int len;//char buf[1024];char wbuf[1024];char rbuf[1024];//1.socket  通信用套接字,创建一个socketsfd = socket(AF_INET,SOCK_STREAM,0);         char ipstr[] = "127.0.0.1";                  //或者本机测试ip//char ipstr[] = "192.168.3.106";           //要连上的ip地址//初始化地址bzero(&sfdaddr,sizeof(sfdaddr));sfdaddr.sin_family = AF_INET;sfdaddr.sin_port = htons(8002);inet_pton(AF_INET,ipstr,&sfdaddr.sin_addr.s_addr); //转换ip 保存到结构体内 //2.connect  主动连接服务器  sfd返回客服端的套接字(文件描述符)connect(sfd,(struct sockaddr *)&sfdaddr,sizeof(sfdaddr));  //3.读写  while(1){        memset(wbuf,0,1024);memset(rbuf,0,1024);scanf("%s",wbuf);write(sfd,wbuf,strlen(wbuf));len=read(sfd,rbuf,sizeof(rbuf));//write(STDOUT_FILENO,buf,len);printf("%s\n",rbuf);}    //4.closeclose(sfd);return 0;
}

Linux网络编程之Socket套接字相关推荐

  1. linux网络编程之SCTP套接字常用接口

    转载地址: oracle开发帮助文档:http://docs.oracle.com/cd/E19253-01/819-7052/index.html SCTP 套接字接口 当 socket() 调用为 ...

  2. linux网络编程之Socket编程

    (1)socket套接字 1)在linux环境下,socket用于表示进程间网络通信的特殊文件类型,其本质是内核借助缓冲区形成的伪文件(不占磁盘空间,除此之外还有二进制文件,管道,字符文件). 2)伪 ...

  3. 【JavaEE】网络编程之TCP套接字、UDP套接字

    目录 1.网络编程的基本概念 1.1为什么需要网络编程 1.2服务端与用户端 1.3网络编程五元组 1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点 2.2UDP套接字API 2.2 ...

  4. linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时

    一.使用alarm 函数设置超时 C++ Code  1 2 3 4 5 6 7 8 9 10 11 12 13   void handler( int sig) { } signal(SIGALRM ...

  5. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:"真爱生活,珍惜生命",好了,言归正传. ...

  6. linux网络编程之socket:使用fork并发处理多个client的请求

    在回射客户/服务器程序中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到select/poll/epoll等高级IO之前,比较老土的办法是使用fork来实现.网络服务器通常用for ...

  7. linux多网卡网络编程,Linux网络编程之Socket初探

    Socket由来 Socket 的英文原意就是"孔"或"插座",现在,作为 BSD UNIX 的进程通讯机制,取其后一种意义.一起看下网络编程里说的socket ...

  8. linux网络编程之socket(十):shutdown 与 close 函数 的区别

    假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...

  9. Linux网络编程之socket文件传输示例

    本文所述示例程序是基于Linux平台的socket网络编程,实现文件传输功能.该示例是基于TCP流协议实现的socket网络文件传输程序.采用C语言编写.最终能够实现传输任何格式文件的文件传输程序. ...

最新文章

  1. 数据蒋堂 | 数据压缩手段
  2. 【数字信号处理】相关系数 ( 相关系数特点 | 完全相关 | 完全无关 | 部分相关 | 取值范围 | 相关信号产生 | 相干信号产生 )
  3. java升级为jdk1.8_jdk1.7升级为1.8
  4. CustomValidator的使用方法
  5. java定向输出程序日志(输出到txt文件中)
  6. 堕落 Java vs 新贵 Python,2018 年最应该学习哪一门编程语言?
  7. SQL语句group by 与order by 执行顺序引发的一场“内斗”
  8. 企业经常说绩效管理难,误区在哪?附绩效管理系统解决方案
  9. 工具 IDA Pro
  10. matlab2017安装完提示编译器,matlab2017a/b 编译器设置之无语神坑
  11. resin设置权限_resin加固
  12. 佟年计算机大赛,ACM大赛
  13. 浅谈用户营销模型AIPL
  14. 串口调试助手fx2n_PLC串口调试助手
  15. matlab神经网络工具箱使用教程
  16. 没有银弹-软件工程中的根本和次要问题
  17. 匈牙利算法的Java语言实现
  18. 2021年压力容器作业R2移动式压力容器充装证考试题库
  19. 学英语最有效的办法:模仿+重复!
  20. 《计算机视觉和图像处理简介 - 中英双语 + 代码实践版》:基于PyTorch Softmax 进行 MNIST 手写数字分类【Digit Classification with Softmax】

热门文章

  1. 1.PS-打开、新建及存储
  2. 因果关系的倒置(3)
  3. 理清容器标准和基金会:OCI,CNCF,appc 和 rkt
  4. redirect_uri 域名与后台配置不一致 10003
  5. 利用sham-link实现路由还原
  6. kindle电子书分享网 - 技术栈
  7. android 15K+面试题
  8. 三大统计学相关系数(pearson、kendall、spearman)
  9. MySQL优化—工欲善其事,必先利其器(2)
  10. 3D打印Gcode命令指令简析