Linux下网络编程一
发布日期:2021-05-07 15:55:55 浏览次数:22 分类:技术文章

本文共 3390 字,大约阅读时间需要 11 分钟。

网络编程基础

一. 库函数

1.connect()

向服务器发起连接请求。

函数声明:

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

函数说明:connect函数用于将参数sockfd 的socket 连至参数serv_addr 指定的服务端,参数addrlen为sockaddr的结构长度。

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

connect函数只用于客户端。

如果服务端的地址错了,或端口错了,或服务端没有启动,connect一定会失败。

2.bind()函数

服务端把用于通信的地址和端口绑定到socket上。

函数声明:

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

参数sockfd,需要绑定的socket。

参数addr,存放了服务端用于通信的地址和端口。

参数addrlen表示addr结构体的大小。

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

如果绑定的地址错误,或端口已被占用,bind函数一定会报错,否则一般不会返回错误。

问题一:

同时启动两个服务端,报出异常

tdx96@ubuntu:~/TDXFolder/socket$ ./server 5005bind: Address already in usetdx96@ubuntu:~/TDXFolder/socket$

解决办法:

在socket调用和bind调用之间加入

int opt = 1;   setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

问题二:

socket的端口范围
1024以上到65535可用,1024以下为系统保留端口

tdx96@ubuntu:~/TDXFolder/socket$ ./server 1024
tdx96@ubuntu:~/TDXFolder/socket$ ./server 1023bind: Permission denied

若要使用1024以下的端口,则输入指令

tdx96@ubuntu:~/TDXFolder/socket$ su密码:root@ubuntu:/home/tdx96/TDXFolder/socket# ./server 1023

3.listen()

listen函数把主动连接socket变为被动连接的socket,使得这个socket可以接受其它socket的连接请求,从而成为一个服务端的socket。

函数声明:

int listen(int sockfd, int backlog);

返回:0-成功, -1-失败

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

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

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

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

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

4.accept()

服务端接受客户端的连接。

函数声明:

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

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

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

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

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

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

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

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

连接请求会被放在队列中,accept读取队列中的链接请求。如果队列为空,则accept阻塞。
在这里插入图片描述

5.send()

send函数用于把数据通过socket发送给对端。不论是客户端还是服务端,应用程序都用send函数来向TCP连接的另一端发送数据。

函数声明:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

sockfd为已建立好连接的socket。

buf为需要发送的数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,内存中有什么就发送什么。

len需要发送的数据的长度,为buf中有效数据的长度。

flags填0, 其他数值意义不大。

函数返回已发送的字符数。出错时返回-1,错误信息errno被标记。

注意,就算是网络断开,或socket已被对端关闭,send函数不会立即报错,要过几秒才会报错。

如果send函数返回的错误(<=0),表示通信链路已不可用。

当发送内容超出缓冲区大小时,send会阻塞,等待缓冲器的空间。

6.recv()

recv函数用于接收对端socket发送过来的数据。

recv函数用于接收对端通过socket发送过来的数据。不论是客户端还是服务端,应用程序都用recv函数接收来自TCP连接的另一端发送过来数据。

函数声明:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd为已建立好连接的socket。

buf为用于接收数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,只要是一块内存就行了。

len需要接收数据的长度,不能超过buf的大小,否则内存溢出。

flags填0, 其他数值意义不大。

函数返回已接收的字符数。出错时返回-1,失败时不会设置errno的值。

如果socket的对端没有发送数据,recv函数就会等待,如果对端发送了数据,函数返回接收到的字符数。出错时返回-1。如果socket被对端关闭,返回值为0。

如果recv函数返回的错误(<=0),表示通信通道已不可用。

二.TCP的三次握手和连接队列

在这里插入图片描述

在这里插入图片描述

测试:
启动服务端,查看当前监听的socket:

tdx96@ubuntu:~$ netstat -na|more

查看服务端5005状态

tdx96@ubuntu:~$ netstat -na|grep 5005tcp        0      0 0.0.0.0:5005            0.0.0.0:*               LISTEN

开启客服端 连接5005端口

查看客户端5005状态

tdx96@ubuntu:~$ netstat -na|grep 5005tcp        0      0 0.0.0.0:5005            0.0.0.0:*               LISTENtcp        0      0 127.0.0.1:60354         127.0.0.1:5005          ESTABLISHEDtcp        0      0 127.0.0.1:5005          127.0.0.1:60354         ESTABLISHED

三次握手已经完成

三. TCP的分包和粘包

在这里插入图片描述

上一篇:C++并发与多线程(一)
下一篇:基于STM32F4实现FOC(磁场定向控制)一:电流采样和波形产生

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月04日 21时02分02秒