《深入理解计算机系统之套接字(socket)》

本文详细介绍了计算机系统中的套接字接口,包括其作为通信端点的角色、地址结构、转换方法以及bind、listen、accept等关键函数的使用。通过示例解释了如何在客户端和服务器之间建立连接,以及数据的读写流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                                                                                                                      套接字接口概述

1.套接字接口是一组函数(如socket, connect, bind, listen, accept函数),他们和Unix I/O函数(最常用rio_writen, rio_readlineb)结合起来,用以创建网络应用。

2.每个套接字都是通信的一个端点,从unix程序的角度来看,套接字就是一个有相应描述符的打开文件。

3.套接字的地址结构: 

    1)IP地址(放在一个结构中)

     struct  in_addr
     {
     unsigned int s_addr;   /* 32-bit IPv4 address */
     };
    2)套接字地址通用结构(Linux系统的套接字可以支持多种协议,每种 不同的协议都是用 不同的地址结构。在头文件<linux/socket.h>中有一个这样的老式结构(现在一般不使用这个结构了))
      struct sockaddr{
         unsigned  short  sa_family;  //protocal family
         char             sa_data[14]; //address data
      };
    3)因特网套接字地址结构( 现在一般使用这个sockaddr_in结构(用来设置/获取地址信息))     
      struct sockaddr_in{
         unsigned  short  sin_family;  
         unsigned  short  sin_port;   //port number in network byte order
         struct in_addr   sin_addr;   //IP address in network byte order
         unsigned  char   sin_zero[8];//为了让sockaddr和sockaddr_in的数据结构保持相同的大小而保留的空字节
      };

        connect, bind, accept函数要求一个指向与协议相关的套接字地址结构的指针,因此需要对各种类型的套接字地址结构进行强制类型转换,把应用程序与协议特定的结构的指针转换成通用结构:

        如 typedef  struct sockaddr  SA;

             struct  sockaddr_in  serveraddr;   //非通用结构的套接字地址结构

             (SA*) serveraddr;                             // 强制类型转换


4.套接字地址结构的传递

   套接字地址结构的传递有两种方式,取决于传递的方向:一是从进程到内核,二是从内核到进程。

   1)从进程到内核传递套接字地址结构的函数有三个:bind,connect,sendto。这几个函数中都包含一个指向套接字地址结构的指针一个该结构的整数大小,如:

     struct sockaddr_in serv;                   //<strong>服务器</strong>套接字地址结构
     //fill in serv{}
     connect(sockfd,(struct sockaddr*)&serv, sizeof(serv));    //用来建立和服务器的连接,sorkfd是有socket函数产生的<strong>客户端描述符</strong>,即sockfd=socket(AE_INET, SOCK_STREAM, 0);
   2) 从内核到进程
      没看到,暂略。
 

5.以上结构的应用

  struct sockaddr_in mysock;
  mysock.sin_family = AF_INET;      //<strong>TCP</strong>地址结构
  mysock.sin_port = htons(3490);    //字节顺序转换函数(主机字节顺序变为网络字节顺序(大端模式))
  mysock.sin_addr.s_addr = inet_addr("166.111.160.10");//设置IP地址
  bzero(&(mysock.sin_zero),8);//设置sin_zero为8位保留字节
  //如果mysock.sin_addr.s_addr = INADDR_ANY,则不指定IP地址(用于server程序)

6.bind、listen和accept被服务器用来和客户端建立连接。

   #include<sys/socket.h>

   int bind(int sockfd, struct sockaddr *my_addr, int addrlen)  //返回值:若成功则为0,出错则为-1

   该函数告诉内核将my_addr中的服务器套接字地址和套接字描述符sockfd(客户端描述符,它存在于一个连接的客户端,也称为主动套接字——客户端是发起连接请求的主动实体)联系起来,故名思议,bind——把服务器套接字地址和客户端描述符联系起来~~

  默认情况下,内核会认为socket函数创建的描述符对应于主动套接字,但实际上服务器也有socket函数,是被动套接字,我们不想让内核误会,所以用 listen函数将sockfd从一个主动套接字转化为一个监听套接字(服务器端的套接字),该套接字可以接受来自客户端的连接请求,这是符合逻辑的(默认情况的并不总是好的,因此需要修改成正确的情况~)。

   accept函数(由服务器调用)等待来自客户端的请求;

   #include<sys/socket.h>

   int accept(int listenfd, struct sockaddr *addr, int *addrlen)     //listenfd是监听套接字(监听描述符)。

   accept函数等待来自客户端的连接请求到达监听描述符listenfd,然后在addr中填写客户端的套接字地址(文件嘛,读了就要写进去),并返回一个已连接描述符,这个描述符可被用来利用Unix I/O函数与客户端通信。


7.合并函数

  就像文章开头我们看到的那样,1)客户端把socket和connect函数结合成open_clientfd函数,该函数运行在主机hostname上的服务器建立一个连接,并在知名端口port(在客户端?)上监听连接请求。客户端用它来和服务器建立连接~

 它返回的是客户端的套接字描述符,此描述符准备好了,可以用Unix I/O函数做输入输出。

 函数原型如下:

  #include"csapp.h"

  int  open_clientfd(char *hostname, int port);

  2)

  服务器端将socket、bind和listen函数结合成一个叫做open——listenfd的函数,服务器用它来创建一个监听描述符,这个描述符准备好在知名端口port上接收连接请求

  函数原型:

   #include"csapp.h"
  int   open_listenfd( int port);

  二者成功后返回的都是描述符,只不过一个(clientfd)在客户端(准备用Unix I/O函数做输入输出),一个(listenfd)在服务器,监听来自客户端的请求。而对监听的请求作出回应的是服务器的accept函数(返回一个连接描述符connfd)。


8)accept之后,就是调用一系列Unix I/O函数(如图中所示)在客户端和服务器通过读写clientfd和connfd(不是listenfd)来回传送数据了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值