socket套接字编程(二)

一、客户端程序中流程的特殊之处

  • 客户端通常不推荐用户自己绑定地址信息,而是让操作系统在发送数据的时候发现socket还没有绑定地址,然后自动选择一个合适的ip地址和端口进行绑定
  • 1.如果不主动绑定,操作系统会选择-个合适的地址信息进行绑定(什么地址就是合适的地址? — 当前没有被使用的端口)
  • 一个端口只能被一个进程占用, 若用户自己指定端口以及地址进行绑定有可能这个端口已经被使用了,则会绑定失败
  • 让操作系统选择合适的端口信息,可以尽最大能力避免端口冲突的概率
  • 对于客户端来说,其实并不关心使用什么源端地址将数据发送出去,只要能够发送数据并且接收数据就可以
  • 2.服务端可不可以也不主动绑定地址? (不可以的)

客户端所知道的服务端的地址信息,都是服务端告诉客户端的,一旦服务端不主动绑定地址,则会造成操作系统随意选择合适的地址进行绑定,服务端自己都不确定自己用了什么地址信息,如何告诉客户端
因此服务端通常必须主动绑定地址,并且不能随意改动

二、tcp编程流程

面向连接,可靠传输,面向字节流

client
1.创建套接字
2.绑定地址信息(不推荐)
3.向服务端发起连接请求
4.收发数据
5.关闭套接字

server
1.创建套接字:在内核中创建socket结构体
2.绑定地址信息:通过socket描述源端地址信息
3.开始监听:告诉操作系统可以开始接收连接请求(tcp是面向连接,通信前先建立连接)
服务端接收新客户端连接请求,会为客户端创建一个新的socket,这个套接字中既具有源端信息,也具有对端信息;这个新创建的套接字用于与这个客户端进行通信
最早的套接字:监听套接字—只用于接收新客户端连接请求
新创建套接字:通信套接字—用于后续与客户端进行数据通信
4.服务端程序中获取这个新建套接字的操作句柄描述符
因为后续与这个客户端的通信都是通过这个操作句柄完成的
最早的套接字描述符操作句柄只是用于建立连接,获取新连接的
5.收发数据:因为tcp的套接字socket中既描述了源端,也描述了对端,因此收发数据不需要在获取/指定对端的地址信息了 并且连接建立以后,谁先发送数据都可以
6.关闭套接字:释放资源

三、tcp编程socket接口介绍

1.创建套接字:
int socket(int domain, int type, int protocol) (AF_ INET, SOCK_ STREAM–流式套接字,IPPROTO_TCP);
2.绑定地址信息:
int bind(int sockfd, struct sockaddr *addr, socklen. t len); struct sockaddr_in;
3.服务端开始监听:
int listen(int sockfd, int backlog); —告诉操作系统开始接收连接请求
backlog:决定同一时间,服务端所能接收的客户端连接请求数量
4.获取新建socket的操作句柄:
从内核指定socket的pending queue中取出一个socket, 返回操作句柄
int accept(int sockfd, struct sockaddr *addr, socklen_t *len)
sockfd:监听套接字–指定要获取哪个pending queue中的套接字
addr:获取一个套接字,这个套接字与指定的客户端进行通信,通过addr获取这个客户端的地址信息
len:输入输出型参数–指定地址信息想要的长度以及返回实际的地址长度
返回值:成功则返回新获取的套接字的描述符—操作句柄;失败返回-1
5.通过新获取的套接字操作句柄(accept返回的描述符)与指定客户端进行通信
接收数据:
ssize
t recv(int sockfd, char *buf, int len, int flag);
返回值:成功返回实际读取的数据长度;连接断开返回0;读取失败返回-1
发送数据:
ssize_ t send(int sockfd, char *data, int
len, int flag);
返回值:成功返回实际发送的数据长度;失败返回-1;若连接断开触发异常
注意:连接若是断开了,recv会返回0;send会触发异常导致进程退出
6.关闭套接字:释放资源
int close(int fd)
7.客户端向服务端发送连接请求
int connect(int sockfd, int sockaddr *addr, socklen_ t len);
sockfd:客户端套接字—若还未绑定地址,则操作系统会选择合适的源端地址进行绑定
addr:服务端地址信息-- struct sockaddr_ in;这个地址信息经过connect之后也会描述到socket中
len: 地址信息长度

四、防止流程阻塞

while(1) {
1.获取新建连接— accept接口是一个阻塞函数–若没有新连接则阻塞等待
2.通过新建连接与客户端进行通信 }

问题
1、accept这个函数是-个阻塞函数:功能是获取新连接,如果当前没有新连接,则阻塞等待直到有新连接
2、recv/send默认也是阻塞函数:接收缓冲区没有数据则recv阻塞 /发送缓冲区数据满了则send阻塞
因为当前tcp服务端流程是固定的(因为我们无法获知什么时候有请求到来,然后调用accept, 什么时候有数据到来然后调用recv),获取新连接,以及与客户端通信;然而这两个功能都有可能阻塞,导致流程无法继续推进

解决方案:要防止流程阻塞,就要保证一个执行流只负责一个功能
一个执行流只管获取新连接,当新连接获取成功,然后创建新的执行流与客户端进行通信

  • 多进程:
    1.父进程创建子进程,数据独有,各自有一份cli_sock;然而子进程通过cli_sock通信,但是父进程不需要,因此父进程关闭自己的cli_sock
    2.父进程要等待子进程退出,避免产生僵尸进程;为了 父进程只负责获取新连接,因此对于SIGCHLD信号 自定义处理回调等待
  • 多线程:
    1.主线程获取到新连接然后创建新线程与客户端进行通信,但是需要将套接字描述符传入线程执行函数中
    2.但是传输这个描述符的时候,不能使用局部变量的地址传递(局部变量的空间在循环完毕就会被释放),可以传描述符的值,也可以传入new的对象
  1. C+ +中对于类型强转,将数据值当作指针传递有很多限制,我们想办法去克服就可以了
    4.主线程中虽然不适用cli_sock,但是不能关闭cli_ sock,因为线程间共享资源,一个线程释放,另一个线程也就没法使用了

连接断开在发送端与接收端上的表现:
1.接收端:连接断开,则recv返回0; 反之若recv返回0,表示的就是连接断开(套接字写端被关闭–双工通信)
2.发送端:连接断开,则send触发异常-SIGPIPE,导致进程退出

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值