linux socket编程系统调用栈

目录

  • 一、网络协议参考模型简介
  • 二、SOCKET概述
  • 三、SOCKET基本数据结构
    • 1、TCP通信编程
    • 2、服务器端实例代码
    • 3、客户端实例代码
    • 4、头文件socketwrapper.h
    • 5、程序实现功能
    • 6、探究socket系统调用

@(linux socket编程实现原理)

一、网络协议参考模型简介

国际标准组织(ISO)制定了OSI模型。这个模型把网络通信的工作分为7层,从上至下为应用层、表示层、会话层、
传输层、网络层、数据链路层、物理层。
而TCP/IP协议将OSI的7层模型简化为4层,从上至下分别为应用层、传输层、网络互连层(网际层)、网络接口层。
TCP/IP协议实际是一个协议族,为网际数据通信提供不同层次的通路。
TCP协议处于传输层,实现了从一个应用程序到另一个应用程序的数据传递。应用程序通过目的IP地址和端口号
来区分接收数据的不同应用程序。

二、SOCKET概述

socket接口是TCP/IP网络的API,它定义了许多函数或例程,程序员可以使用它来开发TCP/IP网络上的应用程序,
要学习Internet上的TCP/IP网络编程,必须理解socket接口。socket接口设计者最先将接口放在UNIX操作系统中。
如果了解UNIX系统的输入输出的话,就很容易理解socket了。网络的socket数据传输是一种特殊的I/O,socket
也是一种文件描述符。

三、SOCKET基本数据结构

struct sockaddr {
/* address family, AF_xxx;一般为AF_INET,代表Internet(TCP/IP)地址族的IPv4协议   */
sa_family_t   sa_family;
/* 14 bytes of protocol address;包含了远程计算机的IP地址、端口号和套接字的数目,这些数据是混杂在一起的*/
char   sa_data[14];
};

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__   16      /* sizeof(struct sockaddr)  */

struct sockaddr_in {
sa_family_t     sin_family; /* Address family       */
__be16      sin_port;   /* Port number          */
 struct in_addr sin_addr;   /* Internet address     */
 /* Pad to size of `struct sockaddr'. */
 unsigned char  __pad[__SOCK_SIZE__ - sizeof(short int) -
        sizeof(unsigned short int) - sizeof(struct in_addr)];
};

1、TCP通信编程

    前面简单介绍了TCP/IP协议,事实上该协议是十分复杂的(要是一个人从头开始研究网络协议的话,这辈子可能
    啥也没干就过去了)。要编写一个优秀的网络程序也是十分困难的。服务端与客户端TCP通信的过程大致如下:
    服务器端:
     1. 首先服务器启动,通过调用socket()建立一个套接字
     2. 调用bind()将该套接字和本地网络地址联系在一起
     3. 调用listen()使套接字做好侦听的准备,并规定它的请求队列的长度
     4. 调用accept()来接收连接
    客户端:
     1. 通过调用socket()建立一个套接字
     2. 调用connect()和服务器建立连接。
     3. 连接一旦建立,客户机和服务器之间就可以通过调用read和write函数来发送和接收数据了
     4. 最后,待数据传送结束之后,双方调用close()关闭套接字,通信关闭。

2、服务器端实例代码

#include <stdio.h>          /* perror */
#include <stdlib.h>         /* exit */
#include <sys/types.h>      /* WNOHANG */
#include <sys/wait.h>       /* waitpid */
#include <string.h>         /* memset */

#include "socketwrapper.h"  /* socket layer wrapper */

#define true        1
#define false       0

#define MYPORT      12334                /* 监听的端口 */
#define BACKLOG     10                  /* listen的请求接收队列长度 */
#define MAXSIZE     100
int main()
{
    int numbytes=0;
    int sockfd, new_fd;            /* 监听端口,数据端口 */
    struct sockaddr_in sa;         /* 自身的地址信息 */
    struct sockaddr_in client_addr; /* 连接对方的地址信息 */
    int sin_size;
    char buf[MAXSIZE];
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
        {
            perror("socket");
            exit(1);
        }
    sa.sin_family = AF_INET;
    sa.sin_port = Htons(MYPORT);         /* 网络字节顺序 */
    sa.sin_addr.s_addr = INADDR_ANY;     /* 自动填本机IP */
    memset(&(sa.sin_zero),0, 8);            /* 其余部分置0 */

    if ( Bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
        {
            perror("bind");
            exit(1);
        }
    if (Listen(sockfd, BACKLOG) == -1)
        {
            perror("listen");
            exit(1);
        }
    sin_size = sizeof(struct sockaddr_in);
    new_fd = Accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
    if (new_fd == -1)
        {
            perror("accept");
            exit(1);
        }
    printf("Got connection from %s\n", Inet_ntoa(client_addr.sin_addr));
    /* 主循环 */
    while(1)
        {
                if ((numbytes=recv(new_fd,buf,MAXSIZE,0))>0)
                {
                    buf[numbytes]=0;
                    printf("received:%s   &   length=%d\n",buf,numbytes);
                    if(send(new_fd,buf,strlen(buf), 0) == -1)
                    perror("send");
                }
        }
    return true;
}

3、客户端实例代码

#include <stdio.h>          /* perror */
#include <stdlib.h>         /* exit */
#include <sys/types.h>      /* WNOHANG */
#include <sys/wait.h>       /* waitpid */
#include <string.h>         /* memset */
#include "socketwrapper.h"  /* socket layer wrapper */

#define true        1
#define false       0

#define PORT        12334   /* Server的端口 */
#define MAXDATASIZE 100     /*一次可以读的最大字节数 */

int main(int argc, char *argv[])
{
    int sockfd, numbytes;
    char buf[MAXDATASIZE];
    struct hostent *he; /* 主机信息 */
    struct sockaddr_in server_addr; /* 对方地址信息 */
    if (argc != 2)
        {
            fprintf(stderr,"usage: client hostname\n");
            exit(1);
        }
    /* get the host info */
    if ((he=Gethostbyname(argv[1])) == NULL)
        {
            /* 注意:获取DNS信息时,显示出错需要用herror而不是perror */
            /* herror 在新的版本中会出现警告,已经建议不要使用了 */
            perror("gethostbyname");
            exit(1);
        }
    if ((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1)
        {
            perror("socket");
            exit(1);
        }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = Htons(PORT); /* short, NBO */
    server_addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]);
    memset(&(server_addr.sin_zero),0, 8); /* 其余部分设成0 */

    if (Connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
        {
            perror("connect");
            exit(1);
        }

    while(1)
    {
      printf("Enter Something:");
      scanf("%s",buf);
      numbytes=send(sockfd,buf,strlen(buf),0);
      numbytes=recv(sockfd,buf,MAXDATASIZE,0);
      buf[numbytes]='\0';
      printf("received:%s\n",buf);
    }
    Close(sockfd);
    return true;

}

4、头文件socketwrapper.h

/********************************************************************/
/* Copyright (C) SSE-USTC, 2010                                     */
/*                                                                  */
/*  FILE NAME             :  socketwraper.h                         */
/*  PRINCIPAL AUTHOR      :  Mengning                               */
/*  SUBSYSTEM NAME        :  ChatSys                                */
/*  MODULE NAME           :  ChatSys                                */
/*  LANGUAGE              :  C                                      */
/*  TARGET ENVIRONMENT    :  ANY                                    */
/*  DATE OF FIRST RELEASE :  2010/10/18                             */
/*  DESCRIPTION           :  the interface to socket layer.         */
/********************************************************************/

/*
 * Revision log:
 *
 * Created by Mengning,2010/10/18
 *
 */

#ifndef _SOCKET_WRAPER_H_
#define _SOCKET_WRAPER_H_

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netdb.h>  /* gethostbyname */

/* ChatSys Socket  - Standard Socket Call Mapping Definition */
#define  Socket(x,y,z)           socket(x,y,z)
#define  Bind(x,y,z)             bind(x,y,z)
#define  Connect(x,y,z)          connect(x,y,z)
#define  Listen(x,y)             listen(x,y)
#define  Read(x,y,z )            read(x,y,z)
#define  Accept(x,y,z )          accept(x,y,(socklen_t *)z)
#define  Recv(w,x,y,z)           recv(w,x,y,z)
#define  Recvfrom(a,b,c,d,e,f)   recvfrom(a,b,c,d,e,f )
#define  Recvmsg(a,b,c)          recvmsg(a,b,c)
#define  Write(a,b,c)            write(a,b,c)
#define  Send(a,b,c,d)           send(a,b,c,d)
#define  Sendto(a,b,c,d,e,f)     sendto(a,b,c,d,e,f)
#define  Sendmsg(a,b,c)          sendmsg(a,b,c)
#define  Close(a)                close(a) 

#define  Htons(a)                htons(a)
#define  Inet_ntoa(a)            inet_ntoa(a)

/* Name */
#define  Gethostbyname(a)        gethostbyname(a)
#endif /* _SOCKET_WRAPER_H_ */

5、程序实现功能

首先运行server,然后运行client。连接建立之后,client通过键盘输入字符,发送到server。
server接收到数据之后,直接返回给client。验证通信是否成功。

实现效果

client第一次发送hello,第二次发送nice

server接收到数据

6、探究socket系统调用

  1. SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
                {
                    int retval;
                    struct socket *sock;
                    int flags;
                    /* Check the SOCK_* constants for consistency.  */
                    BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
                    BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
                    BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
                    BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);
    
    flags = type & ~SOCK_TYPE_MASK;
    if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
        return -EINVAL;
    type &= SOCK_TYPE_MASK;
    
    if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
        flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
    
    retval = sock_create(family, type, protocol, &sock);/*重要函数*/
    if (retval < 0)
        goto out;
    
    retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));/*重要函数*/
    if (retval < 0)
        goto out_release;
    
    out:
        /* It may be already another descriptor 8) Not kernel problem. */
        return retval;
    
    out_release:
        sock_release(sock);
        return retval;
    }

    可以看出 SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)代码中主要是使用了
    sock_create()和sock_map_fd()这两个函数。那么接下来先分析sock_create()

  2. int sock_create(int family, int type, int protocol, struct socket **res)
    {
        return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
    }

    可以看出sock_create()实际调用__sock_create(),继续递归

  3. int __sock_create(struct net *net, int family, int type, int protocol,
                 struct socket **res, int kern)
    {
        int err;
        struct socket *sock;
        const struct net_proto_family *pf;
    /*
     *      Check protocol is in range
     */
    if (family < 0 || family >= NPROTO)
        return -EAFNOSUPPORT;
    if (type < 0 || type >= SOCK_MAX)
            return -EINVAL;
    
    /* Compatibility.
    
       This uglymoron is moved from INET layer to here to avoid
       deadlock in module load.
     */
    if (family == PF_INET && type == SOCK_PACKET) {
        static int warned;
        if (!warned) {
            warned = 1;
            printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",
                   current->comm);
        }
        family = PF_PACKET;
    }
    
    err = security_socket_create(family, type, protocol, kern);
    if (err)
        return err;
    
    /*
     *  Allocate the socket and allow the family to set things up. if
     *  the protocol is 0, the family is instructed to select an appropriate
     *  default.
     */
    sock = sock_alloc();/*重要*/
    if (!sock) {
        if (net_ratelimit())
            printk(KERN_WARNING "socket: no more sockets\n");
        return -ENFILE; /* Not exactly a match, but its the
                   closest posix thing */
    }
    
    sock->type = type;
    
    #ifdef CONFIG_MODULES
        /* Attempt to load a protocol module if the find failed.
         *
         * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user
         * requested real, full-featured networking support upon configuration.
         * Otherwise module support will break!
         */
        if (rcu_access_pointer(net_families[family]) == NULL)
            request_module("net-pf-%d", family);
    #endif
    
    rcu_read_lock();
    pf = rcu_dereference(net_families[family]);
    err = -EAFNOSUPPORT;
    if (!pf)
        goto out_release;
    
    /*
     * We will call the ->create function, that possibly is in a loadable
     * module, so we have to bump that loadable module refcnt first.
     */
    if (!try_module_get(pf->owner))
        goto out_release;
    
    /* Now protected by module ref count */
    rcu_read_unlock();
    
    err = pf->create(net, sock, protocol, kern);/*重要*/
    if (err < 0)
        goto out_module_put;
    
    /*
     * Now to bump the refcnt of the [loadable] module that owns this
     * socket at sock_release time we decrement its refcnt.
     */
    if (!try_module_get(sock->ops->owner))
        goto out_module_busy;
    
    /*
     * Now that we're done with the ->create function, the [loadable]
     * module can have its refcnt decremented
     */
    module_put(pf->owner);
    err = security_socket_post_create(sock, family, type, protocol, kern);
    if (err)
        goto out_sock_release;
    *res = sock;
    
        return 0;
    
    out_module_busy:
        err = -EAFNOSUPPORT;
    out_module_put:
        sock->ops = NULL;
        module_put(pf->owner);
    out_sock_release:
        sock_release(sock);
        return err;
    
    out_release:
        rcu_read_unlock();
        goto out_sock_release;
    }

    其中重要的函数有sock_alloc()和pf->create(net, sock, protocol, kern)函数,递归查看sock_alloc()

  4.     static struct socket *sock_alloc(void)
    {
        struct inode *inode;
        struct socket *sock;
    
        inode = new_inode(sock_mnt->mnt_sb);
        if (!inode)
            return NULL;
    
    sock = SOCKET_I(inode);
    
    kmemcheck_annotate_bitfield(sock, type);
    inode->i_ino = get_next_ino();
    inode->i_mode = S_IFSOCK | S_IRWXUGO;
    inode->i_uid = current_fsuid();
    inode->i_gid = current_fsgid();
    
    percpu_add(sockets_in_use, 1);
    return sock;
    }
  5. struct inode *new_inode(struct super_block *sb)
    {
        struct inode *inode;
    
        spin_lock_prefetch(&inode_sb_list_lock);
    
        inode = alloc_inode(sb);
        if (inode) {
            spin_lock(&inode->i_lock);
            inode->i_state = 0;
            spin_unlock(&inode->i_lock);
            inode_sb_list_add(inode);
        }
        return inode;
    }

    到这里已经快结束了。大致的系统调用栈也已经清晰,最终调用 inode = alloc_inode(sb);

原文地址:https://www.cnblogs.com/ustc-hwq/p/12015943.html

时间: 2024-10-20 03:05:12

linux socket编程系统调用栈的相关文章

Linux socket编程的心跳机制总结

Linux socket编程的心跳机制总结 我写这篇文章的目的是想总结一下心跳机制的使用,因为最近两个项目的TCP通信中都使用了这个方法,感觉用法好诗比较经典的,所以拿出来与大家共享. 什么是心跳机制 心跳机制就是当客户端与服务端建立连接后,每隔几分钟发送一个固定消息给服务端,服务端收到后回复一个固定消息给客户端,如果服务端几分钟内没有收到客户端消息,则视客户端断开.发送方可以是客户端和服务端,看具体需求. 为什么要使用 我们都知道在TCP这种长连接情况下下,有可能有一大段时间是没有数据往来的,

Linux socket编程 DNS查询IP地址

本来是一次计算机网络的实验,但是还没有完全写好,DNS的响应请求报文的冗余信息太多了,不只有IP地址.所以这次的实验主要就是解析DNS报文.同时也需要正确的填充请求报文.如果代码有什么bug,欢迎指正啊.代码排版有点乱... 本文有以下内容 DNS报文的填充和解析 利用socket API传输信息 一.填充DNS请求报文 随便百度一下,就可以知道DNS报文的格式.所以这里只介绍如何填充DNS报文. 首先是填充报文首部: ? 1 2 3 4 5 6 7 8 9 /* 填充首部的格式大致相同,下面的

Linux Socket编程

Linux Socket编程 一.Socket编程具体函数解析参考网址 http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html(本文转载于此网址,转载请注明源链接) 二.Socket编程的基本知识 2.1 基本TCP客户/服务器程序套接字函数 图2.1 TCP服务器/客户端响应方式 2.2 TCP三次握手 图2.2 TCP 三次握手建立连接 2.3 TCP断开连接 图2.3 TCP 四次握手断开连接 三.Socket编程细节知识

Linux Socket编程-(转自吴秦(Tyler))

"一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的.本文的主要内容如下: 1.网络中进程之间如何通信?

Linux Socket编程(不限Linux)

话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的.本文的主要内容如下: 1.网络中进程之间如何通信? 2.Socket是什么? 3.so

Windows Socket和Linux Socket编程的区别 ZZ

socket相关程序从Windows移植到Linux下需要注意的: 1)头文件 Windows下winsock.h/winsock2.h Linux下sys/socket.h 错误处理:errno.h 2)初始化 Windows下需要用WSAStartup Linux下不需要 3)关闭socket Windows下closesocket(...) Linux下close(...) 4)类型 Windows下SOCKET Linux下int 如我用到的一些宏: #ifdef WIN32 typed

【转】Windows Socket和Linux Socket编程有什么区别

socket相关程序从Windows移植到Linux下需要注意的: 1)头文件 Windows下winsock.h/winsock2.h Linux下sys/socket.h 错误处理:errno.h 2)初始化 Windows下需要用WSAStartup Linux下不需要 3)关闭socket Windows下closesocket(...) Linux下close(...) 4)类型 Windows下SOCKET Linux下int 如我用到的一些宏: #ifdef WIN32 typed

Socket详解-Linux Socket编程(不限Linux)

"一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的.本文的主要内容如下: 1.网络中进程之间如何通信?

Linux socket编程示例(最简单的TCP和UDP两个例子)

一.socket编程 网络功能是Uinux/Linux的一个重要特点,有着悠久的历史,因此有一个非常固定的编程套路. 基于TCP的网络编程: 基于连接, 在交互过程中, 服务器和客户端要保持连接, 不能断开.重发一切出错数据.数据验证, 保证数据的正确性.完整性和顺序性, 缺点是消耗的资源比较大. 基于UDP的网络编程: 无连接协议, 在网络交互过程中不保持连接, 只需要在发送数据时连接一下, 不重发.验证数据.优点是资源消耗少, 数据的可靠性完整性 顺序性得不到保证. 二.编程步骤: 服务器: