【问题汇总】
1、服务器和客户端的 函数参数 返回值 不太理解
TCP UDP
tcp
服务器端:
1. socket();功能是创建文件描述符,用于连接sockfd;
参数:
a. AF_INET IPv4
b. 协议类型
SOCK_STREAM TCP
SOCK_DGRAM UDP
SOCK_RAW 原始套接字
3. 0
系统默认自动帮助匹配对应协议
传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
返回 错误 < 0
> 0 返回的文件描述符
用法:
2. bind(); 绑定,将IP 端口号 协议 和sockfd进行绑定;
绑定之前填充结构体:
用法:
参数: sockfd
struct sockaddr 通用结构体 16字节
struct sockaddr_in clientaddr;
struct sockaddr_un
(struct sockaddr *)&clientaddr;
3. listen();用于监听,将主动套接字变为被动套接字;
sockfd
客户端同时连接服务器最大个数, 连接队列
成功返回0
出错返回-1
用法:
4. accept(); 阻塞等待客户端的连接,当有客户连接上来,
accept()返回,并且声称一个文件描述符acceptfd,
acceptfd用于通信;
参数:
sockfd
第二个和第三个参数如果传NULL,
表示服务器不关心具体是哪一个客户端连接的;
如果需要关心是哪个客户端,则需要传入第二和第三个参数
(struct sockaddr *)&clientaddr
用法示例:
5. recv();接收数据
recv(acceptfd,buf,sizeof(buf),0) //阻塞
recv(acceptfd,buf,sizeof(buf),MSG_DONTWAIT) //非阻塞
返回值:
< 0 错误;
==0 客户端退出,断开连接
> 0 成功接收到的字节个数
用法示例:
6. send();
send(acceptfd,buf,sizeof(buf),0)
返回值:
>0 成功发送的字节个数
-1 失败
用法示例:
客户端:
1. socket(); 功能是创建文件描述符,用于连接sockfd;
2. connect(); 用于连接的;
connect(sockfd, (struct sockaddr *)&serveraddr,addrlen);
3. send;
4. recv;
UDP:
服务器端:
1. socket();
填充结构体
2. bind();
3. recvfrom();
4. sendto();send
客户端:
1. socket()
填充结构体
2. sendto()
3. recvfrom();
2、多进程、多线程并发程序理解不太好
并发服务器
多进程 fork()
多线程
IO多路复用
思想:
1. 创建一个关于文件描述符的表(集合)
2. 将关心的文件描述符加入到集合当中
3. 调用函数
select poll 轮询机制
4. 循环判断是哪个文件描述符产生事件
(select返回之后会将除了自生那一位全部清0)
5. 做对应的操作
poll 轮询, 数据拷贝
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
epoll
参考代码 和 day4笔记
3、IO 多路复用的用法,以及各个参数及select 监听多个文件描述符的参数
4、epoll操作步骤和流程
6、unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];这一段是啥意思。
7、UDP聊天室如何让服务器对多个客户端发送数据
链表,
8、三次握手,四次挥手
【1】网络属性设置
getsockopt setsockopt
int setsockopt(int sockfd,int level,int optname,
const void *optval,socklen_t optlen)
功能: 设置套接字属性
参数:
sockfd
level指定控制套接字的层次.
可以取三种值:
1)SOL_SOCKET: 应用层
2)IPPROTO_IP: 网络层
3)IPPROTO_TCP:传输层
optname:
SO_REUSEADDR 允许重用本地地址和端口
optval
int val = 1;
&val
optlen
大小
如果想解决地址端口号重用的问题:
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));
前提是:
1. 得先创建sockfd,
2. 在bind函数之前调用setsockopt();
int getsockopt(int sockfd,int level,int optname,
void *optval,socklen_t *optlen);
功能: 获取套接字得属性
想获取sockfd套接字得接收缓冲区大小:
int optval;
socklen_t optlen = sizeo(optval);
getsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&optval,&optlen);
printf("recvbuf = %d\n",optval);
【2】网络超时检测
1. select/poll中都有设置
select(maxfd + 1,&readfd,NULL,NULL,
NULL);//没有超时检测的,NULL
select(maxfd + 1,&readfd,NULL,NULL,
&tv);
定义超时检测变量tv:
struct timeval tv = {1,0}; //1秒
struct timeval tv = {
.tv_sec = 1,
.tv_usec = 0,
};
如果select没有设置超时时间:
它的返回值有两种:
< 0 错误
> 0 表示有一个或多个事件产生
如果select设置了超时时间:
< 0 出错
> 0 表示有一个或多个事件产生
== 0 表示超时
示例:
===============================================
poll函数设置超时时间
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
poll函数第三个参数是设置超时时间
如果 -1 阻塞
其他 1000 (毫秒级别)
================================================
2. 通过setsockopt来设置超时时间
SO_RCVTIMEO 接收超时
struct timeval tv = {1,0};
服务器端通过设置acceptfd属性,来设置超时时间
setsockopt(acceptfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
用法:放到while循环里的accept函数后面
3. alarm(5) 闹钟 定时器
5秒之后会,会有一个信号产生(SGIALRM)
alarm会打断下一个系统调用
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
signum 信号
struct sigaction {
void (*sa_handler)(int);
};
========================================
void handler()
{
printf("timeout .....\n");
}
struct sigaction act;
sigaction(SIGALRM,NULL,&act); //1. 获取属性
act.sa_handler = handler; //2. 修改
sigaction(SIGALRM,&act,NULL); //3. 写回
在recv前调用alarm函数
alarm的 SIGALRM信号产生后会打断(终端)下面的系统调用recv;
代码中的用法示例:
========================================
【3】广播和组播
广播 (UDP协议)
(1)以192.168.1.0(255.255.255.0)网段为例,最大的主机地址192.168.1.255 IP是广播地址
(2)255.255.255.255在所有的网段中都是广播地址
广播代码流程:
广播的发送者:tcp client.c
1. 创建用户数据报套接字;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2. 缺省创建的套接字默认是
不允许广播数据包发送的,需要设置属性
setsockopt可以设置套接字属性
int optval = 1;
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeo(optval));
3. 接收方地址指定为广播地址,指定端口号
struct sockaddr_in recvaddr;
...
4. 发送数据包
sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&recvaddr,xxx);
广播的接收者:类似:tcp server.c
1. 创建用户数据报套接字
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2.
绑定IP地址(广播IP或0.0.0.0)和端口
绑定的端口必须和发送方指定的端口相同
struct sockaddr_in recvaddr,sendaddr;
bind(sockfd,(struct sockaddr *)&recvaddr,xxx);
3. 等待接收数据
recvfrom();
【4】组播
组播代码流程:
组播的发送者:
1. 创建用户数据报套接字
2. 指定接收方地址指定为组播地址 224.0.0.10
指定端口信息
3. 发送数据包
组播的接收者:
1. 创建用户数据报套接字
2. 加入多播组
struct ip_mreq {
struct in_addr imr_multiaddr; // 组播IP 224.0.0.10
struct in_addr imr_interface; // INA DDR_ANY
};
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.10");
mreq.imr_interface.s_addr = htons(INADDR_ANY);
setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
3.
绑定IP地址(加入组的组播IP或0.0.0.0)和端口
绑定的端口必须和发送方指定的端口相同
4. 等待接收数据