ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Socket学习笔记

2022-03-06 22:34:17  阅读:142  来源: 互联网

标签:addr 字节 int 笔记 学习 socket 接字 客户端 Socket


SOCKET

插板插座

网络套接字

在通信过程中,套接字一定是成对存在的

两份套接字,C一个 S一个

 

 

一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现)

 

网络字节序

小端法: 高位高地址 低位低地址 (Inter使用)

大端法 : 高位低地址 低位高地址 (IBM一开始使用)TCP/IP协议规定,网络数据流应采用大端字节序

例如UDP段格式,地址0-1是16位的源端口号,如果这个端口号是1000 (0x3e8), 则地址0是0x03, 地址1是0xe8, 也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址存0x03, 高地址存0xe8。但是,如果发送主机是小端字节序的,这16位被解释成0xe803, 而不是1000。 因此,发送主机把1000填到发送缓冲区之前需要做字节序的转换。同样地,接收主机如果是小端字节序的,接到16位的源端口号也要做字节序的转换。如果主机是大端字节序的,发送和接收都不需要做转换。同理,32位的IP地址也要考虑网络字节序和主机字节序的问题。

 

htonl 是将本地的32位整型数转换为网络字节序。 主要是用于IP的转换。

192.168.1.11 --> string -->atoi -->int -->htonl -->网络字节序

IP地址转换函数

int inet_pton (int af ,const char *src, void *dst)

af 指代当前协议,IPV4 (AF_INET)或者IPV6 (AF_INET6)

src 传入ip地址 (点分十进制)

dst 传出 转换后的 网络字节序的IP地址

返回值:

成功:1

异常: 0 说明src 指向的不是一个有效的IP地址

失败: -1

const char *inet_ ntop(int af,const void * src, char * dst, socklen_t size); 网络字节序--->本地字节序(string IP) af: AF_INET、AF_INET6 src: 网络字节序IP地址 dst: 本地字节序(string IP) size: dst 的大小。

返回值:成功:dst

失败 : null

 

sockaddr 地址结构

     struct  sockaddr_in add;

第一个参数 add.sin_family = AF_INET

第二个参数 add.sin_port = htons(8080);

第三个参数是结构体 add.sin_addr.s_addr

但是一般是用add.sin_addr.s_addr = htonl( INADDR_ANY) (取出系统中任意有效的IP地址,是二进制类型,所以用htonl进行转换)

 

流程

TCP服务端同socket函数生成一个套接字,套接字中有文件描述符,这个文件描述符相当于句柄,一个入口。然后调用bind()函数将端口号和IP绑定,再调用listen() listen的作用是设置监听上限,就是一个参数设置。再进行调用accept()是阻塞监听客户端连接。accept的参数中包含着之前用socket()生成的套接字的句柄。然后accept函数会返回一个新的socket()。这个新的socket 和客户端的套接字进行通信。

TCP客户端通过socket()新建了一个套接字,然后通过connect()进行连接。connect()中要绑定IP和port 。此时服务端和客户端就连接在了一起,整个系统中存在三个socket(),一对和一个独立的,一对的是进行连接通信的,单独的那个是进行监听的。

 

Socket函数

 int socket(int domain, int type, int protocol);

domain是指定当前的IP协议是哪个,例如我们选择AF_INET即IPV4.

 

 

 

type指定当前套接字的数据传输协议。期中有流式传输STREAM, 报式协议SOCK_DGRAM

 

 

 

protocol : 表示所选协议中的代表协议,一般我们都是传0.

举例: type的参数是STREAM时,protocol的参数是0,就表示当前协议是TCP,如果type是DGRAM,protocol的参数是0,就表示单前协议是UDP

返回值:如果成功就返回新套接字所对应的文件描述符。

失败就是 -1 errno。

举例:fd= socket(AF_INET,SOCK_STREAM,0) 就是返回一个IPV4的TCP协议的套接字。

 

bind()

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

给socket 绑定一个地址结构(IP+port)

sockfd 就是socket函数返回值

addr 就是sockaddr 地址结构,要分别对三个参数进行初始化。

这个结构体和参数中需要的结构体不同,所以初始化完了之后要进行强转。

例如:

 struct sockaddr_in addr ;
 ​
  addr.sin_family = AF_INET;   //(和前面使用的IP协议相同)
 ​
  addr.sin_port = htons(8080);
 ​
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
 ​
 addr :(struct sockaddr *) &addr ;
 ​
 addrlen :sizeof(addr)  地址结构的大小

返回值:

成功: 0

失败: -1 errno

 

listen()

  int listen(int sockfd, int backlog);

设置同时与服务器建立连接的上限数(同时进行三次握手的客户端数量)

sockfd: socket的句柄

backlog : 上限数值, 最大值为128

成功:返回0

失败: 返回-1

 

ACCEPT

 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 ​
 #define _GNU_SOURCE             /* See feature_test_macros(7) */
 #include <sys/socket.h>
 ​
 int accept4(int sockfd, struct sockaddr *addr,socklen_t *addrlen, int flags);
 ​

阻塞等待客户端建立连接,成功返回一个与客户端成功连接socket文件描述符。用一个socket返回一个新socket

sockfd: socket的句柄

addr : 和bind 的参数差一个关键字const ,上一个是传入参数,这个是传出参数,即传出的是成功与服务器建立连接的那个客户端的地址结构。

addrlen : 入:addr的大小 出:客户端addr的实际大小

返回值:

成功:能与客户端进行数据通信的socket对应的文件描述。

失败: -1 errno

一个端口肯定只能绑定一个socket。服务器端的端口在bind的时候已经绑定到了监听套接字socetfd所描述的对象上,accept函数新创建的socket对象其实并没有进行端口的占有,而是复制了socetfd的本地IP和端口号,并且记录了连接过来的客户端的IP和端口号。

那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?

客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。

由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理:如果收到的是请求连接的数据包,则传给监听着连接请求端口的socetfd套接字,进行accept处理;如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd_new 套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。

 

connect()

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

与服务器建立连接。

sockfd : 客户端socket函数返回值。

addr : 传入参数, 服务器的地址结构 。

addrlen : 服务器地址结构的长度。

返回值:

成功 :0

失败: -1 errno

这边不使用bind 函数进行绑定的话,就采用了“隐式绑定”。

 

TCP通信流程分析:

server:

  1. socket( ) 创建socket

  2. bind ( ) 绑定服务器地址结构

  3. listen( ) 设置监听上限

  4. accept() 阻塞监听客户端连接

  5. read (fd) 读socket获取客户端数据

  6. 小--大写 toupper ()

  7. write(fd)

  8. close()

client:

  1. socket() 创建socket

  2. connect( ) 与服务器建立连接

  3. write ( ) 写数据到socket

  4. read() 读转换后的数据。

  5. 显示读取结果

  6. close

标签:addr,字节,int,笔记,学习,socket,接字,客户端,Socket
来源: https://www.cnblogs.com/fight-nxlwwy/p/15973849.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有