socket网络通信中的 listen(),connecet() 和 accept()函数

一、listen() 函数

1.作用:

  listen() 函数把主动连接套接字变为被动连接的套接字,使得这个 socket 可以接受其他 socket 的连接请求,从而成为一个服务端的 socket。
在这里插入图片描述

2.函数声明及参数

2.1 函数声明

int listen(int sockfd, int backlog);

2.2 函数参数

  (1)参数 sockfd:

  参数sockfd是已经被bind过的socket(这个函数是监听,肯定是先绑定了,才监听)。

  socket函数返回的socket是一个主动连接的socket,在服务端的编程中,程序员希望这个socket可以接受外来的连接请求,也就是被动等待客户端来连接。由于系统默认时认为一个socket是主动连接的,所以需要通过某种方式来告诉系统,程序员通过调用listen函数来完成这件事。

  (2)参数backlog,这个参数涉及到一些网络的细节,比较麻烦,填5、10都行,一般不超过30。

  (3) 当调用listen之后,服务端的socket就可以调用accept来接受客户端的连接请求。

返回值:成功则返回0,失败返回-1,错误原因存于errno 中。

listen函数一般不会返回错误。

3.注意事项

  当服务端程序还没有开始监听时,客户端请求连接时,会出现无法连接。

  测试:
(1)在 listen() 函数前加上 sleep() 让 listen() 函数沉睡一会儿
在这里插入图片描述
(2)客户端请求连接

在这里插入图片描述

二、accept() 函数

1.作用:

  之前说 accept() 函数是接受客户端的连接,准确的说法应该是:从准备好的请求连接队列中取出(获取)一个请求,如果队列为空,accept函数将阻塞等待。

在这里插入图片描述

  测试:
  (1)把 socket 设为监听模式后,让程序沉睡一会儿
在这里插入图片描述
  (2)如果这个时候客户端请求连接,但是这个连接请求是还没有进到连接队列的,因为我用sleep函数让服务端程序沉睡一会。这个时候队列是空的,要过了sleep规定的时间,连接请求才能进去队列。accept函数才能从中获取连接请求。

在这里插入图片描述

客户端请求连接
在这里插入图片描述
等待了一段时间,才能从中获取请求连接。
在这里插入图片描述

2.函数声明及参数

2.1 函数声明:

  根据作用也可以猜测一下这个函数的参数了,客户端请求连接的话,怎么标识客户端?IP地址,所以参数中要有一个参数是用来存放客户端的地址信息的。

  socket 通信就是服务端和客户端的两个 socket 进行通信,因为参数中有了一个参数是客户端的地址信息了,那么有一个参数应该是服务端的 socket

  accept函数,是从准备好的连接请求队列里面取出一个客户端的请求,并把这个客户端的地址信息放到参数的 sockaddr_in 结构体里面。

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

2.2 函数参数

  参数sockfd:
是已经被listen过的socket。

  参数addr:
用于存放客户端的地址信息,用sockaddr结构体表达,如果不需要客户端的地址,可以填0。

参数addrlen用于存放addr参数的长度,如果addr为0,addrlen也填0。

accept函数等待客户端的连接,如果没有客户端连上来,它就一直等待,这种方式称之为阻塞。

accept等待到客户端的连接后,创建一个新的socket,函数返回值就是这个新的socket,服务端使用这个新的socket和客户端进行报文的收发。

返回值:
  成功则返回0,失败返回-1,错误原因存于errno 中。

accept在等待的过程中,如果被中断或其它的原因,函数返回-1,表示失败,如果失败,可以重新accept。

2.3 注意

  int  clientfd;                  // 客户端的socket。
  int  socklen=sizeof(struct sockaddr_in); // struct sockaddr_in的大小
  struct sockaddr_in clientaddr;  // 客户端的地址信息。
  clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);

  看这段代码,为什么还要定义一个 clientfd socket 呢?直接用 listenfd socket 来通信(发送接收报文)不行吗?

  重新定义一个用于通信的 socket 的主要原因是 :有时候连接服务端的客户端不止一个,如果只用 listenfd socket 通信是不行的,多于一个客户端就分不清是谁发过来的数据了。
在这里插入图片描述
在这里插入图片描述

  所以这段代码通常是和循坏一起使用的,创建多个通信的socket

 clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);

三、connect() 函数

1.作用

  客户端向服务器发起连接请求。

2.函数声明及参数

  根据函数的作用,我们可以猜出,函数的参数有哪些。这个函数是客户端用来连接服务端的,所以要有服务端的地址信息(sockaddr),还有客户端的信息(客户端的信息放在socket里面)

在这里插入图片描述

int connect (int sockfd, struct sockaddr * serv_addr, int addrlen);

  (1)函数说明:

  connect 函数用于将参数 sockfd 的socket 连接至参数 serv_addr 指定的服务器(就是将客户端的socket连接到服务器)。参数 addrlen 为 sockaddr 的结构长度。

  (2)返回值:
  成功则返回0,失败返回-1,错误原因存于 errno 中。

  (3)connect 函数只用于客户端,因为是客户端去连接。如果服务端的地址错了,或端口号错了,或服务端没有启动,connect 一定会失败。

四、总结

  1.服务端在调用 listen() 之前,客户端不能向服务端发起连接请求。(这个就像是银行里的前台还没开始上班,就不要去请求别人的帮助)

  2.服务端调用 listen() 函数后,服务端的socket开始监听客户端的连接。(到了上班的时间,前台开始工作,等待用户)

  3.客户端调用 connect() 函数向服务端发起连接请求。

  4.在TCP底层,客户端和服务端握手后建立起通信通道,如果有多个客户端请求,在服务端就会形成一个已准备好的连接的队列。
在这里插入图片描述

  5.服务端调用 accept() 函数从队列中获取一个已准备好的连接,函数返回一个新的 socket ,新的 socket 用于与客户端通信,listen 的 socket 只负责监听客户端的连接请求。

要实现向服务器注册的功能,你需要在代码增加以下步骤: 1. 接收客户端发来的用户名和密码信息。 2. 将用户名和密码信息保存到一个字典,用于后续验证用户身份。 3. 向客户端发送注册成功的消息。 下面是修改后的代码示例: ``` import socket users = {} # 保存注册用户信息的字典 host = '172.22.102.20' port = 9999 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((host, port)) server_socket.listen(1) print('等待连接...') client_socket, client_address = server_socket.accept() print(f'已连接:{client_address}') while True: message = client_socket.recv(1024).decode('utf-8') print(f'客户端:{message}') if not message: break if message.startswith('register'): # 处理注册请求 _, username, password = message.split() # 解析用户名和密码 users[username] = password # 保存用户信息 reply_message = f'注册成功,用户名为{username}' elif message.startswith('login'): # 处理登录请求 _, username, password = message.split() # 解析用户名和密码 if users.get(username) == password: # 验证用户身份 reply_message = f'登录成功,欢迎{username}!' else: reply_message = '用户名或密码错误,请重新输入。' else: reply_message = f'已收到:{message}' client_socket.sendall(reply_message.encode('utf-8')) client_socket.close() server_socket.close() ``` 在客户端,你可以使用以下代码向服务器注册新用户: ``` import socket host = '172.22.102.20' port = 9999 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((host, port)) username = input('请输入用户名:') password = input('请输入密码:') message = f'register {username} {password}' client_socket.sendall(message.encode('utf-8')) reply_message = client_socket.recv(1024).decode('utf-8') print(reply_message) client_socket.close() ``` 当输入用户名和密码后,客户端会将注册请求发送给服务器,并等待服务器的回复。如果注册成功,客户端会收到服务器发来的注册成功消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值