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. 一个js函数算出任意位数的水仙花数

    一个算出任意位数的水仙花数的函数如下: var arr =[]; /*更改num确定取值范围*/ for(var num = 100; num <= 9999;num++){ /*多位数版本*/ ...

  2. ICPC训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019

    I题 求 a 数组平方的前缀和和求 a 数组后缀和,遍历一遍即可 AC代码 #include<iostream>#include<cmath>using namespace s ...

  3. [bzoj1800]fly 飞行棋<暴力>

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1800 说实话我这几天运气不错,随便在bzoj上找题都可以找到水题,这题在代码上没有丝毫难度 ...

  4. 用全站 CDN 部署 Discourse 论坛

    Discourse 介绍 Discourse 是一款由 Stack Overflow 的联合创始人--Jeff Atwood,基于 Ruby on Rails 开发的开源论坛.相较于传统论坛,Disc ...

  5. minIO分布式集群搭建+nginx负载均衡

    暂时关闭防火墙 systemctl stop firewalld 查看防火墙状态 systemctl status firewalld 赋予最高权限 chmod +x minio !/bin/bash ...

  6. CF632(div.2)C. Eugene and an array

    https://codeforces.ml/contest/1333/problem/C 大概题意是规定和为0的数组为不合格数组,询问给定数组中共有多少个合格子数组. 解题 子数组的数量 一个长度为 ...

  7. mysql--使用left join条件查询时加where条件的问题

    场景:为一个活动添加指导文件,每一个活动会对应多种指导文件类型,进入每一个活动的设置指导文件页面后所呈现的指导文件类型相同,查询时,使用指导文件类型表LEFT JOIN指导文件表,要求指导文件类型全部 ...

  8. 搭建DVWA Web渗透测试靶场

    文章更新于:2020-04-13 按照惯例,需要的文件附上链接放在文首. 文件名:DVWA-1.9-2020.zip 文件大小:1.3 M 文件说明:这个是新版 v1.9 (其实是 v1.10开发版) ...

  9. 家庭版记账本app开发进度。开发到现在整个app只剩下关于图表的设计了,具体功能如下

    首先说一下自己的功能: 实现了用户的登录和注册.添加收入记账和添加支出记账.粗略显示每条账单基本情况.通过点击每条账单来显示具体的情况, 之后就是退出当前用户的操作. 具体的页面情况如下: 这就是整个 ...

  10. (js描述的)数据结构[字典](7)

    (js描述的)数据结构[字典](7) 一.字典的特点 1.字典的主要特点是一一对应关系. 2.使用字典,剋通过key取出对应的value值. 3.字典中的key是不允许重复的,而value值是可以重复 ...