@

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

国际标准组织(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);

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

  1. Linux Socket 编程简介

    在 TCP/IP 协议中,"IP地址 + TCP或UDP端口号" 可以唯一标识网络通讯中的一个进程,"IP地址+端口号" 就称为 socket.本文以一个简单的 ...

  2. Linux socket 编程中存在的五个隐患

    前言:         Socket API 是网络应用程序开发中实际应用的标准 API.尽管该 API 简单,但是   开发新手可能会经历一些常见的问题.本文识别一些最常见的隐患并向您显示如何避免它 ...

  3. Linux Socket编程

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

  4. Linux Socket编程(不限Linux)【转】

    转自:http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几 ...

  5. Linux Socket编程(不限Linux)

    "一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...

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

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

  7. Windows Socket和Linux Socket编程的区别 ZZ

    socket相关程序从Windows移植到Linux下需要注意的: 1)头文件 Windows下winsock.h/winsock2.h Linux下sys/socket.h 错误处理:errno.h ...

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

    一.socket编程 网络功能是Uinux/Linux的一个重要特点,有着悠久的历史,因此有一个非常固定的编程套路. 基于TCP的网络编程: 基于连接, 在交互过程中, 服务器和客户端要保持连接, 不 ...

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

    "一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...

随机推荐

  1. 洛谷 P1919 A*B Problem升级版

    妈妈我终于会\(A*B\ problem\)啦~~ 题目大意: 给你两个正整数 \(a,b\),求\(a*b\) 其中\(a,b\le 10^{1000000}\) 我们只要把多项式\(A(x)=\s ...

  2. 洛谷 P1950 长方形_NOI导刊2009提高(2)

    传送门 思路 首先定义\(h\)数组,\(h[i][j]\)表示第\(i\)行第\(j\)列最多可以向上延伸多长(直到一个被用过的格子) 然后使用单调栈算出 \(l_i\)和 \(r_i\) ,分别是 ...

  3. windows下sed回车换行符处理

    windows下sed回车换行符处理如果用sed for windows对整个文件进行了编辑,编辑之后一般需要处理回车换行符:rem windows的回车换行符是\r\n,linux的是\n,所以要替 ...

  4. Salesforce 开发整理(十)项目部署总结

    项目部署顺序 全局值集 小组 自定义字段-对象-设置(SF1 紧凑布局要和记录类型在这里要一起部署) 邮件模板-静态资源 角色 工作流-流定义(包含进程生成器) 批准过程 开发部署<Apex类, ...

  5. k8s本地部署

    k8s是什么 Kubernetes是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署.自动扩缩容.维护等功能. Kubernetes 具有如下特点: 便携性: 无论公有云.私有云.混合 ...

  6. 了解jsp,这一篇就够了.

    jsp的执行过程: 1 客户端发出请求. 2 Web容器将JSP转译成Servlet源代码. 3 Web容器将产生的源代码进行编译. 4 Web容器加载编译后的代码并执行. 5 把执行结果响应至客户端 ...

  7. ng 引入query

    ng 引入jquery 1.在项目中 npm install --save jquery 在对应组件中加入 import * as $ from "jquery";   在angu ...

  8. 如何定时查询某线程的CPU执行时间

    #!/bin/bash pid=$(ps -T -p $(pgrep xxx) | grep xxx | gawk -F" " '{print $2}') if [ -z $pid ...

  9. 第五节:EF Core中的三类事务(SaveChanges、DbContextTransaction、TransactionScope)

    一. 说明 EF版本的事务介绍详见: 第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges.DBContextTransaction.TransactionScope). 本节主 ...

  10. PG undo redo

    除了理所当前的各路文本记录(比方数据库的运行报错日志之类),PG的二进制类日志文件主要有两个,一个就是对应传统数据库理论的redo日志,理论上,所有数据的修改操作都会被记录到这个日志,在事务提交的时候 ...