Preview


基于上一篇博客,本文将继续展开TCP面向连接的,客户端以及服务端各自需要进行的操作,我们按照真实TCP连接的顺序,分别阐述客户端socket(), connect()以及服务端socket(), bind(), listen(), accept()建立连接的过程。连接建立之后,阐述send(), recv()的具体细节。

Create Socket


UNIX系统万物皆文件的思想,引入了重要的文件描述符概念,详情可以阅读CS:APP的UNIX I/O章节。简单类比,可以将文件描述符看作一个指针数组的index,指针数组指向的内容与文件相关。

在socket编程中,有两种方式创建新的套接字并获取对应的文件描述符,socket()以及accept(),本章节主要介绍socket()

#include <sys/types.h>
#include <sys/socket.h> int socket(int domain, int type, int protocol);

可以理解,创立一个套接字,必须要获得协议相关内容,例如指明TCP/IP协议。

本博客主要针对TCP,所以以此陈述。

相应的,domain就代表连接使用的是IPv4还是IPv6。那么type就对应的是SOCK_STREAM。protocol就需要是知名是tcp还是UDP(其实type等同于TCP/UDP不太精准,只是说TCP是基于SOCK_STREAM),这个可以利用getprotobyname()函数获取。

事实上,socket的三个参数我们是利用getaddrinfo()获取的关于addrinfo链表写入的(真就工具人呗)

  • domain: ai_family
  • type: ai_socktype
  • protocol: ai_protocol

关于domain,这里又有一段历史...

domain is PF_INET or PF_INET6

This PF_INET thing is a close relative of the AF_INET that you can use when initializing the sin_family field in your struct sockaddr_in. In fact, they’re so closely related that they actually have the same value, and many programmers will call socket() and pass AF_INET as the first argument instead of PF_INET. Now, get some milk and cookies, because it’s time for a story. Once upon a time, a long time ago, it was thought that maybe an address family (what the “AF” in “AF_INET” stands for) might support several protocols that were referred to by their protocol family (what the “PF” in “PF_INET” stands for). That didn’t happen. And they all lived happily ever after, The End. So the most correct thing to do is to use AF_INET in your struct sockaddr_in and PF_INET in your call to socket().

Client


先说简单而无脑的客户端,TCP的3次握手总得有人先握手,connect()便是开启握手过程的函数

connect()


开始和人打招呼,得先知道别人在哪,对应互联网就是套接字地址,利用上一篇博客的内容就可以轻松愉快的获得了。

深入这些之后就发现,逐步和计网的课正在结合起来,getaddrinfo()有些类似于DNS域名解析,connect()就类似于开始握手。

#include <sys/types.h>
#include <sys/socket.h> int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

函数的参数是好理解的,你需告知系统,是哪个套接字,去找谁,开启连接,此处需要addrlen(),即sizeof( *serv_addr),应该是函数内部具有更多细节。

这里就可以给出结合socket(), connect()客户端发起连接的一系列准备工作了

struct addrinfo hints, *res;
int sockfd; memset(&hints, 0, sizeof(hints));
hints.ai_family= AF_UNSPEC;
hints.ai_socketype= SOCK_STREAM; getaddrinfo("www.example.com", "3490", &hints, &res); sockfd= socket(res->ai_family, res->ai_socktype, res->ai_protocol); connect(sockfd, res->ai_addr, res->ai_addrlen);

Server


服务器的活就多了,因为需要考虑让很多人来连接,所以需要固定端口号(bind()), 默认套接字打开是用来找别人的(CS:APP话来说,主动套接字),需要改编为可以监听别人进来的数据(listen()),接受以后,计网知识对应的,需打开连接套接字(accept())。

bind()


在前面客户端部分未陈述,事实上,内核对于套接字的端口开始是随便分的,排除已经使用的,以及周知端口随机分配。

#include <sys/types.h>
#include <sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

很简单的参数设置,和哪个套接字bind(), 把这个套接字bind()上的地址,还有也许出于函数设置的addrlen: sizeof(*my_addr)

先看看老派的做法:

int sockfd;
struct sockaddr_in my_addr; sockfd= socket(PF_INET, SOCK_STREAM, 0); my_addr.sin_family= AF_INET;
inet_pton(AF_INET, "10.12.110.57", &(my_addr.sin_addr));
// actually older way is my_addr.sin_addr.s_addr=inet_addr("10.12.110.57");
// or my_addr.sin_addr.s_addr= INADDR_ANY;
my_addr.sin_port= htons(MYPORT);
memset(my_addr.sin_zero, 0, sizeof(my_addr))

还是换工具人上场吧

struct addrinfo hints, *res;
int sockfd; memset(&hints, 0, sizeof(hints));
hints.ai_family= AF_UNSPEC;
hints.ai_socktype= SOCK_STREAM;
hints.ai_flags= AI_PASSIVE; getaddrinfo(NULL, "3490", &hints, &res); sockfd= socket(res->ai_family, res->ai_socktype, res->ai_protocol); bind(sockfd, res->ai_addr, res->ai_addrlen);

这就将上一篇博客提到的模板连接起来了。

listen()


话不多,上定义

int listen(int sockfd, int backlog)

指定好两件事,让谁监听,最多能处理几个,这就分别对应了sockfd, backlog。

accept()


#include <sys/types.h>
#include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

这里就是涉及到计网的知识了,TCP面向连接时服务器端,是专门利用一个套接字监听,称为监听套接字,再利用fork()(CS:APP异常章节),创建了新的连接套接字来和客户端交互,这样做也好理解,例如一个web应用,总不可能全世界每时每刻就让一个人连接他。

顾名思义,猜测这个函数应该还有发送回去ACK的功能

accept()参数是这样设置的,从哪个监听套接字收到了连接请求?我总得知道这个连接是哪来的?以及老生常谈的addrlen

Easy enough. addr will usually be a pointer to a local struct sockaddr_storage. This is where the information about the incoming connection will go (and with it you can determine which host is calling you from which port). addrlen is a local integer variable that should be set to sizeof(struct sockaddr_storage) before its address is passed to accept(). accept() will not put more than that many bytes into addr. If it puts fewer in, it’ll change the value of addrlen to reflect that.

这里就有细节需要注意,他是记录到sockaddr_storage结构里,前面介绍过,这样IPv4, IPv6通吃,addrlen设置也很有意思,相当于是一个放入地址上限的意思,但是放少了,又会把他改掉。

Communication


前面连接没问题,就开始各种交流吧

这两个函数针对的是stream socket,就是设置了SOCK_STREAM的。

send()


int send(int sockfd, const void *msg, int len, int flags);

你需要通过哪个套接字帮你发送消息(你把待发信息交给他处理)(sockfd)?处理的信息是啥(msg)?发多少(len)?发送姿势是啥(通常为0,遇事不决man一下)?

recv()


int recv(int sockfd, void *buf, int len, int flags);

你想从哪个套接字接受发过来的数据(sockfd)?放到哪(buf)?最多能接受多少(len,注意这里和send()是不同的,这里是最多 可以接受多少信息)?接受姿势是啥(通常也是0)?

Conclusion


至此,你已经可以写一个简单的类似于OICQ之类的玩意了,关于TCP的socket()编程简单介绍就结束了,随后会加上示例代码。

Linux网络编程(2)的更多相关文章

  1. 【深入浅出Linux网络编程】 "开篇 -- 知其然,知其所以然"

    [深入浅出Linux网络编程]是一个连载博客,内容源于本人的工作经验,旨在给读者提供靠谱高效的学习途径,不必在零散的互联网资源中浪费精力,快速的掌握Linux网络编程. 连载包含4篇,会陆续编写发出, ...

  2. 【linux草鞋应用编程系列】_5_ Linux网络编程

    一.网络通信简介   第一部分内容,暂时没法描述,内容实在太多,待后续专门的系列文章.   二.linux网络通信     在linux中继承了Unix下“一切皆文件”的思想, 在linux中要实现网 ...

  3. Linux 网络编程(IO模型)

    针对linux 操作系统的5类IO模型,阻塞式.非阻塞式.多路复用.信号驱动和异步IO进行整理,参考<linux网络编程>及相关网络资料. 阻塞模式 在socket编程(如下图)中调用如下 ...

  4. linux网络编程 no route to host 解决方案

    linux网络编程 no route to host 解决方案 [整合资料] (2013-05-13 21:38:12) 转载▼ 标签: net iptables it 分类: Linux 参考资料h ...

  5. linux网络编程-(socket套接字编程UDP传输)

    今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...

  6. Linux网络编程&内核学习

    c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: ...

  7. linux网络编程_1

    本文属于转载,稍有改动,以利于学习. (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个 ...

  8. Linux网络编程入门 (转载)

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  9. Linux网络编程必看书籍推荐

    首先要说讲述计算机网络和TCP/IP的书很多. 先要学习网络知识才谈得上编程 讲述计算机网络的最经典的当属Andrew S.Tanenbaum的<计算机网络>第五版,这本书难易适中. &l ...

  10. [转] - Linux网络编程 -- 网络知识介绍

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

随机推荐

  1. JavaScript JSON 与 AJAX

    JavaScript JSON 与 AJAX JSON 是一种轻量的数据交互格式,与 AJAX 配合完成前端页面与服务端的信息传递,本文介绍 JSON 的使用.原生 AJAX 写法.JSONP 跨域解 ...

  2. 面试刷题30:SpringBean的生命周期?

    spring是Java软件开发的事实标准. 我是李福春,我在准备面试,今天的问题是:springBean的生命周期是怎样的? 答:spring最基础的能力是IOC(依赖注入),AOP(面向切面编程), ...

  3. C 2010年笔试题

    1 有一个函数, 写一段程序,输入的值,输出的值. #include <stdio.h> void main() { int x,y; printf("输入x:"); ...

  4. [vijos1162]波浪数

    题目链接:https://www.vijos.org/p/1162 这题的解法我觉得可能是模拟吧,但是题的分类又是构造QAQ..... 不是很懂,所以我们把这个方法叫做奇技淫巧吧 这题的暴力思路就是针 ...

  5. Django之queryset API

    1. QuerySet 创建对象的方法 >>> from blog.models import Blog >>> b = Blog(name='Beatles Bl ...

  6. # H - H HDU - 2066 (多起点、多终点问题)

    H - H HDU - 2066 (多源点.多汇点问题) 一个图上,有M条边,Z个出发点,Y个终止点.求一条最短路,其中起点是Z中的任意一点,终点是Y中任意一点. Input 输入数据有多组,输入直到 ...

  7. webpack4.x 从零开始配置vue 项目(三)

    目标 babel 转换ES6 语法 postCss 增强css功能,如自动增加前缀 vue-loader 解析vue 文件 实现基本的vue项目开发环境,打包等 Babel 由于浏览器对es6语法兼容 ...

  8. 微信网页授权,获取微信code,获取access_tocken,获取用户信息

    微信开发中,经常有这样的需求:获得用户头像.绑定微信号给用户发信息.. 那么实现这些的前提就是授权!   1.配置安全回调域名: 在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 ...

  9. PTA数据结构与算法题目集(中文) 7-10

    PTA数据结构与算法题目集(中文)  7-10 7-10 公路村村通 (30 分)   现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低 ...

  10. 从JDK源码学习Hashmap

    这篇文章记录一下hashmap的学习过程,文章并没有涉及hashmap整个源码,只学习一些重要部分,如有表述错误还请在评论区指出~ 1.基本概念 Hashmap采用key算hash映射到具体的valu ...