unix网络编程——TCP套接字编程

TCP客户端和服务端所需的基本套接字。服务器先启动,之后的某个时刻客户端启动并试图连接到服务器。之后客户端向服务器发送请求,服务器处理请求,并给客户端一个响应。该过程一直持续下去,直到客户端关闭,给服务端发送EOF(文件结束),服务器也关闭连接的服务器端,然后结束运行或者等待新的客户发起连接请求。如图1所示:
图1 TCP网络套接字示意图
在图中涉及到不同的函数,接下来进行详细的介绍。
socket函数
为了进行网络I/O,进程首先需要调用socket函数,指定使用的通信协议类型(IPv4的TCP、IPv6的UDP、Inux域字节流协议等)。
#include<sys/socket.h>
int socket(int family, int type, int protocol);
返回:若成功返回非负数,若失败返回-1
family表示协议族,协议族取值如表1所示:
| family | 说明 |
| AF_INET |
IPv4协议 |
| AF_INET6 | IPv6协议 |
| AF_LOCAL | Unix域协议 |
| AF_ROUTE | 路由套接字 |
| AF_KEY | 密钥套接字 |
表1 协议族family取值
type表示套接字类型,套接字类型type如表2所示:
| type | 说明 |
| SOCK_STREAM | 字节流套接字 |
| SOCK_DGRAM | 数据报套接字 |
| SOCK_SEQPACKET | 有序分组套接字 |
| SOCK_RAW | 原始套接字 |
表2 套接字类型
protocol表示某个协议类型常值,或者设置为0,以选择family和type组合的系统默认值,但并不是所有的family和type组合都是有效的,表3给出了正确组合。
表3 偷来的截图
socket函数调用成功后返回一个小的非负整数值,称为套接字描述符(socket descriptor),简称sockfd。指定了协议族(IPv4、Ipv6或Unix)和套接字类型(字节流、数据报或原始套接字),并没有指定本地协议地址或远程协议地址。
connect函数
TCP客户端使用connect函数来建立与TCP服务器之间的连接。
#include<sys/socket.h>
int connect(int sockfd, const struct *servaddr, socklen_t addrlen);
返回:若成功返回0,若失败返回-1
sockfd:socket函数返回的套接字描述符
servaddr:套接字地址结构的指针
addrlen:套接字地址结构的大小
套接字地址结构必须含有服务器的IP地址和端口号。客户端在调用connect函数前不必非要调用bind函数,因为如果需要的话,内核会确认源IP地址,并选择一个临时端口作为源端口。
如果是TCP套接字,调用connect函数会激发TCP三次握手,而且仅在连接建立成功或失败时才会返回。
bind函数
bind函数将一个本地协议地址赋予一个套接字,对于网际协议,协议地址是32位的Ipv4地址或128位的IPv6地址与16位的TCP或UDP端口号的组合。
#include<sys/socket.h>
int bind(int sockfd, const struct *myaddr, socklen_t addrlen);
返回:若成功返回0,若失败返回-1
sockfd:socket函数返回的套接字描述符
servaddr:套接字地址结构的指针
addrlen:套接字地址结构的大小
对于TCP,调用bind函数可以指定一个端口号和一个IP地址,也可以不指定。
服务器在启动时绑定它们的众所周知的端口,当调用connect或listen的时候,内核会为相应的套接字选择一个临时端口。让内核选择临时端口对于TCP客户端来说很正常,除非应用需要一个预留端口,然而对于TCP服务器来说却极为罕见,因为服务器是通过它们众所周知的端口被大家认识的。
(例外情况:RPC服务器,它们通常就由内核为它们的监听套接字选择一个临时端口,而该端口随后通过RPC端口映射器进行注册。客户在connect这些服务器之前,必须与端口映射器联系来获取它们的临时端口,这种情况也是用与UDP的RPC服务器)
进程可以将一个特定的IP地址绑定到它的套接字上,不过这个IP地址必须属于其所在主机的网络接口之一。对于TCP客户端,这就为在该套接字上发送的IP数据报指派了源IP地址,对于TCP服务器,这就限定该套接字只接收那些目的地为这个IP地址的客户连接。TCP客户通常不把IP地址绑定到套接字上。当连接套接字的时候,内核将根据所用外出网络接口选择源IP地址,而所有外出接口则取决于到服务器所需的路径。如果TCP服务器没有把IP地址绑定到套接字上,内核就把客户端发送的SYN的目的IP地址作为服务器的源IP地址。
如果指定端口为0,那么内核就在bind被调用时选择一个临时端口。然而如果指定IP地址为通配地址,那么内核将等到套接字已连接(TCP)或已在套接字上发出数据报(UDP)时才选择一个本地IP地址。
对于IPv4来说,通配地址由常值INADDR_ANY来指定,其值一般为0。它告知内核去选择IP地址。
listen函数
#include<sys/socket.h>
int listen(int sockfd, int backlog);
返回:若成功返回0,若失败返回-1
sockfd:socket函数返回的套接字描述符
backlog:内核应为响应套接字排队的最大连接个数
listen函数通常在调用socket函数和bind函数之后,调用accept函数之前调用。
listen函数仅由TCP服务器调用,它做两件事情:
当socket函数创建一个套接字的时候,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户端套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。调用listen函数使得套接字从CLOSED状态转到LISTEN状态。
图2 TCP状态转换图
为了理解backlog参数,必须为内核维护两个队列:
(1)未完成连接队列(incomplete connection queue),每个这样的SYN分节对应其中一项:已由某个客户端发起并达到服务器,而服务器正在等待完成相应的TCP三次握手过程。这些套接字处于SYN_RCVD状态(见图2);
(2)已完成连接队列(completed connection queue),每个已完成TCP三次握手过程的客户端对应其中一项,这些套接字处于ESTABLISHED状态(见图2)。
accept函数
accept函数由TCP服务器调用,用于从已完成连接队列的头部返回下一个已连接,如果已连接队列为空,那么进程休眠(如果套接字是默认的阻塞方式)。
#include<sys/socket.h>
int accept(int sockfd, struct *cliaddr, socklen_t *addrlen);
返回:若成功返回非负描述符,若失败返回-1
sockfd:socket函数返回的套接字描述符
cliaddr:返回已连接的对端(客户端)的协议地址
addrlen:值-结果参数(调用前,引用前,置为由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为由内核存放在该套接字地址结构内的确切字节数)
fork和exec函数
fork函数是unix派生新进程的唯一方法。
#include<unistd.h>
pid_t fork(void);
返回:在子进程中为0,在父进程中为子进程ID,若失败返回-1
fork函数调用一次,会返回两次。在父进程(调用进程)中返回一次,返回值是子进程(新派生进程)的进程ID号;在子进程中又返回一次,返回值为0。因此可以通过返回值来确定是父进程还是子进程。
fork函数在父进程和子进程中返回值不同的原因:任何子进程只有一个父进程,而且子进程总是可以通过调用getppid取得父进程的进程ID,反之,父进程有很多子进程,而且无法获取各个子进程的进程ID。如果父进程想要追踪所有子进程的进程ID,那么必须记录每次调用fork的返回值(因为在每次调用fork的时候,父进程会返回子进程的ID号,如果每次都进行记录,那么就可以追踪所有子进程的进程ID)。
父进程中调用fork之前打开的所有描述符在fork返回之后由子进程分享,我们将看到网络服务器利用这个特性:父进程调用accept之后调用fork,所接受的已连接套接字随后在父进程和子进程之间共享。通常情况下,子进程接着读写这个已连接套接字,父进程则关闭这个已连接套接字。
fork的典型用法:
(1)一个进程创建一个自身的副本,这样每个副本都可以在另一个副本执行其他任务的同时处理各自的某个操作。这是网络服务器的典型用法。
(2)一个进程想要执行另一个程序。既然创建新进程的唯一方法是调用fork,该进程于是首先调用fork创建一个自身副本,然后其中一个副本(通常称为子进程)调用exec把自身替换成新的程序,

unix网络编程——TCP套接字编程的更多相关文章
- 初探网络编程--TCP套接字编程演示
今天看了一下<计算机网络:自顶向下方法>,也就是计算机网络的教材的应用层一章,决定实现以下后面的Java C/S应用程序的例子,用来演示TCP和UDP套接字编程. 程序流程如下: 1.一台 ...
- 【UNIX网络编程(四)】TCP套接字编程具体分析
引言: 套接字编程事实上跟进程间通信有一定的相似性,可能也正由于此.stevens这位大神才会将套接字编程与进程间的通信都归为"网络编程",并分别写成了两本书<UNP1> ...
- UNP学习笔记1——基本TCP套接字编程
1 套接字地址结构 大多数套接字函数都需要一个指向套接字地址结构的指针作为参数.每个协议族都定义了自己的套接字结构.这些套接字的结构以sockaddr_开头,以每个协议族唯一的后缀名结尾. 1.1 I ...
- TCP套接字编程模型及实例
摘要: 本文讲述了TCP套接字编程模块,包括服务器端的创建套接字.绑定.监听.接受.读/写.终止连接,客户端的创建套接字.连接.读/写.终止连接.先给出实例,进而结合代码分析. PS:本文权当 ...
- 【UNIX网络编程(二)】基本TCP套接字编程函数
基于TCP客户/server程序的套接字函数图例如以下: 运行网络I/O.一个进程必须做的第一件事就是调用socket函数.指定期望的通信协议类型. #include <sys/socket.h ...
- unix网络编程第四章----基于TCP套接字编程
为了执行网络I/O操作.进程必须做的第一件事情就是调用Socket函数.指定期待的通信协议 #include<sys/socket.h> int socket(int family,int ...
- <网络编程>基本TCP套接字编程
tcp提供了可靠传输,当tcp向另一端发送数据的时候,要求对端返回一个确认.如果没有接收到确认,tcp就重传数据并且等待更长时间,数次重传失败后,tcp才放弃. 建立一个tcp连接会发生如下事情: 服 ...
- <unix网络编程>UDP套接字编程
典型的UDP客户/服务器程序的函数调用如下: 1.缓冲区 发送缓冲区用虚线表示,任何UDP套接字都有发送缓冲区,不过该缓冲区仅能表示写到该套接字的UDP数据报的上限.如果应用进程写一个大于套接字缓冲区 ...
- 套接字编程相关函数(2:TCP套接字编程相关函数)
本文摘录自<UNIX网络编程 卷1>. 基本套接字函数 socket函数 为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型.其定义如下: #in ...
随机推荐
- redis 数据库随笔 (一)
redis数据库的基本类型分析: 1.string 最基本的数据类型.只存贮一个值,key-value,最大值存储512M. 创建命令:hmset 读取命令:hget 2.hash 集合,存储为一个 ...
- 百度地图API和高德地图API资料集锦
[高德地图API]从零开始学高德JS API(五)路线规划——驾车|公交|步行 [高德地图API]从零开始学高德JS API(四)搜索服务——POI搜索|自动完成|输入提示|行政区域|交叉路口|自 ...
- 20155339 2016-2017-2 《Java程序设计》第7周学习总结
20155339 2016-2017-2 <Java程序设计>第7周学习总结 教材学习内容总结 第十三章 时间与日期 认识时间与日期 时间的度量 格林威治标准时间(GMT),现已不作为标准 ...
- 简单的贝叶斯分类器的python实现
# -*- coding: utf-8 -*- ''' >>> c = Classy() >>> c.train(['cpu', 'RAM', 'ALU', 'io ...
- 11- IO模型-未完成
1.同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么 ...
- Merge语句中NULL的陷阱
NULL表示unknown,不确定值,所以任何值(包括null值)和NULL值比较都是不可知的,在on子句,where子句,Merge或case的when子句中,任何值和null比较的结果都是fals ...
- virtualBox linux centos 挂载u盘 解决乱码
linux 挂载u盘 环境:虚拟机virtualBox5.2.8 linux centos6.5 64bit 第一步:插入u盘 第二步:virtualBox 设置启用u盘 提示:如果不知道哪个是你u盘 ...
- LintCode——尾部的零
尾部的零:设计一个算法,计算出n阶乘中尾部零的个数 样例:11! = 39916800.因此应该返回2 分析:假如你把1 × 2 ×3× 4 ×……×N中每一个因数分解质因数,例如 1 × 2 × 3 ...
- 维诺图(Voronoi Diagram)分析与实现(转)
一.问题描述1.Voronoi图的定义又叫泰森多边形或Dirichlet图,它是由一组由连接两邻点直线的垂直平分线组成的连续多边形组成. 2.Voronoi图的特点(1)每个V多边形内有一个生成元: ...
- CHAPTER 38 Reading ‘the Book of Life’ The Human Genome Project 第38章 阅读生命之书 人体基因组计划
CHAPTER 38 Reading ‘the Book of Life’ The Human Genome Project 第38章 阅读生命之书 人体基因组计划 Humans have about ...