近期在做的项目中,涉及到了进程间数据传输,系统的原本实现是通过管道,但是原有的实现中两个进程是在同一台机器,而且两个进程的关系为父子关系,而我们要做的是将其中一个进程移植到服务器上,因此两个进程要分开,所以管道必然是不可行的方案,而对于其它的进程通信方式,FIFO,消息队列,信号量和共享内存,显然也是不可行的。因此采取了通过socket的通信方式,即网络套接字,用来做数据的传输。接下来,将对自己对socket的学习一个整理,socket是什么?socket的创建,绑定,发送,接收消息过程进行分析,同时附带一个简单的代码实例。

网络套接字Socket

套接字是通信端点的抽象,其英文socket,即为插座,孔的意思。如果两个机子要通信,中间要通过一条线,这条线的两端要连接通信的双方,这条线在每一台机子上的接入点则为socket,即为插孔,所以在通信前,我们在通信的两端必须要建立好这个插孔,同时为了保证通信的正确,端和端之间的插孔必须要一一对应,这样两端便可以正确的进行通信了,而这个插孔对应到我们实际的操作系统中,就是socket文件,我们再创建它之后,就会得到一个操作系统返回的对于该文件的描述符,然后应用程序可以通过使用套接字描述符访问套接字,向其写入输入,读出数据。
站在更贴近系统的层级去看,两个机器间的通信方式,无非是要通过运输层的TCP/UDP,网络层IP,因此socket本质是编程接口(API),对TCP/UDP/IP的封装,TCP/UDP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。
Socket的创建

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

创建一个socket

int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

这样,我们便创建了一个socket,对于socket接收的参数都有什么意义呢?从上面,我们可以知道socket是对于底层网络通信的一个封装,而对于底层的网络通信也是具备多种类型的。而这些参数则是通过组合来表示各类通信的特征,从而建立正确的套接字。

  • domain:通信的特性,每个域有自己的地址表示格式,AF打头,表示地址族(Address family)


type:套接字的类型,进一步确定通信特征。

protocol:表示为给定域和套接字类型选择默认协议,当对同一域和套接字类型支持多个协议时,可以通过该字段来选择一个特定协议,通常默认为0.上面设置的socket类型,在执行的时候也会有默认的协议类型提供,比如SOCK_STREAM就TCP协议。

从上面的socket类型中,我们看到有SOCK_RAW该种类型,SOCK_RAW套接字提供一个数据报接口。通过这个我们可以直接访问下面的网络层,绕过TCP/UDP,因此我们可以进行制定自己的传输层协议。
至此,我们的socket已经创建出来了,当我们不再使用的时候,我们可以调用close函数来将其关闭,释放该文件描述符,这样便可以得到重新的使用。
套接字通信是双向的,但是,我们可以采用shutdown函数来禁止一个套接字的I/O.

#include<sys/socket.h>
int shutdown(int sockfd, int how);

how可以用来指定读端口或者是写端口,这样我们便可以关闭掉读端或者写端。

通信

我么已经创建好了Socket,接下来要做的就是通过socket进行通信了,在两个进程间进行通信,首先,我们要找到这些进程,找到进程,也就是能够有这些进程的唯一标示,有了这些标示,我们才可以确定通信的双方,然后进行数据的传输,对于一个通信进程的标示,所采取的方式是通过一个网络地址,也就是IP地址,战找到我们要通信的主机,然后通过端口号,找到相应的服务。网络地址+端口号唯一标示了一个我们要通信的目标进程。

  • 字节序

字节序是处理器架构的特性,用来指示像整数这种数据类型的内部如何排序,大端和小端,因此如果通信双方的处理器架构不同,则会导致字节序的不一致的问题出现。最底层的网络协议指定了字节序,大端字节序,但是应用程序在处理数据时,则会遇到字节序不一致的问题。对此,系统提供了进行处理器字节序和网络字节序之间实施转换的函数。

#include <arpa/inet.h>uint32_t htonl(uint32_t hostint32)//主机字节转化为网络字节序
uint16_t htons(uint16_t hostint16)
uint32_t ntohl(uint32_t netint32)//网络字节序转化为主机字节序
unint16_t ntohs(uint16_t netint16)
  • 地址格式

上面,我们已经谈到如何表示一个要通信的进程,需要一个网络地址和端口,而在系统中如何具体的标示这一特征呢?根据之前socket的创建,我们知道不同socket对应了不同的通信特征,而对于不同的通信特征,其地址表示上也有一些差别。
这里我们只看一下IPV4因特网域地址的表示结构。
struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr;}
sin_family: 通信的的域,这里为AF_INET
sin_port:通信的端口
sin_addr:网络地址

套接字和地址关联

我们套接字已经创建好了,地址结构也已经了解了,接下来就是要将套接字和地址进行关联,关联的方法则是通过bind
函数。

#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t len);

创建地址

struct sockaddr_in server_sockaddr;server_sockaddr.sin_family = AF_INET;server_sockaddr.sin_port = htons(PORT);server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t server_len = sizeof(server_sockaddr);
bind(server_sockfd, (struct sockaddr*)&server_sockaddr, server_len)

通过bind函数,我们实现了socket和地址的绑定。
建立连接
socket建立好了,地址也绑定好了,这个时候,我们就可以进行连接了,要有一方进行连接的建立,通过调用connect
函数。

#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t len);

sockfd:这个就是本地端socket描述符,如果我们没有赋值,系统会默认提供一个值。只有当服务器开启,并正常运行,我们的连接才能够正常建立。
如何让socket接收连接请求呢?在另一端,我们调用listen
方法来接收连接请求。

#include <sys/socket.h>int listen(int sockfd, int backlog);
  • sockfd:绑定了地址的socket文件描述符。
  • backlog:服务器负载,提示系统进程所要入队的未完成请求数量。
    通过listen我们得到了连接请求,接下来,就是建立连接,通过函数accept
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *restric addr, socklen_t *restrict len);

调用accept函数的返回值是套接字文件描述符,该描述符连接到调用connect的客户端。
一旦服务器调用了listen,所用的套接字就能接收连接请求,使用accept函数获得连接请求并建立连接。
使用accept函数获得连接请求并建立连接。

int accept(int sockfd, struct sockaddr *restrict addr, socklent_t *restrict len);

当调用accept函数会产生一个新的套接字,这个新的套接字和原始套接字有相同的套接类型。这个时候,我们可以传入一个指向socket的指针和其大小,设置之后,调用了accept就会将客户端的地址进行缓冲。
数据传输
连接已经建立好了,由于socket本身都是文件描述符,因此接下来就可以调用所read和write来通过套接字通信。
对于面向连接的数据传输,我们需要的两个函数是send和recv。

#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags)
  • sockfd:accept返回的socket文件描述符。
  • buf:发送数据,
  • bytes:发送数据大小
  • flags:对于传送数据的一些配置项

对于不同的socket类型,系统提供了不同的发送方法。

#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags)

具体参数和send类似。
socket选项设置
对于Socket,系统提供了更具体细致化的一些配置选项,通过这些配置选项,我们可以进行进一步具体的配置。

#include <sys/socket.h>int setsockopt(int sockfd, int level, int option, const void *val, socklen_t len);


sockfd:我们要进行配置的socket
level:根据我们选用的协议,配置相应的协议编号
option:选项即为上表
最后参数是用来存放返回值

实现demo实例
server

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>#define PORT 22468
#define KEY 123
#define SIZE 1024int main()
{char buf[100];memset(buf,0,100);int server_sockfd,client_sockfd;socklen_t server_len,client_len;struct  sockaddr_in server_sockaddr,client_sockaddr;/*create a socket.type is AF_INET,sock_stream*/server_sockfd = socket(AF_INET,SOCK_STREAM,0);server_sockaddr.sin_family = AF_INET;server_sockaddr.sin_port = htons(PORT);server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);server_len = sizeof(server_sockaddr);int on;setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR,&on,sizeof(on));/*bind a socket or rename a sockt*/if(bind(server_sockfd, (struct sockaddr*)&server_sockaddr, server_len)==-1){printf("bind error");exit(1);}if(listen(server_sockfd, 5)==-1){printf("listen error");exit(1);}client_len = sizeof(client_sockaddr);pid_t ppid,pid;while(1) {if((client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_sockaddr, &client_len)) == -1){printf("connect error");exit(1);} else {printf("create connection successfully\n");int error = send(client_sockfd, "You have conected the server", strlen("You have conected the server"), 0);printf("%d\n", error);}} return 0;
}
  • client
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h> #define SERVER_PORT 22468
#define MAXDATASIZE 100
#define SERVER_IP "Your IP" int main() { int sockfd, numbytes; char buf[MAXDATASIZE]; struct sockaddr_in server_addr; printf("\n======================client initialization======================\n"); if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); }server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); bzero(&(server_addr.sin_zero),sizeof(server_addr.sin_zero)); if (connect(sockfd, (struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) == -1){perror("connect error"); exit(1);} while(1) { bzero(buf,MAXDATASIZE); printf("\nBegin receive...\n"); if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1){  perror("recv"); exit(1);} else if (numbytes > 0) { int len, bytes_sent;buf[numbytes] = '\0'; printf("Received: %s\n",buf);printf("Send:"); char msg[100];scanf("%s",msg);len = strlen(msg); //sent to the serverif(send(sockfd, msg,len,0) == -1){ perror("send error"); }} else { printf("soket end!\n"); break;} }  close(sockfd); return 0;}

总结

最近也在看的一个RPC框架,thrift,定义好我们的接口文件,然后可以帮助我们生成两端的桩文件,而且实现原理上,也不过是通过底层的socket通信做了包装,执行相应的调用。socket通信在大三的OS课上写过,本文主要目的记录本次学习,对于socket知识进行了一个回顾。

Linux下Socket通信(TCP实现)相关推荐

  1. Linux 下socket通信终极指南(附TCP、UDP完整代码)

    linux下用socket通信,有TCP.UDP两种协议,网上的很多教程把两个混在了一起,或者只讲其中一种.现在我把自己这两天研究的成果汇总下来,写了一个完整的,适合初学者参考,也方便自己以后查阅. ...

  2. linux socket ip层配置,Linux下Socket通信(TCP实现)

    近期在做的项目中,涉及到了进程间数据传输,系统的原本实现是通过管道,但是原有的实现中两个进程是在同一台机器,而且两个进程的关系为父子关系,而我们要做的是将其中一个进程移植到服务器上,因此两个进程要分开 ...

  3. Linux下Socket通信中非阻塞connect、select、recv 和 recvfrom、send和sendto大致讲解,附带非租塞connect代码、MSG_NOSIGNAL

    linux中send函数MSG_NOSIGNAL异常消息 在服务器端用ctrl+c 来结束服务器接收进程来模拟服务器宕机的情况,结束服务 socket 进程之后,服务端自然关闭进程,可是 client ...

  4. linux下socket编程-TCP

    网络字节序 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,因此,网络数据流的地址应这样规定:先发出 ...

  5. Linux下socket通信和epoll

    上一篇博客用多线程实现服务端和多个客户端的通信,但是在实际应用中如果服务端有高并发的需求,多线程并不是一个好选择. 实现高并发的一种方法是IO多路复用,也就是select,poll,epoll等等. ...

  6. linux下socket通信,server和client简单例子

    #include "metafile.h" #include <string.h> #include <ctype.h>//定义全局变量 TrackerLi ...

  7. 【Linux】一篇文章搞定 CPP模拟实现TCP协议下socket通信

    CPP模拟实现TCP协议下socket通信 1. TCP 编程流程图 2. 数据收发阶段使用的API 2.1 send接口 2.2 recv接口 3. 两个队列 4. 总结TCP 编程双端流程 5. ...

  8. Linux下Socket编程之TCP应用

    现在,我们用前面所构建的socket类,重新设计<Linux下Socket编程之TCP Server端>中echo的服务器,然后设计客户端程序. echo服务器的工作原理很简单: 1.接收 ...

  9. 一文了解linux下socket编程

    一文了解linux下socket编程 文章目录 一文了解linux下socket编程 1 网络编程的相关简述 1.1 引言 1.2 Tcp和Udp简介 1.3 TCP三次握手和四次挥手 1.4 网络编 ...

  10. linux下socket和MySQL数据库编程

    一.基本socket函数 Linux系统是通过提供套接字(socket)来进行网络编程的.网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符.socket也有一个类似于打 开 ...

最新文章

  1. 约翰•麦卡锡——不走寻常路的常识逻辑学家
  2. 为什么阿里程序猿纷纷在内网晒代码?
  3. springboot转发http请求_网易后端实习生分享:Springboot异常和错误处理规范
  4. RSocket协议初识
  5. 初中三年级计算机课程教案,三年级信息技术教案下
  6. DarkNet yoloV2 转到caffe使用
  7. godot 以 WebAssembly 为目标平台编译导出模板
  8. 12本最具影响力的程序员书籍_书籍书评_酷勤网
  9. ES系列:解决Cluster state has not been recovered yet, cannot write to the [null] index问题
  10. java kryo 序列化_Kryo序列化
  11. 大家怎么看阿里云的“云渲染”服务?
  12. linux conforming code segment nonconforming code segment
  13. sklearn机器学习(五)线性回归算法测算房价
  14. Swift中的UIKit重力学(一)
  15. 详解Material Design体系组件
  16. 案例篇-HBase 在滴滴出行的应用场景和最佳实践
  17. 服装店收银系统如何设置小票内容?
  18. 《网络是怎样连接的》读书笔记1
  19. 用Python对2019年二手房价格进行数据分析
  20. 和菜鸟一起学linux之bluetooth学习记录基础知识

热门文章

  1. Atitit 未来 技术趋势 没落技术 attilax著 艾龙 总结 1. 2018技术趋势 2 1.1. 人工智能与区块链 2 1.2. 2、 PWA 或将大热 2 1.3. 5、
  2. Atitit nodejs5 nodejs6  nodejs 7.2.1  新特性attialx总结
  3. Atitit.列表页面and条件查询的实现最佳实践(2)------翻页 分页 控件的实现java .net php
  4. paip.编程压缩Access数据库
  5. paip.提升效率---质量控制--代码风格模板化
  6. paip.提升安全性--------密码控件与软键盘
  7. Paip.YXSHOP易想商场功能模块说明
  8. 比中年危机更可怕的,是“下半生危机”
  9. 【TSP】基于matlab改进的人工鱼群算法求解旅行商问题【含Matlab源码 1479期】
  10. 【优化算法】正弦余弦算法(SCA)【含Matlab源码 1308期】