Linux的TCP接口介绍

1. socket

1.1原型

#include <sys/socket.h>
int socket (int family ,int type ,int protocol)

family: AF_INET(ipv4) AF_INET(ipv6) AF_LOCAL (Unix域协议) AF_ROUTE(路由套接字) AF_KEY(秘钥套接字)

type: SOCK_STREAM(字节流套接字) SOCK_DGRAM(数据包套接字) SOCK_SEQPACKET(有序分组套接字) SOCK_RAW(原始套接字)

protocol:IPPROTO_TCP IPPROTO_UDP IPPROTO_SCTP

1.2 family和type参数组合

AF_INETAF_INET6AF_LOCALAF_ROUTEAF_KEY
SOCK_STREAMTCP/STCPTCP/STCP
SOCK_DGRAMUDPUDP
SOCK_SEQPACKETSCTPSCTP
SOCK_RAWIPV4IPV6

1.3 调用函数

IPV4的TCP通信 :

int sockfd;

if( (sockfd = socket(AF_INET,SOCK_STREAM,0)) <0) {}

2. connect(客户端)

2.1 函数原型

#include <sys/socket.h>
int connect (int sockfd ,const struct sockaddr *servaddr ,socklen_t addrlen)


sockfd 套接字描述符

servaddr 通用套接字地址结构

socklen_t sizeof(servaddr )

2.2 三次握手

假设 A 为客户端,B 为服务器端。
首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。
A 向 B 发送连接请求报文,SYN=1,ACK=0,选择一个初始的序号 x。
B 收到连接请求报文,如果同意建立连接,则向 A 发送连接确认报文,SYN=1,ACK=1,确认号为 x+1,同时
也选择一个初始的序号 y。
A 收到 B 的连接确认报文后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。
B 收到 A 的确认后,连接建立。

三次握手的原因
第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。
客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。

2.3 出错的情况(网络阻塞,端口,路由)(超时、拒绝服务)

  1. TCP客户端没有接收到SYN分节的响应,则返回 ETIMEDOUT

  2. 客户端接收到RST 的响应,则表明服务器主机在我们指定的端口上没有进程在监听或者服务器上的监听队列已满,返回ECONNREFUSED

  3. 若客户发出的SYN在中间的某个路由器上引发一个“ destination unreachable”的ICMP错误,

    EHOSTUNREACH *ENETUNREACH

2.4 调用函数

struct sockaddr_in servaddr;//定义一个套接字结构(IPV4)
//初始化套接字结构,TCP协议,**server IP and port**
bzero(&servaddr,sizeof(servaddr));//清0操作
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5330);//port
if( inet_pton(AF_INET,argv[1],&servaddr.sin_addr) <= 0){ }
//客户端发起连接,struct sockaddr *通用套接字结构
if ( connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0) {}

3. bind(服务端)

3.1函数原型

#include <sys/socket.h>

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

调用bind函数可以指定一个端口号/IP地址( IP, Port)

如果一个TCP客户端或者服务器未调用bind,则调用connect或者listen时,内核就要为相应的套接字选择一个临时端口。

一般情况下,TCP客户端默认不调用bind,服务器调用bind。

3.2 出错的情况

常见错误:EADDRINUSE(Address already in use)

3.3 调用函数


//					server

//初始化套接字结构,TCP协议,server IP and port
	bzero(&servaddr,sizeof(servaddr));//清0操作
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(5330);//port
	//内核决定IP地址
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY == 0
	if( bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0){}

4.listen

4.1 函数原型

#include <sys/socket.h>

int listen(int sockfd, int backlog);

backlog:可连接的TCP数量,内核为相应套接字排队的最大连接个数

 On  success,  zero is returned. 
 On error, -1 is returned, and errno is set appropriately.

拓展知识

​ 服务器TCP连接通道 数 = 未完成连接队列 + 已完成连接队列

​ 调用listen成功,套接字从CLOSED切换到LISTEN

​ SYN_RCVD (未完成连接队列 ) ,由某个客户端发出SYN分节,三次握手的第一阶段,当服务器接收到SYN时,由LISTEN切换到SYN_RCVD.

​ 当服务器完成三次握手,SYN_RCVD切换到ESTABLISHED

4.2 出错的情况

EBADF

The  argument  sockfd is not a valid file descriptor.

EADDRINUSE

Another socket is  already  listening  onthe same port.

4.3 调用函数

if(listen(sockfd,10) <0){}

5.accept

5.1 函数原型

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

   参数数据类型参考上面

   addr 是客户端的套接字结构,可查看客户端的IP和端口号

   成功时,返回一个已连接描述符

5.2 出错的情况

查看:man accept

里面涉及十多种错误

5.3 调用函数

int connfd;
if( (connfd = accept(sockfd,(struct sockaddr *)NULL,NULL) ) <0){}
//查看客户端信息
struct sockaddr_in clientaddr;
char buff [100];
socklen_t len =sizeof(clientaddr);

if (connfd = accept(sockfd,(struct sockaddr *)&clientaddr,&len) <0){}
		printf("client ip and port,%s  ,%d\n",inet_ntop(AF_INET,&clientaddr.sin_addr,buff,sizeof(buff)),ntohs(clientaddr.sin_port));

6.close

6.1 函数原型

#include <unistd.h>
int close (int sockfd);

使用后,描述符不能作为read或者write的参数。

6.2 调用函数

if( close (connfd)<0) {}

7.socket总结

客户端
int socket (int family ,int type ,int protocol)
int connect (int sockfd , const struct sockaddr *servaddr , socklen_t addrlen)
int close (int sockfd);

服务器
int socket (int family ,int type ,int protocol)
int bind (int sockfd,const struct sockaddr *myaddr,socklen_t addrlen)
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int close (int sockfd);

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值