深入剖析Linux套接字通信技术 (linux套接字通信)

随着计算机技术的不断发展,Linux作为一个开源的操作系统,在实现高效通信方面显得尤为出色。其中,套接字通信技术是一种非常重要的通信方式。本文将从概念、分类、实现等多个方面。

一、概念

套接字通信技术(Socket Programming)是一种应用程序编写网络通信的方法,通常也称为网络套接字或网络编程。套接字是指应用程序中进行网络通信时所使用的接口,是通信的一种方式。

Linux的套接字是基于文件的输入/输出方法,也就是说,套接字也是一种文件描述符。Linux中所有的输入/输出都是通过文件描述符进行的,所以套接字本质上也是一种文件描述符。

二、分类

Linux套接字通信技术分为两类:流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)。

流套接字提供了一种面向连接的通信方式,数据在传输时保证了数据的可靠性,但是也存在一些缺点,比如发送数据的顺序可能与接收数据的顺序不一致。流套接字通常使用TCP协议进行通信。

数据报套接字则提供了一种面向消息的通信方式,数据在传输时保证了数据的顺序,但是不保证数据的可靠性。数据报套接字通常使用UDP协议进行通信。

三、实现

Linux的套接字通信技术的实现基于两个系统调用:socket()和bind()。socket()系统调用用于创建一个新的套接字,包含了套接字类型、协议类型和协议族等信息。bind()用于将一个特定的地址和套接字进行绑定。

接着,可以使用listen()进行监听,accept()进行连接,发送数据使用send(),接收数据使用recv()。

当使用TCP协议进行通信时,通常需要进行三次握手来建立连接。具体过程如下:

之一次握手:客户端向服务器发送一个SYN报文,并标志位SYN=1,同时选择一个初始的序列号seq=x。

第二次握手:服务器收到SYN报文,必须确认客户端的SYN,同时自己也要发送一个SYN报文,标志位SYN=1,同时选择一个初始的序列号seq=y。

第三次握手:客户端收到服务器的SYN报文,必须确认服务器的SYN。

当连接建立好之后,可以通过send()和recv()进行数据的传输。

四、

Linux套接字通信技术是Linux系统中一种非常重要的通信方式,其实现基于socket()和bind()等系统调用。在使用流套接字时,通信采用TCP协议,需要进行三次握手来建立连接;在使用数据报套接字时,通信采用UDP协议,不存在三次握手过程。

由于其通信方式的高效性和可靠性,Linux套接字通信技术在很多领域得到了广泛应用,比如网络及服务器通信、网络游戏等领域。

相关问题拓展阅读:

linux系统的进程间通信有哪几种方式

数据传输 

一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间

共享数据 

多个进程想要操作共享数据,一个进程对共享数据

通知事 

一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

资源共享 

多个进程之间共享同样的资源。为了作到这一点厅拍,需要内核提供锁和同步机制。

进程控制 

有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

Linux 进程间通信(IPC)的发展

linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的。而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同。

前者对Unix早期的进程间通信手段进行了系统的改进和扩充,形成了“system V IPC”,通信进程局限在单个计算机内;

后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制。

Linux则把两者继承了下来

早期UNIX进程间通信

基于System V进程间通信

基于Socket进程间通信

POSIX进程间通信。

UNIX进程间通信方式包括:管道、FIFO、信号。

System V进程间通信方式包括:System V消息队列、System V信号灯、System V共享内存

POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存。

由于Unix版本的多样性,电子电气工程协会(IEEE)开发了一个独立的Unix标准,这个新的ANSI Unix标准被称为计算机环境的可移植性操作系统界面(PSOIX)。现有大部分Unix和流行版本都是遵循POSIX标准的,而Linux从一开始就遵循POSIX标准;

BSD并不是没有涉足单机内的进程间通信(socket本身就可以用于单机内的进程间通信)。事实上,很多Unix版本的单机IPC留有BSD的痕迹,如4.4BSD支持的匿名内存映射、4.3+BSD对可靠信号语义的实现等等。

linux使用的进程间通信方式

管道(pipe),流管道(s_pipe)和有名管道(FIFO)

信号(signal)

消息队列

共享内存

信号轿清量

套接字(socket)

管道( pipe )

管道这种通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

流管道s_pipe: 去除了之一种限制,可以双向传输.

管道可用于具有亲缘关系进程间的通信,命名管道:name_pipe克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

信号量( semophore )

信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该扮帆羡函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);

消息队列( message queue )

消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

信号 ( singal )

信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

主要作为进程间以及同一进程不同线程之间的同步手段。

共享内存( shared memory )

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

套接字( socket )

套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信

更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

进程间通信各种方式效率比较

类型

无连接

可靠

流控制

记录消息类型

优先级

普通PIPE    N    Y    YN    

流PIPE    N    Y    YN    

命名PIPE(FIFO)    N    Y    YN    

消息队列    N    Y    YY    

信号量    N    Y    YY    

共享存储    N    Y    YY    

UNIX流SOCKET    N    Y    YN    

UNIX数据包SOCKET    Y    Y    NN    

注:无连接: 指无需调用某种形式的OPEN,就有发送消息的能力流控制:

如果系统资源短缺或者不能接收更多消息,则发送进程能进行流量控制

各种通信方式的比较和优缺点

管道:速度慢,容量有限,只有父子进程能通讯

FIFO:任何进程间都能通讯,但速度慢

消息队列:容量受到系统限制,且要注意之一次读的时候,要考虑上一次没有读完数据的问题

信号量:不能传递复杂消息,只能用来同步

共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

如果用户传递的信息较少或是需要通过信号来触发某些行为.前文提到的软中断信号机制不失为一种简捷有效的进程间通信方式.

但若是进程间要求传递的信息量比较大或者进程间存在交换数据的要求,那就需要考虑别的通信方式了。

无名管道简单方便.但局限于单向通信的工作方式.并且只能在创建它的进程及其子孙进程之间实现管道的共享:

有名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,使用不当容易出错.所以普通用户一般不建议使用。

消息缓冲可以不再局限于父子进程,而允许任意进程通过共享消息队列来实现进程间通信,并由系统调用函数来实现消息发送和接收之间的同步,从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用方便,但是信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合。

共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。

但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的,因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中,所以只能由处于同一个计算机系统中的诸进程共享。不方便网络通信。

共享内存块提供了在任意数量的进程之间进行高效双向通信的机制。每个使用者都可以读取写入数据,但是所有程序之间必须达成并遵守一定的协议,以防止诸如在读取信息之前覆写内存空间等竞争状态的出现。

不幸的是,Linux无法严格保证提供对共享内存块的独占访问,甚至是在您通过使用IPC_PRIVATE创建新的共享内存块的时候也不能保证访问的独占性。 同时,多个使用共享内存块的进程之间必须协调使用同一个键值。

linux中关于流式套接字编程代码的解释,求大神把每行代码加上注释,具体一点

楼上写的真不少,30-分值的送他,辛苦了。

服务器端

#include 

#include 

#include 

#include 

#include 

#include 

#include 

 

int main(int argc,char* argv)

{

int server_sockfd,client_sockfd;

int server_len,client_len;

struct sockaddr_in server_address;

struct sockaddr_in client_address;

//获得一个socket文件描述符,使用tcp ip 协议 

server_sockfd=socket(AF_INET,SOCK_STREAM,0);

server_address.sin_family=AF_INET;

//设置服务器端接岩困受任何主机的请求

server_address.sin_addr.s_addr=htonl(INADDR_ANY);

//使用端口9734 来接受请求

server_address.sin_port=htons(9734);

server_len=sizeof(server_address);

//绑定socket 

bind(server_sockfd,(struct sockaddr *)&server_address,server_len);

 

 //监听连接请求,连接请求队列 大小为 5 

listen(server_sockfd,5);

 

while(1)

{

char ch;

printf(“server waiting\n”);

 

client_len=sizeof(client_address);

 

 //接受client 端的连接请求,如果没有连接请求,该进程将阻塞

client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);

//读数据

read(client_sockfd,&ch,1);

ch++;

//写数据

write(client_sockfd,&ch,1);

//关闭与client端的连接

close(client_sockfd);

 

}

}

客户端

#include 

#include 

#include 

#include 

#include 

#include 

#include 

 

int main(int argc,char* argv)

{

int sockfd;

int len;

struct 哪蠢sockaddr_in address;

int result;

char ch=’A’;

 

 //设置需要连接服务器的 ip port 

sockfd=socket(AF_INET,SOCK_STREAM,0);

address.sin_family=AF_INET;

address.sin_addr.s_addr=inet_addr(“127.0.0.1”);

address.sin_port=htons(9734);

len=sizeof(address);

 

 //连接服务器

result=connect(sockfd,(struct sockaddr *)&address,len);

if(result==-1)

{

perror(“oops:client3”);

exit(1);

}

 

 //写数据

write(sockfd,&ch,1);

//读数据

read(sockfd,&ch,1);

printf(“char from server=%c\n”,ch);

//关李枣陪闭与服务器的连接

close(sockfd);

exit(0);

 

}

关于你的问题:

1.简单的方法,设置socket套接字为非阻塞模式,然后轮询,并查看是否超时。

或者是使用select poll 等方法 ,设置超时时间。

或者 使用 alarm 信号,都可以。

2..每当接收到一个client 端的连接请求,就开辟一个线程为之服务。

或者使用select poll  epoll等方法,推荐这个。

3.你是指的是处理信号吗?这个有专门的函数 signal 可以处理。

Linux下Socket编程 怎样实现客户端之间互相通信

网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

下面用Socket实现一个windows下的c语言socket通信例子,这里我们客户端传递一个字符串,服务器端进行接收。

【服务器端】

#include “stdafx.h”

#include 

#include 

#include 

#define SERVER_PORT 5208 //侦听端口

void main()

{

    WORD wVersionRequested;

    WSADATA wsaData;

    int ret, nLeft, length;

    SOCKET sListen, sServer; //侦听套接字,连接套接字

    struct sockaddr_in saServer, saClient; //地址信息   

    char *ptr;//用于遍历信息的指针   

    //WinSock初始化

    wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本

    ret=WSAStartup(wVersionRequested, &wsaData);

    if(ret!=0)

    {

printf(“WSAStartup() failed!\n”);

return;

    }

    //创建Socket,使用TCP协议

    sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (sListen == INVALID_SOCKET)

    {

WSACleanup();

printf(“socket() faild!\n”);

return;

    }

    //构建本地地址信息

    saServer.sin_family = AF_INET; //地址家族

    saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序

    saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址

  

    //绑定

    ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));

    if (ret == SOCKET_ERROR)

    {

 模核printf(“bind() faild! code:%d\n”, WSAGetLastError());

closesocket(sListen); //关闭套接字

WSACleanup();

return;

    }

  

    //侦听连接请求

    ret = listen(sListen, 5);

    if (ret == SOCKET_ERROR)

    {

printf(“listen() faild! code:%d\n”, WSAGetLastError());

closesocket(sListen); //关闭套接字

return;

    }

  

    printf(“Waiting for client connecting!\n”);

    printf(“Tips: Ctrl+c to quit!\n”);

    //阻塞等待接受客户端连接

 while(1)//循环监听客户端,永远不停止,所以,在本项目中,我们没有心跳包。

 {

  length = sizeof(saClient);

 散码唤 sServer = accept(sListen, (struct sockaddr *)&saClient, &length);

  if (sServer == INVALID_SOCKET)

  {

   printf(“accept() faild! code:%d\n”, WSAGetLastError());

   closesocket(sListen); //关闭套接字

   WSACleanup();

   return;

  }

  char receiveMessage;

   nLeft = sizeof(receiveMessage);

  ptr = (char *)&receiveMessage;

  while(nLeft>0)

  {

   //接收数据

   ret = recv(sServer, ptr, 5000, 0);

   if (ret == SOCKET_ERROR)

   {

    printf(“recv() failed!\n”);

    return;

   }

   if (ret == 0) //客户端已经关冲凯闭连接

   {

    printf(“Client has closed the connection\n”);

    break;

   }

   nLeft -= ret;

   ptr += ret;

  }  

    printf(“receive message:%s\n”, receiveMessage);//打印我们接收到的消息。

  

 }

  //  closesocket(sListen);

  //  closesocket(sServer);

  //  WSACleanup();

}

【客户端】

#include “stdafx.h”

#include 

#include 

#include 

#define SERVER_PORT 5208 //侦听端口

void main()

{

   WORD wVersionRequested;

   WSADATA wsaData;

   int ret;

   SOCKET sClient; //连接套接字

   struct sockaddr_in saServer; //地址信息

   char *ptr;

   BOOL fSuccess = TRUE;

   //WinSock初始化

   wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本

   ret = WSAStartup(wVersionRequested, &wsaData);

   if(ret!=0)

   {

printf(“WSAStartup() failed!\n”);

return;

   }

   //确认WinSock DLL支持版本2.2

   if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)

   {

WSACleanup();

printf(“Invalid WinSock version!\n”);

return;

   }

   //创建Socket,使用TCP协议

   sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   if (sClient == INVALID_SOCKET)

   {

WSACleanup();

printf(“socket() failed!\n”);

return;

   }

   //构建服务器地址信息

   saServer.sin_family = AF_INET; //地址家族

   saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序

   saServer.sin_addr.S_un.S_addr = inet_addr(“192.168.1.127”);

   //连接服务器

   ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer));

   if (ret == SOCKET_ERROR)

   {

printf(“connect() failed!\n”);

closesocket(sClient); //关闭套接字

WSACleanup();

return;

   }

  

  

   char sendMessage=”hello this is client message!”;

   ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0);

   if (ret == SOCKET_ERROR)

   {

printf(“send() failed!\n”);

   }

   else

printf(“client info has been sent!”);

   closesocket(sClient); //关闭套接字

   WSACleanup();

}

网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该银铅搜Socket实现的。下面用Socket实现一个windows下的c语言socket通信例子,这里我们客户端传递一个字符串,服务器端进行接收。

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

【服务锋历激哗器端】

#include “stdafx.h”

#include

#include

#include

#define SERVER_PORT 5208 //侦听端口

void main()

关于linux套接字通信的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » 深入剖析Linux套接字通信技术 (linux套接字通信)