netfinal
一、填空题
1. 常用函数
-
int socket(int domain, int type, int protocol)
- 作用:创建套接字
- 成功:返回描述符 失败:-1
- **domain:协议族 **
- type: socket类型
- protocol:哪种协议,由于指定了type,这里一般用“0”
-
int close(int fd)
- 作用:关闭套接字
- 成功返回0,失败:-1
- fd:套接字描述符
-
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen)
- 作用:服务器绑定地址信息
- 成功返回0,失败:-1
- sockfd:套接字
- myaddr:指向地址结构的指针
- addrlen:指定结构体变量的大小
-
in_addr_t inet_addr 成功 32位数 失败INADDR_NONE
- 作用:是将一个ip地址字符串转换成一个整数值
-
int gethostname(char *name, size_t len)
- 作用:返回本地主机的标准主机名
- 成功返回0,失败返回-1
-
int inet_aton(const char *ipstring,struct in_addr *inp)
- 成功返回1,失败返回0
- 将x.x.x.x形式的IP地址串转换为in_addr类型的结构体
-
in_addr_t inet_addr(const char *ipstring)
- 成功返回32位整数,失败返回INADDR_NONE
- 将x.x.x.x形式的IP地址串转换为32位,网络字节顺序的整数
-
ssize_t sendto(int sockfd, const void *buf, // 发送数据的起始地址 size_t len, // 要发送的字节数 int flags, // 无特殊要求为0 struct sockaddr *dest_addr, //接收方地址描述结构体的地址 sockaddr_in socklen_t addrlen // dest结构体的长度 sizeof()表示 ) 成功返回字节数,失败返回-1
-
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, //发送方地址描述结构体的地址 IPv4 sockaddr_in socklen_t *addrlen //src结构体的长度, ,返回src_addr结构体的字节数 ) 成功返回字节数,失败返回-1
-
int listen( int sockfd, //监听套接字 int backlog // 待处理,连接请求 被accept()处理,backlog不再影响(让监听套接字进入监听状态),成功0失败-1
-
int accept( int sockfd, struct sockaddr *addr, socklen_t *addrlen)
监听套接字,套接字来自那个地址,地址的结构体占用多少字节(接收客户端连接请求。)
返回的套接字是响应套接字,用于真正的数据通信
-
int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen)连接服务端 成功0,失败-1
-
ssize_t send(int sockfd, const void *buf, size_t len, int flags)发送数据 成功返回发送的字节数。
-
ssize_t recv(int sockfd, void *buf, size_t len, int flags) 成功返回接收的字节数。返回如果为0,对方关闭了数据发送通道(即半关闭)
-
notoa:功能是将网络地址转换成“.”点隔的字符串格式。
ntohs:作用是将一个16位数由网络字节顺序转换为主机字节顺序。
、
2. 常用头文件
#include <stdio.h>//标准输入输出头文件
#include <stdlib.h>//定义杂项函数及内存分配函数
#include <string.h>//字符串处理函数的头文件
#include <malloc.h>//分配 size 字节的存储区
#include <errno.h>// ISO C99标准错误代码
include <math.h>//定义数学函数
#include <dirent.h>//用于目录操作的头文件
#include <unistd.h>//标准符号常量和类型
#include <time.h>//定义关于时间的函数
#include <sys/types.h>//数据类型的声明
include <sys/socket.h> 主文件
include <arpa/inet.h>Internet地址转换
netinet/in.h - Internet地址
sys/select.h - select I/O模型
sys/epoll.h - epoll I/O模型
netdb.h - 网络数据库相关,例如域名解析
fcntl.h - 文件控制
3.多线程
-
头文件
#include<pthread.h>
-
常用函数
- int pthread_create(pthread_t *restrict tid, const pthread_attr_t *restrict attr, void (start_routine)(void *), void *restrict arg)
- int pthread_join(pthread_t tid, void **status)指定线程结束 需要用线程的执行结果
- int pthread_detach(pthread_t tid)不需要执行结果,释放,不等待结束
- void pthread_exit(void *retval)终止调用线程
线程同步
Mutex互斥量,当两个线程竞争使用资源时,为避免执行顺序引起的错误
-
Int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) //互斥量的线程启动前调用
若无特殊要求,第二个参数设置为null
-
int pthread_mutex_lock(pthread_mutex_t *mutex)
进入互斥量临界区
-
int pthread_mutex_unlock(pthread_mutex_t *mutex)
退出临界区
-
int pthread_mutex_destory(pthread_mutex_t *mutex)
当互斥线程全部结束时调用
信号量
-
int sem_init(sem_t *sem,int pshared,unsigned int value)
若信号量只供进程内线程调用,Pshared设置为0,参数value>=1,表示可用资源数量
-
int sem_wait(sem_t *sem) 进入临界区调用 (p()阻塞)
-
int sem_post(sem_t *sem) 退出临界区调用 (v()执行)
-
int sem_destory(sem_t *sem) 不再需要时释放
main() 进程的默认执行体、主线程 return结束后, 进程会结束,其他所有依赖线程终止。 pthread_exit()结束,主线程会结束,但其它线程的执行不受影响
4. 常用端口
TCP 80 http 53 dns 25 smtp 20/21 ftp 23 telnet 110 pop UDP 53 dns
69 tftp 161 snmp 22ssh
0:不作用或者作为特殊的用途
1~255:保留给特定的服务
256~1023:保留给其他服务
1024~4999:用作任意客户的端口
5000~65535:用作用户服务器端口
5. 常用命令
Ipconfig:查看网络接口信息
Ifconfig:查看所有网络接口信息
Ping:验证网络是否连通
Traceroute:路由追踪
Nslookup:域名解析
Route:查看linux服务器上的路由表
Netstat:查看端口的连接状态
netstat -anu4 可查看udp的ipv4下他的监听端口号(会显示ip地址 端口号)
chmod 7 udpc 设置权限
编译:gcc - o udpc1 udplc01.c(要生成的文件,源文件)
TCPDump: 对网络中传送的数据包完全截获下来提供分析
Wireshark:截取网络封包,显示出最为详细的网络封包资料
Nmap:网络连接端扫描软件,用来扫描网上电脑开放的网络连接端。确定哪些服务运行在哪些连接端,并且推断计算机运行哪个操作系统
Iptables: IP 信息包过滤系统,控制 IP 信息包过滤和防火墙配置
6.填空题
1、下列选项中,将端口号由本机字节顺序转换为网络字节顺序的函数()。
htonl****()或htons()
3、RFC中,HTTP服务采用的默认端口号是()。80
4、某些函数在单线程中执行时可以获得正确结果,但在多线程并发执行时出现了逻辑错误,原因是(可能发生死锁,线程不安全,内存泄漏等问题)。
5、在 Linux编程中,要使用 POSIX标准的线程库,需要包含哪一个头文件?(pthread.h)
6.在流式套接字上调用recv(),若返回值为0,表示(对方关闭了数据通道)。
7.Windows API 中,SendMessageO的作用是(发送消息)。
8.在 WinSock2编程时,除默认的库外,需要编译器单独加载的库是(ws2_32.lib)
9.让套接字进入监听状态的函数是()。listen(int sockfd,int backlog)
10.Linux中,使用信号量进行线程同步时,相当于操作系统中Р操作的函数调用是()。.
int sem_wait(sem_t *sem)
11.Linux中,线程A创建了线程B后,需要等待线程B执行完成后A再继续执行,则应在A中调用的线程控制函数是()。
int pthread_join(pthread_t tid,void **status)
12.函数inet addrO的作用是()
将x.x.x.x形式的IP地址串转换为32位、网络字节顺序的整数
13.套接字s工作在阻塞模式,在其上调用哪个函数时不会阻塞?(bind)
recv accept connect bind 都会阻塞
15、gethostbynameO的作用是( )。获取主机信息
二、简答题
-
网间进程通信需要解决的根本问题有哪些?需要注意哪些细节的处理?
1)网间进程的标识问题;
(2)如何与网络协议栈连接的问题;
(3)协议的识别问题;
(4)不同的通信服务的问题.
1)字节顺序:Internet采用Big Endian
2)字的长度
3)字节定界:UDP有界,TCP无界
4)字符编码(尤其是Java/Python等高级语言,网站开发中同样涉及)
5)日期、时间、等国际化和本地化处理(网站开发中同样涉及)
-
在 Linux的TCP并发服务端中,只关注是否有连接请求或数据到达,请用文字描述select模型的编程步骤,注明相应的函数和宏.…
FD_ZERO(…)
FD_SET(…) //将监听套接字存入读集合
while(…)
{
准备集合(循环)
设置timeout
select(…)
循环:若成功,逐一判断某套接字是否仍在某集合中,若是,执行相应的I/O操作或出错处理
}
-
什么是线程,什么是进程?
进程:
-
程序的运行实例,是软件的动态体现,侧重代码执行过程的管理和控制,通过运行执行预期功能
-
一个进程实体由程序代码、数据和进程控制块三部分构成。
线程:
- 描述代码的微观运行情况,是cpu调度的基本单位,线程依附于进程
- 线程自己不拥有系统资源,但它可与同一进程的其 它线程共享进程所拥有的全部资源
-
-
先执行左下角的客户端,发现第二个客户端根本没有执行,
提示连接被对端复位,为什么?(10分主观题)
1.1)tcpls01v2,可以正常运行,尽管可以启动多个客户端,但仍然只有一个客户端执行完全部计算。其它的客户端根本没有机会参与计算,为什么?
-
给客户端分配id的原因是什么?
-
实际应用中面临的复杂问题
假设套接字接收到的字节数是len,但应用程序读取的字节数是part,part <len,并且下次接收以前只处理了part/2个字节,后面该如何接收?前面的处理都比较简单、理想化,实际是比较复杂的。
假如应用缓冲区中接收到了50个字节,还有50个字节在系统缓冲区中还没有读,并且这次接收的50个字节只处理了25个字节,此时,应用缓冲区还有25个字节没有处理,后面再接收的那些数据放到缓冲区的什么位置,才能保证
- 前面没处理的字节不要丢失,不要被覆盖
- 整个缓冲区要有足够的空间来容纳我新读入的数据,需要去凑,因为流式套接字数据不分界
三。判断题
-
epoll模型中,最多可以同时管理64个套接字 ×
-
Linux下,大部分套接字函数的返回值为-1时,表示操作失败。 √
-
在Linux 中,服务端要使用1024以下的端口,需要具备root权限。 √
-
WinSock中的select模型用法与Linux中的完全相同。 ×
-
在 Linux中,若监听套接字工作在非阻塞模式,accept(返回的响应套接字也会自动工作在非阻塞模式, √
-
TCP通信中,一方调用send的次数和另一方调用recv的次数可以不同。
-
·发送4字节整数前,应将其转换为接收方硬件系统采用的字节顺序。
-
不同OS中,协议栈的实现形式不同,有的是在内核中,有的不是
-
Windows 下,WSAStartup()的作用是初始化 WinSock的执行环境。 √
-
wSAGetLastErrorO在任何情况下都可以获取上次套接字操作过程中
的错误代码。 ×
四、编程题
29、设计并实现一个基于Linux的TCP服务端,只有一个main函数,并发特性,工作IP地址是0.0.0.0,端口号是2022,所有套接字工作在非阻塞模式。不使用多线程。进入监听状态后,开始无限主循环:
1)准备工作。
2)无限主循环
2.1)等待客户端连接请求
2.2)当收到客户端连接请求后,显示格式化字符串“Connection %d from %s:%d”,其中第1个%d为服务端启动后连接的客户端的从1开始的序号,每当建立一个连接请求,该序号就增1。%s和第2个%d对应客户端套接字的点分十进制P地址和端口号,
2.3)与客户端进行交互,循环:
2.3.1)接收客户端发送过来的文字信息,不显示在屏幕上。客户端发送的数据组成为:2字节短整型数,其值为后面字符的个数,该值可以为0;从第3个字节开始是标准ASCII字符组成的字符串(不含字符串结束符)
2.3.2)将接收的文字信息中的问号替换为句号,再发送回客户端,反馈格式与接收格式相同
2.3.3)检测到网络故障后,显示"network error",结束交互
2.3.4)检测到客户端关闭了连接后,显示"connectionclosed",结束交互
2.5)关闭响应套接字
3)在主循环结束后,关闭监听套接字
要求:监听套接字为s0,响应套接字为s1,其它的变量名自行确定。
29、设计并实现一个基于Linux的TCP服务端,只有一个main函数,无并发特性,工作IP地址是0.0.0.0,端口号是2021,所有套接字工作在阻塞模式。进入监听状态后,开始无限主循环:
1)准备工作。
2)无限主循环
2.1)等待客户端连接请求
2.2)当收到客户端连接请求后,显示格式化字符串“Connection %d from %s:%d”,其中%d为服务端启动后连接的客户端的从1开始的序号,每当建立一个连接请求,该序号就增1。%s和第2个%d对应客户端套接字的点分十进制P地址和端口号,
2.3)与客户端进行交互,循环:
2.3.1)接收客户端发送过来的文字信息,不显示在屏幕上。客户端发送的数据组成为:2字节短整型数,其值为后面字符的个数,该值可以为0;从第3个字节开始是标准ASCII字符组成的字符串(不含字符串结束符)
2.3.2)将接收的文字信息中的问号替换为句号,再发送回客户端,反馈格式与接收格式相同
2.3.3)检测到网络故障后,显示"network error",结束交互
2.3.4)检测到客户端关闭了连接后,显示"connectionclosed",结束交互
2.5)关闭响应套接字
3)在主循环结束后,关闭监听套接字
要求:监听套接字为s0,响应套接字为s1,其它的变量名自行确定。
五、select模型
-
套接字存储在fd_set集合,select模型会监控一段时间内集合中所有有效的套接字是否“操作就绪”,
- 若是会保留下来
- 否则将该套接字移出集合
-
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds,struct timeval *timeout)
-
nfds : 三个集合中所包含的最大描述符值+1
-
readfds : 检查套接字可读性(响应套接字有数据到达,调用recv;对于监听套接字,有连接请求到达,可以调用accpet)
-
writefds :检查可写性 仅对响应套接字有效,成功调用 send()
-
exceptfds:异常套接字集合,设NULL会无限期等待至出错
-
timeout:超时时间设置,其类型是:
struct timeval {
long tv_sec; //seconds
long tv_usec; //microseconds
};//***timeout两个成员均为0,则select()会立即返回 ***
-
返回值:
- -1:错误,
- 0:规定时间内所有集合中的描述符都未变为就绪状态,
- >0:三个集合中处于就绪状态的描述符总数
-
-
select模型宏操作
-
FD_ZERO(fd_set *) 清空某个几个,仅进行一次
-
FD_SET(int, fd_set*)将某个描述符加入指定的集合
-
FD_ISSET(int, fd_set*) 调用select模型后,判断某套接字是否存在于集合中,若存在,则可以对其进行某种I/O或出错处理
-
FD_CLR(int, fd_set*) 将某个描述符从集合中移出,即不再关注该描述符
使用select模型的框架
-
-
select模型使用
FD_ZERO(...)//把set队列初始化成空队列
FD_SET(...) //将监套接字加入读集合
while(...)
{准备集合(循环)
设置timeout
select(...)
循环:若成功,逐一判断某套接字是否存在某集合中,
若失败,执行相应的I/O操作或出错处理
}
六、阻塞模式、非阻塞模式
-
阻塞模式
- 当调用某函数时,若操作条件不具备,函数会一直处于等待状态,程序的执行进程会在该函数调用处停顿,知道操作完成或发生错误为止
- recv() accept()connect() bind()
- 典型的阻塞操作
- accept:当没有新的连接请求到来时,会一直等待,直到新的连接请求到来或发现错误
- recv/recvfrom:当没有数据到达时,会一直等待,直到数据到达或出现错误
- 操作不具备一直等待
-
非阻塞模式
-
当调用某个函数时,无论操作条件是否具备,函数均会立即返回,程序的执行进程不会在该函数调用出停顿,而是继续执行。
-
通过返回值及errno判断函数返回的原因
-
返回值大于0,表示读取到了数据
-
返回值为-1,errno为EWOULDBLOCK表示操作条件不具备,表示无数据到达
其他:errno表示出现了错误
-
-
accept:当没有新的连接请求到来时,会一直等待
-
recv:当没有数据到达时,会一直等待
-
非阻塞模式编程
- socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0)
- fcntl(sock,F_SETFL,fcntl(sock,F_GETFL,0)|O_NONBLOCK);
-
非阻塞模式编程框架
n = recv(...)
if(n>0)
此时,数据已经读入到应用缓冲区,进行相应处理
else if(n == 0)
此时对方关闭了连接
else
if(errno == EAGAIN || errno == EWOULDBLOCK)
并非真正错误,而是无数据到达
-
七、UDP/TCP编程框架
-
UDP编程框架
服务器
1)创建数据报套接字
2)绑定(地址和端口信息,端口需要通知所有客户端)
3)等待接收客户端数据
4)收到数据后,按照通信规程(也可称为业务逻辑)处理,一般要反馈
5)在必要的情况下,关闭套接字
客户端工作过程 :
1)创建数据报套接字
2)根据服务端公布的地址、端口信息,向服务端发送数据
3)发送数据或反馈信息时,按与对方约定好的通信规程处理
4)结束后,关闭套接字
-
流式套接字编程框架
客户端
1)创建流式套接字
2)根据服务端公布的地址信息,连接到服务端
3)连接成功后,可以按照通信规程(业务逻辑)传输数据
3.1)发送数据:将数据从应用程序缓冲区复制到套接字的发送缓冲区,至于何时真正发送到对方,由协议栈决定
3.2)接收数据:将数据从套接字的接收缓冲区移动到应用程序的缓冲区,
4)数据传输完成后,可关闭套接
服务端
1)创建流式套接字,目的是监听连接请求
2)绑定对外公布的地址信息
3)确定连接请求队列大小,进入监听套接字监听状态
4)当有客户端的连接请求到来时,创建一个为该客户端提供1对1服务的流式套接字,它只起数据传输作用,称为响应套接字
5)响应套接字与客户端套接字成为通信双方,这个通信是有连接的,在此基础上,可以按照通信规程(业务逻辑)传输数据
5.1)发送数据:将数据从应用程序缓冲区复制到套接字的发送缓冲区,至于何时真正发送到对方,由协议栈决定
5.2)接收数据:将数据从套接字的接收缓冲区移动到应用程序的缓冲区,
6)数据传输完成后,可关闭响应套接字
7)一般情况下,不关闭监听套接字,除非有特殊要求