Unix网络编程学习笔记之第4章 基于TCP套接字编程

1. socket函数

int socket(int family, int type,int protocol)

成返回一个套接字描述符。错误返回-1

其中family指定协议族,一般IPv4为AF_INET, IPv6为AF_INET6。

其中type指定套接字类型,字节流:SOCK_STREAM.   数据报:SOCK_DGRAM。

一般情况下通过family和type的组合都可以唯一确定一个套接字类型。所以一般我们就把protocol设为0就可以了。

有时在某些特殊情况下,family和type的组合不是都是有效的,这时我们就要给protocol指定一些特殊的值了。

2. connect函数

int connect(int sockfd, const struct sockaddr * servaddr, socklen_t addrlen);

连接服务器,其中servaddr是服务器的地址。

如果是TCP套接字,connect会触发三次握手。

从前文可以知道,当客户端接收到服务器端的对SYN的响应的时候,connect函数就返回,若客户端发送的SYN出错,或者响应的ACK出错都会引起connect函数出错。成功返回0,出错返回-1且errno被设置。

注意:如果connect出错,不能直接重新connect。必须要先关闭这个套接字,然后重新socket-connect。

3. bind函数

   int bind(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen)

成功返回0,错误返回-1

为指定的套接字绑定一个本地的套接字地址。

(1) 一般服务器端需要绑定一个公开的端口号,而服务器端一般绑定Ip时是INADDR_ANY,意为当accept时,内核会从本地IP地址中选择一个本地IP赋值。这对于一台机器上有多个网络接口时,是很有影响的。

而通常机器只有一个网络接口,则我们也使用这种方式,是因为我们不必要写服务器本地的IP(硬编码),这样写使得我们的程序有好的移植性。

(2) 一般客户端socket函数之后就直接connect了,不进行bind,因为我们通常不需指定客户端的Ip和端口号。让内核自动赋值就可以了。

(3) IPv4中的INADDR_ANY通常为0,所以我们为其赋值时,是使用如下格式:

servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

因为sin_addr是一个结构体,所以我们使用sin_addr.s_addr来使用其整数形式赋值。

4. listen函数

int listen(int sockfd, intbacklog)

成功返回0,错误返回-1

注意这里的listen并不是我们通常理解的监听的意思,因为套接字不是在这里阻塞的,而是在accept阻塞的。

Listen只做两件事:

(1) 把socket函数创建的套接字,设为被动套接字。因为socket函数默认创建主动套接字,主动套接字:是需要connect去主动连接的。

(2) 规定了内核应该为连接套接字排队的最大个数。

内核是如何进行连接排队的?

内核维护两个队列,未完成连接队列,已完成连接队列。

未完成连接队列:客户SYN到达后,就被放入未完成连接队列队尾。

已完成连接队列:客户完成了三次握手之后,就把它放入已完成队列队尾。

然后进程调用accept,就从已完成队列队首项返回给进程。

这里的疑问?服务器端不是一直在accept阻塞吗,怎么这里还提到进程调用accept这个说法?

因为这里的情况是在多个客户端几乎同时达到连接时,其中某一个连接发生的情况,因为我们写服务器端程序时,都是把accept写在一个循环内的,所以某个客户的SYN到达,可能这时并没有执行到accept,所以这里说等到进程调用accept时。也就是说,系统在已完成连接队列为空时,accept才会阻塞。

注意这里所说的backlog是两个队列之和,但实际情况下,一般内核允许排队的个数都要略大于这个值。

5. accept函数

int accept(int sockfd, struct sockaddr* cliaddr , socklen_t* addrlen)

成功返回描述符,错误返回-1

接受客户连接,如果已完成连接队列中有数据,则读取队头,返回一个已连接套接字描述符。如果已完成连接队列为空,则阻塞。

成功返回返回一个已连接套接字描述符,失败返回负值。

注意:第三个参数为整型的地址。因为accept函数是从内核得到的套接字。如果程序对客户端的套接字地址不感兴趣,则可以把后面两个参数都设为NULL。

一个服务器通常只有一个监听套接字,而为每个客户创建一个已连接套接字。

6. getsockname和getpeername

   int getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen)

返回和套接字描述符sockfd关联的本地套接字地址

   int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen)

返回和套接字描述符sockfd关联的对端套接字地址

显然这两个函数都是从内核中得到套接字地址,所以第三个参数是整型的地址。

注意:

(1) 一般客户端没有bind,所以在connect之后,才可以调用getsockname/getpeername。

(2) 一般服务器端bind端口之后,可以调用getsockname获取端口号。

一般服务器端bind的是通配地址,所以一般不可以获取监听套接字描述符的关联ip地址,而是获取已连接套接字描述符关联的ip地址。

(3) POSIX允许对未bind的套接字调用getsockname,所以该函数适合任何已打开的套接字描述符(即调用socket函数返回的套接字描述符都叫已打开的套接字描述符),只是不一定输出的是什么。

插入知识:

1. socket这几个函数都是一样的,成功返回0/描述符,失败返回-1。所以它们判断成功的条件都是一样的。

2. RST分组,RST分组是TCP在发生错误时发送的一种TCP分组。

产生RST的条件:

(1) 一个目的地为某端口的SYN到达,然后本机没有正在监听该端口的程序,此时本机就发送一个RST。

(2) TCP想要取消一个连接。

(3) TCP接受到一个不存在的连接的分组。即某个客户端没有连接,就往服务器发送数据,这时服务器就会给这个客户端发送RST。

其实RST的意思就是让对方重新连接的意思。

时间: 2024-08-13 14:31:12

Unix网络编程学习笔记之第4章 基于TCP套接字编程的相关文章

Unix网络编程学习笔记之第8章 基于UDP套接字编程

一. UDP C/S的典型函数调用 UDP没有像TCP那样的连接,客户端直接sendto向某服务器发送数据,服务器端一直recvfrom阻塞,以接收任何客户端发送的数据. 二. sendto和recvfrom函数 int sendto(int sockfd, const void* buff, size_t nbytes, int flag, const struct sockaddr* to, socklen_taddrlen); int recvfrom(int sockfd, void*

UNP学习笔记(第四章 基本TCP套接字编程)

本章讲解编写一个完整的TCP客户/服务器程序所需要的基本套接字函数. socket函数 #include <sys/socket.h> int socket(int family,int type,int protocol); //返回:成功则为非负描述符,若出错则为-1 family参数指明协议族,它是如下某个常值 type参数指明套接字类型,它是如下某个常值 protocol参数为下面某个协议类型常值,或者设为0,以选择所给定family和type组合的系统默认值 下图展示了基本TCP客户

unix网络编程第四章----基于TCP套接字编程

为了执行网络I/O操作.进程必须做的第一件事情就是调用Socket函数.指定期待的通信协议 #include<sys/socket.h> int socket(int family,int type,int protocol); family表示协议族,比如AF_INET,type表示套接字类型, protocol一般设置为0 family: AF_INET ipv4协议 type: SOCK_STREAM 字节流套接字 SOCK_DGRAM 数据报套接字 SOCK_RAW 原始套接字 pro

《网络编程》基于 TCP 套接字编程的分析

本节围绕着基于 TCP 套接字编程实现的客户端和服务器进行分析,首先给出一个简单的客户端和服务器模式的基于 TCP 套接字的编程实现,然后针对实现过程中所出现的问题逐步解决.有关基于 TCP 套接字的编程过程可参考文章<基本 TCP 套接字编程>.该编程实现的功能如下: (1)客户端从标准输入读取文本,并发送给服务器: (2)服务器从网络输入读取该文本,并回射给客户端: (3)客户端从网络读取由服务器回射的文本,并通过标准输出回显到终端: 简单实现流图如下:注:画图过程通信双方是单独的箭头,只

第4章 基本tcp套接字编程

4.1 各种套接字api(重要) 4.1.1 socket() 用于创建一个套接字描述符,这个描述符指明的是tcp还是udp,同时还有ipv4还是ipv6 #include <sys/socket.h>?int socket(int family, int type, int protocol);//成功返回描述符,错误-1 family主要是指明的协议族,AF_INET:ipv4.AF_INET6:ipv6 .AF_LOCAL:unix域协议.AF_ROUTE:路由套接字.AF_KEY秘钥套

第四章 基本TCP套接字编程 第五章 TCP客户/服务器程序实例

TCP客户与服务器进程之间发生的重大事件时间表 TCP服务器 socket() --- bind() --- listen() --- accept() --- read() --- write --- read() --- close TCP客户 socket() --- connect() --- write() --- read()  --- close() 套接字函数简介 int socket(int family, int type, int protocol); 指定要用的通信协议类

UNP——第四章,TCP套接字编程

1.socket 函数 family AF_INET IPv4 AF_INET6 IPv6 AF_LOCAL Unix域 AF_ROUTE 路由套接字 AF_KEY 密钥套接字 type SOCK_STREAM 字节流 SOCK_DGRAM 数据报 SOCK_SEQPACKET 有序分组 SOCK_RAW 原始套接字 AF_INET 和 AF_INET6 的 protocol IPPROTO_TCP IPPROTO_UDP IPPROTO_SCTP (2)AF_xxx 和 PF_xxx AF_

Unix网络编程学习笔记之第2章 TCP和UDP

TCP 1. TCP面向连接的协议,是一个字节流协议,没有任何记录边界.发送的是数据分组. 2. TCP提供了可靠性:确认重传和重组 (1) TCP每发送一份数据都会要求对端进行确认.如果超时,就会重传.TCP会估计往返时间RTT,以确定等待多长时间重传. (2) 如果多次发送数据分组,TCP可以保证分组的按序达到.即会根据序列号进行重组. 3. TCP提供流量控制 TCP在任何时刻通知对端,它此时一次能够接受多少字节的数据,即通告窗口.该窗口指出接受缓冲区当前可用的空间. 4. 为何说TCP是

《网络编程》基本 TCP 套接字编程

在进行套接字编程之前必须熟悉其地址结构,有关套接字的地址结构可参考文章<套接字编程简介>.基于 TCP 的套接字编程的所有客户端和服务器端都是从调用socket 开始,它返回一个套接字描述符.客户端随后调用connect 函数,服务器端则调用 bind.listen 和accept 函数.套接字通常使用标准的close 函数关闭,但是也可以使用 shutdown 函数关闭套接字.下面针对套接字编程实现过程中所调用的函数进程分析.以下是基于 TCP 套接字编程的流程图: socket 函数 套接