Linux网络编程(2)
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,这里又有一段历史...
domainisPF_INETorPF_INET6This
PF_INETthing is a close relative of theAF_INETthat you can use when initializing thesin_familyfield in yourstruct sockaddr_in. In fact, they’re so closely related that they actually have the same value, and many programmers will callsocket()and passAF_INETas the first argument instead ofPF_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 useAF_INETin yourstruct sockaddr_inandPF_INETin your call tosocket().
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.
addrwill usually be a pointer to a localstruct 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).addrlenis a local integer variable that should be set tosizeof(struct sockaddr_storage)before its address is passed toaccept().accept()will not put more than that many bytes intoaddr. If it puts fewer in, it’ll change the value ofaddrlento 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)的更多相关文章
- 【深入浅出Linux网络编程】 "开篇 -- 知其然,知其所以然"
[深入浅出Linux网络编程]是一个连载博客,内容源于本人的工作经验,旨在给读者提供靠谱高效的学习途径,不必在零散的互联网资源中浪费精力,快速的掌握Linux网络编程. 连载包含4篇,会陆续编写发出, ...
- 【linux草鞋应用编程系列】_5_ Linux网络编程
一.网络通信简介 第一部分内容,暂时没法描述,内容实在太多,待后续专门的系列文章. 二.linux网络通信 在linux中继承了Unix下“一切皆文件”的思想, 在linux中要实现网 ...
- Linux 网络编程(IO模型)
针对linux 操作系统的5类IO模型,阻塞式.非阻塞式.多路复用.信号驱动和异步IO进行整理,参考<linux网络编程>及相关网络资料. 阻塞模式 在socket编程(如下图)中调用如下 ...
- linux网络编程 no route to host 解决方案
linux网络编程 no route to host 解决方案 [整合资料] (2013-05-13 21:38:12) 转载▼ 标签: net iptables it 分类: Linux 参考资料h ...
- linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- Linux网络编程&内核学习
c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: ...
- linux网络编程_1
本文属于转载,稍有改动,以利于学习. (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个 ...
- Linux网络编程入门 (转载)
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- Linux网络编程必看书籍推荐
首先要说讲述计算机网络和TCP/IP的书很多. 先要学习网络知识才谈得上编程 讲述计算机网络的最经典的当属Andrew S.Tanenbaum的<计算机网络>第五版,这本书难易适中. &l ...
- [转] - Linux网络编程 -- 网络知识介绍
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
随机推荐
- Samba centos7文件共享服务器搭建教程,可以更改任意需求操作配置详解。
先安装软件 yum -y install samba-client 请看如下配置文件说明 [gongxiang] comment = This is my shared folder ...
- NodeMCU入坑指南-低成本打造零舍友闻风丧胆WiFi断网神器
前言 最近对IoT方面比较感兴趣,所以在某宝上入手了一块NodeMCU的开发板,至于为什么我选择这块开发板呢?嘿嘿,当然是因为便宜啊
- netcore webapi参数
1.参数带[FormBody]标签 2.ajax 请求 content-type:application/json 3.post时 需要JSON.stringify 4.GET 时不需要JSON.st ...
- 基于Java的数字货币交易系统的架构设计与开发
前言 无论是股票交易系统,还是数字货币交易系统,都离不开撮合交易引擎,这是交易平台的心脏.同时,一个优秀的架构设计也会让交易平台的运维和持续开发更加容易.本文基于对开源项目的深入研究,总结了数字货币交 ...
- IOS部分APP使用burpsuite抓不到包原因
曾经在ios12的时候,iphone通过安装burpsuite的ca证书并开启授权,还可以抓到包,升级到ios13后部分app又回到以前连上代理就断网的情况. 分析:ios(13)+burpsuite ...
- fiddler详解
一.介绍Fiddler是一个http协议调试工具,能记录并检查电脑和互联网之间的http通讯,设置断点,查看所有的“进出”fiddler的数据(cookie,html,js,css等文件) 通常可从以 ...
- 1051 Pop Sequence (25分)
Given a stack which can keep M numbers at most. Push N numbers in the order of 1, 2, 3, ..., N and p ...
- P1198 [JSOI2008]最大数(线段树基础)
P1198 [JSOI2008]最大数 题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制: ...
- JVM类加载过程详细分析
双亲委派加载模型 为什么需要双亲委派加载模型 主要是为了安全,避免用户恶意加载破坏JVM正常运行的字节码文件,比如说加载一个自己写的java.util.HashMap.class.这样就有可能造成包冲 ...
- Java 判断日期的方法
//str:传入的日期 eg:"2018-07-23" function IsDate(str) { arr = str.split("-"); if(arr. ...