最近在做的项目中,涉及到进程间的数据传输。系统原来的实现是通过管道的,但是原来的实现中,两个进程在同一台机器上,两个进程之间的关系是父子关系,我们需要做的是移植一个进程到服务器,所以两个进程必须分开,所以管道肯定是不可行的方案,而对于其他进程通信方式,FIFO、消息队列、信号量和共享内存显然也是不可行的。因此采用了通过socket的通信方式,即网络socket,用于数据传输。接下来整理一下我对socket的学习。什么是插座?分析了套接字创建、绑定、发送和接收消息,以及一个简单的代码示例。网络套接字Socket是通信端点的抽象,它的英文socket是socket和hole的意思。如果两台机器要通信,中间需要通过一根线,这条线的两端需要连接通信的两端。之前,我们必须在通信的两端建立这个插口。同时,为了保证正确的通信,两端之间的插孔必须是一一对应的,这样两端才能正确通信,而这个套接字对应的就是我们实际的操作系统,也就是套接字文件。创建后,我们会得到操作系统返回的文件的描述符,然后应用程序可以使用套接字描述符Word访问套接字,向其写入输入,并从中读取数据。从更接近系统的层面来看,两台机器之间的通信方式无非就是传输层的TCP/UDP和网络层的IP。因此,套接字本质上是TCP/UDP/IP的编程接口(API)。封装,TCP/UDP/IP也为程序员做网络开发提供了一个接口,就是Socket编程接口。套接字创建#includeintsocket(intdomain,inttype,intprotocol);创建一个socketintserver_sockfd=socket(AF_INET,SOCK_STREAM,0);这样,我们就创建了一个socket,对于socket来说,接收到的参数是什么意思呢?从上面我们可以知道,socket是对底层网络通信的一种封装,底层网络通信有很多种。这些参数组合起来代表各种通信类型的特点,从而建立正确的套接字。domain:通信的特性,每个domain都有自己的地址表示格式,以AF开头,表示地址族(Addressfamily)type:socket的类型,进一步决定了通信特性。协议:表示为给定的域和套接字类型选择默认协议。当同一个域和套接字类型支持多种协议时,可以通过该字段选择具体的协议,通常默认为0。上面设置的套接字类型,在执行时也会有提供一个默认的协议类型,比如SOCK_STREAM为TCP协议。从上面的套接字类型中,我们看到有一种SOCK_RAW类型,SOCK_RAW套接字提供了数据报接口。通过这个,我们可以直接访问下层网络,绕过TCP/UDP,这样我们就可以制定自己的传输层协议。至此,我们的socket就创建好了。当我们不再使用它的时候,我们可以调用close函数关闭它,释放文件描述符,这样就可以重新使用了。Socket通信是双向的,但是我们可以使用shutdown函数来禁止一个socket的I/O。#includeintshutdown(intsockfd,inthow);how可以用来指定读端口或者写端口,这样我们就可以关闭读或者写端口了。通信我们已经创建了Socket,接下来要做的就是通过socket进行通信,两个进程之间要进行通信,首先我们需要找到这些进程,找到进程,也就是我们可以有唯一标识这些过程,有了这些标志,我们就可以确定通信双方进行通信,然后进行数据传输。对于一个通信过程的标记,采用的方法是使用网络地址,即IP地址,找到我们要通信的主机,然后通过端口号,找到对应的服务。网络地址+端口号唯一标识了我们要与之通信的目标进程。EndiannessEndianness是处理器架构的一个特性,用来表示整数等数据类型在内部是如何排序的,bigendian和littleendian,所以如果通信双方的处理器架构不同,字节顺序就会不同.出现不一致。底层网络协议规定了字节序和big-endian字节序,但是应用在处理数据的时候,会遇到字节序不一致的问题。为此,系统提供了处理器字节序和网络字节序之间转换的功能。#includeuint32_thtonl(uint32_thostint32)//将主机字节序转为网络字节序uint16_thtons(uint16_thostint16)uint32_tntohl(uint32_tnetint32)//将网络字节序转为主机字节序netint16)地址格式上面我们已经讲了如何表示一个进程进行通信,需要网络地址和端口,但是在系统中如何具体标记这个特性呢?根据前面socket的创建,我们知道不同的socket对应不同的通信特性,而对于不同的通信特性,其地址表示也存在一些差异。这里我们只看IPV4互联网域地址的表示结构。结构sockaddr_in{sa_family_tsin_family;in_port_tsin_port;structin_addrsin_addr;}sin_family:通信域,这里是AF_INETsin_port:通信端口sin_addr:网络地址socket和地址关联我们的socket已经创建好了,地址结构也明白了,下一步就是将socket与地址,关联的方法是通过bind函数。#includeintbind(intsockfd,conststructsockaddr*addr,socklen_tlen);创建地址结构sockaddr_inserver_sockaddr;server_sockaddr.sin_family=AF_INET;server_sockaddr.sin_port=htons(PORT);server_sockaddr.sin_addr。s_addr=htonl(INADDR_ANY);socklen_tserver_len=sizeof(server_sockaddr);bind(server_sockfd,(structsockaddr*)&server_sockaddr,server_len)通过bind函数,我们实现了socket和地址的绑定。建立连接套接字建立,地址绑定。这时候,我们就可以连接了。一方需要通过调用connect函数建立连接。#includeintconnect(intsockfd,conststructsockaddr*addr,socklen_tlen);sockfd:这是本地套接字描述符,如果我们不赋值,系统会默认提供一个值。只有服务器开机正常运行,我们的连接才能正常建立。如何让套接字接收连接请求?在另一端,我们调用listen方法来接收连接请求。#includeintlisten(intsockfd,intbacklog);sockfd:地址绑定的套接字文件描述符。backlog:服务器负载,提示系统进程将入队的未完成请求数。我们通过listen得到连接请求,接下来就是建立连接,通过函数accept#includeintaccept(intsockfd,structsockaddr*restrictaddr)调用accept函数的返回,socklen_t*限制len);该值是连接到调用connect的客户端的套接字文件描述符。服务器一旦调用listen,使用的socket就可以接收到连接请求,使用accept函数获取连接请求并建立连接。使用accept函数获取连接请求并建立连接。intaccept(intsockfd,structsockaddr*restrictaddr,socklent_t*restrictlen);当调用accept函数时,将生成一个新的套接字。这个新套接字与原始套接字具有相同的套接字类型。这时候,我们可以传入一个指向套接字的指针和它的大小。设置好后,调用accept会缓冲客户端的地址。数据传输连接已建立。由于socket本身就是一个文件描述符,所以可以调用read和write通过socket进行通信。对于面向连接的数据传输,我们需要的两个函数是send和recv。#includessize_tsend(intsockfd,constvoid*buf,size_tnbytes,intflags)sockfd:accept返回的套接字文件描述符。buf:发送数据,bytes:发送数据大小flags:发送数据的一些配置项针对不同的socket类型,系统提供了不同的发送方式。#includessize_trecv(intsockfd,void*buf,size_tnbytes,intflags)具体参数和send类似。Socket选项设置对于Socket,系统提供了一些更具体、更详细的配置选项,我们可以通过这些选项进行更具体的配置。#includeintsetsockopt(intsockfd,intlevel,intoption,constvoid*val,socklen_tlen);sockfd:我们要配置的socketlevel:根据我们选择的协议,配置相应的协议号option:option是上表最后一个参数,用来存放返回值实现demo实例server#include#include#include#include#include#include#include#include#include#include#definePORT22468#defineKEY123#defineSIZE1024intmain(){charbuf[100];内存集(buf,0,100);intserver_sockfd,client_sockfd;socklen_tserver_len,client_len;类型是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=sizeofver_sockaddr);上;setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/*绑定套接字或重命名套接字*/if(bind(server_sockfd,(structsockaddr*)&server_sockaddr,server_len)==-1){printf("绑定错误");退出(1);}if(listen(server_sockfd,5)==-1){printf("监听错误");退出(1);}client_len=sizeof(client_sockaddr);pid_tppid,pid;while(1){if((client_sockfd=accept(server_sockfd,(structsockaddr*)&client_sockaddr,&client_len))==-1){printf("连接错误");退出(1);}else{printf("创建连接成功\n");interror=send(client_sockfd,"你已经连接服务器",strlen("你已经连接服务器"),0);printf("%d\n",错误);}}return0;}client#include#include#include#include#include#include#include#include#include#include#defineSERVER_PORT22468#defineMAXDATASIZE100#defineSERVER_IP“你的IP”intmain(){intsockfd,numbytes;charbuf[最大数据大小];结构sockaddr_in服务器地址;printf("\n======================客户端初始化======================\n");如果((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){perror("socket");退出(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,(structsockaddr*)&server_addr,sizeof(structsockaddr_in))==-1){perror("连接错误");退出(1);}while(1){bzero(buf,MAXDATA尺寸);printf("\n开始接收...\n");如果((numbytes=recv(sockfd,buf,MAXDATASIZE,0))==-1){perror("recv");退出(1);}elseif(numbytes>0){intlen,bytes_sent;buf[numbytes]='\0';printf("收到:%s\n",buf);printf("发送:");字符消息[100];scanf("%s",味精);len=strlen(味精);//发送到服务器if(send(sockfd,msg,len,0)==-1){perror("senderror");}}else{printf("套接字结束!\n");休息;}}关闭(sockfd);可以帮我们在两端生成stub文件,原理上就是通过底层的socket通信进行封装,对应的调用socket通信写在大三的OS类中。本文的主要目的是记录这次学习。对套接字知识进行了回顾。