目录
网络编程
internet历史
1957年,前苏联卫星上天 1958年,米国建立ARPA(国防部高级研究计划署) 1968年,ARPA提出 资源共享计算机网络,简称ARPAnet,“阿帕网”。实现互联不同的计算机。 早期arpanet使用的网络控制协议NCP(network control procotol) ncp不能互联不同类型的操作系统以及不同类型计算机,也没有纠错功能。
TCP/IP协议
internet中的世界语
TCP 协议:主要实现纠错能力:用来检测网络传输中的出错,并具备一定的纠错能力 IP协议:主要实现不同类型操作系统、计算机互联。 UDP协议:用户数据协议 ICMP:互联网控制信息协议 HTTP:超文本传输协议 ARP:地址解析协议
网络体系结构:
由于网络是非常复杂的,因此采用分而治之的方法涉及。
因此网络体系结构是指 网络的层次结构和每层使用的协议的集合。
OSI体系结构:七层模型,理想化的模型,尚未完全实现。
应用层:主要是一些应用程序,获得要传输的数据(qq键盘输入) HTTP/FTP/DNS歇息 表示层:对数据进程 格式定义、加密或者转换(格式定义、转换数据) MD5 加密协议 会话层:建立通信进程的逻辑名字和 物理名字之间的联系。(端口号) 传输层:提供可靠数据传输,差错处理及恢复(TCP)。流量控制(UDP)。 TCP/UDP 网络层:数据分组、路由选择。(数据分组筛选,由路由分发到目的) IP/ICMP 数据链路层:数据组成可发送 和 接受的帧 物理层:传输物理信号等。 两路交换机:数据链路层的交换(硬件) 三路交换机:网络层是交换(软件)
**TCP/IP体系结构:
internet 事实上的工业标准 应用层:常用协议(http:超文本传输协议、ftp:文件传输、dns:域名解析协议、smtp:邮件传输协议) 传输层:确定数据包交给主机那个任务 :tcp 、udp 网络层:实现端对端的传输:ip(互联网络)、icmp(互联网络控制管理协议,--》ping命令)、igmp(广播、组播) 网络接口物理层:屏蔽硬件的差异,向上一层提供统一接口。ARP:地址解析协议(ip地址-》MAC地址)、RARP:逆地址解析协议(MAC地址-》IP地址)。 MSS(max data message ):最大报文长度:1460 MTU(max transmission unit):最大传输单元:1500
***TCP 协议和UDP协议
1、tcp协议
传输控制协议,面向链接,数据传输安全可靠。(数据无失序、无重复、无丢失)。常用场景:对安全性要求较高的场合(密码传输):qq、微信的登陆与注册;大量数据传输 • 特点:由于面向链接,建立链接管道,因此数据传输效率相对较低
2、udp协议
传输控制协议,无连接,保证数据安全可靠性。使用场景:少量图片数据传输;媒体流数据传输;qq、微信聊天;无线网络传输;广播、组播。 • 特点:由于无链接,不用建立管道,因此数据传输效率相对较高。
网络预备知识:
1、socket
1、是一种编程接口 2、也是一个文件描述符 3、可用于TCP、UDP、IPX
2、套接字类型
1、流式套接字:SOCK_STREAM:提供一种面向链接的服务,用于TCP传输控制协议 2、数据报套接字:SOCK_DGRAM:提供无连接服务,用于UDP通信 3、原始套接字:SOCK_RAW: 提供底层通信(IP、TCMP、IGMP)
3、IP地址
网络中标识主机的编号,常以点分形式存在。192.168.2.177 IPV4地址:整个IP占32位,共四字节,每段8位表示(0~255) IPV6地址:整个ip占128位 IP地址由 网络号 + 主机号组成:192.168.2 + 177 其中 ”2“ 表示网段号,也就是那个路由网络中(192.168.2.1,所有路由器主机号都是”1。网关 网络IP分类:主要看IP(第一段)前8位组成 A类网:0- 127, 子网掩码:255.0.0.0 B :128-191, 子网掩码:255.255.0.0 C :192-223, 子网掩码:255.255.255.0 D :224-239,组播地址 E :240-255,保留测试地址
5、子网掩码
255.255.255.255
6、计算网络号
已知ip(c类网) 网络号:IP&(umask)192.168.2.177 &(255.255.255.0) =192.168.2 主机号:ip&(~umask)192.168.2.177 &(~(255.255.255.0)) =177
7、ip地址的转换
由于网络中只能识别二进制所以必须转换:将代码字符串的IP地址转换成 网络识别的二进制 主机字节序转网络字节序: in_addr_t inet_addr(const char * strptr) #include<arpa/inet.h> strptr:代码中字符串ip(主机字节序的IP地址) 返回: 返回网络字节序二进制首地址 网络字节序转主机字节序: char* inet_ntoa(stuct in_addr inaddr) #include<arpa/inet.h> inaddr:主机字节序 IP地址 返回值: 主机字节序 IP字符串 首地址
8、字节序:数据存储顺序
小端序:低位存低地址 ,ubuntu采用小段 大端序:地位存高地址,网络采用大端序
9、端口号
在主机中标识处理网络数据的进程,也是进程的id号 1-1023:总所周知的端口号,用户一般不能使用 1024-49151:已登记的端口号, 49152-65535 动态端口号, 主机字节序转网络字节序: u_long htonl( int long ) u_short htons(int post) 网络字节序转主机字节序 u_long int ntohl( u_long hodtlong) u_short ntohs(u_short hostshort)
客户端
与用户进行数据交互的界面程序即上位机程序。
服务器
为客户端提供数据服务的后台软件。
tcp服务器编程流程
1、创建套接字(socket()) 2、绑定 bind() 3、监听 listen() 4、接受连接 accept() 5、数据交互 recv()/send() read()/write() 6、关闭套接字 close() int socket(int domain, int type,int procotol) 功能:打开网卡设备,设置套接字类型 domain:IP协议地址族,IPV4(AF_INET) , IPV6(AF_INET6) type:套接字类型 SOCK_STREAM procotol: 通常为 0 返回值: 成功:返回套接字(网卡描述符) 失败:-1 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 功能:将IP地址、端口号与套接字进行绑定,将IP和端口共享到网络中。 socketfd:socket返回值(成功) addr:结构体指针(通用协议地址结构体的首地址 struct sockaddr { sa_family_t sa_family; //AF_INET char sa_data[14];// } internet协议地址结构体: struct sockaddr_in { sa_family_t sin_family;//AF_INET in_port_t sin_port; //网络字节序端口号 struct in_addr sin_addr; }; struct in_addr { uint32_t s_addr; //网络字节序ip地址 }; 结构体头文件: #include<arpa/inet.h> addrlen:结构体大小 struct sockaddr_in addr; addr.sin_fmily = AF_INET; addr.sin_port = htons(9527); INADDR_ANY(0.0.0.0) addr.sin_addr.s_addr = inet_addr("0.0.0.0"); int listen(int sockfd,int backlog) 功能:设置监听套接字,设置 客户端连接请求队列长度,也就是设置同一时刻能接受的最大请求数 监听完成就是启动服务器,套接字变成监听套接字 backlog:请求队列长度,在内核中开辟 返回值: 成功:0 失败:-1 int accpet(int sockfd,struct sockaddr* addr,socklen_t *len) 功能:阻塞等待,接受客户端连接,建立通信管道 如果 请求队列中没有客户端的请求,该函数就组设。有就立即接受连接,建立通道。
数据交互
函数原型: ssize_t read(int connfd, void *buf, size_t size); 参数: connfd:连接套接字(通信管道id) ssize_t recv(int connfd, void *buf, size_t len, int flags); 参数: flags:阻塞与非阻塞模式,通常0表示阻塞。 注意: 1、read读数据,默认为阻塞模式,如果读缓冲区有数据立即返回,无数据就等待 2、recv读数据可以设置 模式 3、当 连接断开时,read或recv立即返回 0值 ssize_t write(int connfd, void *buf, size_t size); 参数: connfd:连接套接字(通信管道id) ssize_t send(int connfd, void *buf, size_t len, int flags); 参数: flags:阻塞与非阻塞模式,通常0表示阻塞。 关闭套接字 int close(int sockfd);
客户端交互过程中,服务器意外结束,如果客户端继续发送数据,第一次成功,但二次会出现管道破裂终止程序运行。
笔试面试:
1、网络粘包(网络风暴)
数据的读写数据不匹配,就会导致多条数据包粘连在缓存区中。
调整读写速率 调整读取的每条数据包大小
2、客户端连接服务器成功过程(三次过程)
第一次:客户端向服务器发送 SYN=j 请求 第二次:服务器向客户端发 syn+ack 第三次:客户端向服务器 syn
为什么三次握手而不是两次:解决网络信道不可靠问题
3、客户端或者服务器断开时(四次挥手)
第一次挥手:客户端向 服务器发 fin 第二次挥手:服务器向客户端 fin 第三次:服务器向客户端 ack 第四次:客户端向服务器 ack 数据交互中,客户端突然断电。服务器如何判断客户端情况。 心跳包机制(每隔一段时间服务器发送一个包,没回应)
网络抓包
wir
UDP服务器、客户端
服务器
1、创建套接字 socket() 2、绑定 bind() while(1){ 3、接收数据 recvfrom() 4、发送数据 sendto() } 5、关闭
客户端
1、创建套接字socket 2、绑定(可选)bind while(1) { 3、发送 sendto() 4、接受数据 recvfrom() }
1、函数原型: ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 功能: 阻塞等待,接受数据,保存数据发送者的ip、端口信息 头文件: #include <sys/types.h> #include <sys/socket.h> 参数: sockfd :套接字 buf :存储数据的缓冲区首地址 len :要读取的数据字节数 flags :0(阻塞模式) src_addr:保存数据发送端 ip和端口号的 结构体首地址 addrlen :存储结构体的长度 返回值: 成功:实际读取的字节数 失败:-1,并设置错误信息 2、函数原型: ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 功能: 将buf地址上的前len个字节数据,写入 sockfd网络中, 发送到 dest_addr对应的目标主机中。 头文件: #include <sys/types.h> #include <sys/socket.h> 参数: sockfd :套接字 buf :待发送的数据缓冲区首地址 len :要发送的数据字节数 flags :0(阻塞模式) dest_addr:目标主机的ip和port存储的结构体首地址 addrlen :结构体的长度 返回值: 成功:发送的实际字节数 失败:-1,并设置错误信息
linux中的IO模型
1、阻塞io:最常用、最简单,但效率最低(read,write) 2、非阻塞io:防止进程阻塞在io操作上,但必须使用轮询机制(浪费cpu资源) 3、io多路复用,同时对多个io进行操作,效率比阻塞高。浪费的cpu资源比非阻塞少 4、信号驱动io
io多路复用
基本思想: 1、先构造一张有关文件描述符表 2、使用一个函数,检测该表中哪一个或多个文件描述符可进行 io操作,该函数就立即返回 该文件描述符的标志 3、根据该函数 返回的文件描述符,就可以进行io操作
select()
#include<stdio.h>
#include <sys/select.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd = open("/dev/input/mouse0", O_RDONLY);
if (fd < 0){
perror("open");
return -1;
}
//创建 文件描述符集合
fd_set rfd1, rfd2;
//清空集合
FD_ZERO(&rfd1);
//将带操作的文件描述符加入 集合中
FD_SET(0, &rfd1); //加入键盘的文件描述符
FD_SET(fd, &rfd1); //加入鼠标的文件描述符
int maxfd = fd + 1;
while(1){
// 由于select检测时,如果集合中有可读文件描述符,则该集合会被设置标志,立即返回(一次只能检测一个)
// 使用完成不会自动清除标志,如果不做任何处理,则下次循环,就不会设置新的标志
rfd2 = rfd1; //使用新的结集合覆盖 集合中的所有数据。
//select检测集合中哪个文件描述符可操作,原理:函数内部自动遍历 0~maxfd之间的所有文件描述符,加入集合的文件描述符才能返回标志
// 第2参数:描述符可读的集合
//参数3:描述符可写集合,NULL表示不检测;
//参数4:NULL不检测;
//参数5:NULL一直阻塞,0(不用)
// select函数在参数 5 规定时间内检测集合中,是否有可操作文件描述符。
// 如果没有,则阻塞;如果有则返回可操作描述符的数量,并设置标志
// 如果时间到了,则返回 0
int ret = select(maxfd, &rfd2, NULL, NULL, NULL);
if(ret < 0){
perror("select");
continue;
}
printf("ret: %d\n", ret);
if ( FD_ISSET(0, &rfd2) ){//检测判断 集合中被置位的文件描述符是谁(检测判断哪个文件描述符可操作)
char buf[32] = {0};
read(0, buf, sizeof(buf));
printf("keyBoard: %s\n", buf);
} else if( FD_ISSET(fd, &rfd2) ){
char buf[32] = {0};
read(fd, buf, sizeof(buf));
printf("mouse: %s\n", buf);
}
}
return 0;
}
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
int server_init(int port)
{
//1、创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0){
perror("socket");
return -1;
}
printf("sockfd: %d\n", sockfd);
//2、绑定
struct sockaddr_in addr; //定义Internet协议地质结构体变量,用来存储ip和port
addr.sin_family = AF_INET; //Internet 协议 IPV4
addr.sin_port = htons(port); //端口号
//addr.sin_addr.s_addr= inet_addr("0.0.0.0"); //0地址,绑定当前系统任意ip
addr.sin_addr.s_addr= htonl(INADDR_ANY);
if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
perror("bind");
return -1;
}
//3、监听
if( listen(sockfd, 5) < 0){
perror("listen");
return -1;
}
printf("server init success\nserver listening.....\n");
return sockfd;
}
int waitForClient(int listenfd)
{
struct sockaddr_in caddr; //定义结构体变量,用来保存成功的客户端信息
socklen_t len = sizeof(caddr);
int connfd = accept(listenfd, (struct sockaddr *)&caddr, &len);
if (connfd < 0){
perror("accept");
return -1;
}
printf("client ip: %s port: %u\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
return connfd;
}
int main(int argc, char *argv[])
{
int sockfd = server_init(9526);
if(sockfd < 0){
return -1;
}
// 创建描述符集
fd_set fd1, fd2;
FD_ZERO(&fd1);
FD_SET(sockfd, &fd1);
int maxfd = sockfd;
//4、接受连接
while(1){
fd2 = fd1;
struct timeval val = {5, 0};
if( select(maxfd + 1, &fd2, NULL, NULL, &val) <= 0){
printf("select error or time out\n");
continue;
}
for(int i = 0; i < maxfd+1; i++){ //将集合中0~maxfd中每个文件描述符都进行遍历判断
if ( FD_ISSET(i, &fd2) ){ //判断fd= i,的文件是否可用
if (i == sockfd){ //判断当前 集合中产生变化的 i是否是 sockfd
int connfd = waitForClient( i );
if(connfd < 0){
continue;
}
FD_SET(connfd, &fd1); //将客户端连接套接字加入集合,下次循环进行检测
maxfd = maxfd > connfd ? maxfd : connfd; //更新集合中最大的 文件描述符
} else { //只要集合中发生变化的不是 sockfd,那就一定是 connfd(连接套接字)
char buf[32] = {0};
int ret = read(i, buf, 32);
if(ret < 0){
perror("read");
continue;
} else if (ret == 0){
printf("client leaved\n");
} else {
printf("recv: %s\n", buf);
}
}
}
}
}
close(sockfd);
return 0;
}
poll()
#include<stdio.h>
#include <sys/select.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
int main(int argc, char *argv[])
{
int fd = open("/dev/input/mouse0", O_RDONLY);
if (fd < 0)
{
perror("open");
return -1;
}
//由于要检测的 文件描述符只有 2个,因此数组元素 2个
struct pollfd pfd[2];
//向数组元素中装填 要检测的文件描述符和 事件
pfd[0].fd = 0; //文件描述符
pfd[0].events = POLLIN; //带检测的事件:数据可读
pfd[1].fd = fd;
pfd[1].events = POLLIN;
while(1){
// 参数1:要检测的文件描述符和事件的结构体数组 首地址
// 参数2:要检测的文件描述符个数
// 参数3:超时设置, 单位 毫秒
// 成功:返回 非负值(可操作文件描述的元素的个数);超时:返回 0; 失败:返回-1
int ret = poll(pfd, 2, 5000);
if(ret < 0){
perror("poll");
continue;
} else if (ret == 0)
{
printf("time out\n");
continue;
}
for(int i = 0; i < 2; i++)
{
if( pfd[i].revents & pfd[i].events )
{ //判断数组中哪个元素的 revents 被置位
if( pfd[i].fd == 0)
{ //判断数组中产生事件的元素中 文件描述符为 谁
char buf[32] = {0};
read(pfd[i].fd, buf, 32);
printf("keyBoard: %s\n", buf);
} else if ( pfd[i].fd == fd){
char buf[32] = {0};
read(pfd[i].fd, buf, 32);
printf("mouse: %s\n", buf);
}
}
}
}
return 0;
}
以太网头:14个字节:目标mac地址(6字节)、源mac地址(6字节),两字节(0x0800:ipv4,)。
ip头(20字节):源IP、目标ip。
tcp头(20字节):源端口、目标端口
设置套接字属性
setsockopt()
广播和主播
同时给局域网中所有主机发报叫广播。
tcp轮循服务器
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
int main(int argc, char *argv[])
{
//1、创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0){
perror("socket");
return -1;
}
printf("sockfd: %d\n", sockfd);
//2、绑定
struct sockaddr_in addr; //定义Internet协议地质结构体变量,用来存储ip和port
addr.sin_family = AF_INET; //Internet 协议 IPV4
addr.sin_port = htons(9526); //端口号
//addr.sin_addr.s_addr= inet_addr("0.0.0.0"); //0地址,绑定当前系统任意ip
addr.sin_addr.s_addr= htonl(INADDR_ANY);
if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
perror("bind");
return -1;
}
//3、监听
if( listen(sockfd, 5) < 0){
perror("listen");
return -1;
}
printf("server init success\nserver listening.....\n");
// while(1);
//4、接受连接
while(1){
struct sockaddr_in caddr; //定义结构体变量,用来保存成功的客户端信息
socklen_t len = sizeof(caddr);
int connfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
if (connfd < 0){
perror("accept");
return -1;
}
printf("client ip: %s port: %u\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
char buf[32] = {0};
while(1){
#if 0
bzero(buf, sizeof(buf));
int ret = read(connfd, buf, sizeof(buf) );
if (ret < 0){
perror("read");
return -1;
} else if (ret == 0){
printf("client is leaved\n");
break;
} else {
printf("ret: %d %s\n", ret, buf);
if( !strncmp(buf, "quit", 4) ){
break;
} else if ( !strncmp(buf, "exit", 4)){
close(connfd);
close(sockfd);
return -1;
}
}
#endif
#if 0
char buf[32] = {0};
fgets(buf, 32, stdin);
int ret = write(connfd, buf, strlen(buf)-1);
printf("send: %d\n", ret);
#endif
write(connfd, "hello world", 11);
}
}
close(sockfd);
return 0;
}
udp轮循服务器
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
int main(int argc, char *argv[])
{
int fd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == fd)
{
perror("socket:");
return -1;
}
struct sockaddr_in addr;
addr.sin_port = htons(atoi(argv[1]));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind:");
return -1;
}
while(1)
{
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
char buf[130] = {0};
int ret = recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
if(ret == - 1)
{
perror("recvfrom:");
return -1;
}else{
printf("information from : ip= %s, post =%d, ret = %d, buf = %s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),ret,buf);
if(strncmp(buf,"quit",4) == 0)
{
printf("client quit\n");
}else if(strncmp(buf,"exit",4) == 0)
{
close(fd);
return 0;
}
}
sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&caddr,len);
}
return 0;
}
并发服务器
多进程
#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
int accept_client(int fd);//接收客户端连接
int init_socket(char *port);//初始化套接字
void handler(int no);//信号处理函数
int talk_to_client(int fd1);//接收客户端发出的信息
int main(int argc, char* argv[])
{
int fd1,pid,ret;
int fd = init_socket(argv[1]);
if(fd < 0)
{
printf("init_socket failed.\n");
return -1;
}
signal(SIGCHLD,handler);//捕捉自进程信号,进行处理
while(1)
{
fd1 = accept_client(fd);
if(fd1 == -1)
{
return -1;
}
pid = fork();
if(pid == -1)
{
perror("fork:");
}else if(pid == 0 )
{
talk_to_client(fd1);
exit(0);
}
}
close(fd);
exit(0);
return 0;
}
int init_socket(char *port)//初始化套接字
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd)
{
perror("socket:");
return -1;
}else{
printf("socket success.\n");
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port));
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
{
perror("bind:");
return -1;
}else{
printf("bind success.\n");
}
if(listen(fd,13) < 0)
{
perror("listen:");
return -1;
}else{
printf("listen........\n");
}
return fd;
}
int accept_client(int fd)//接收客户端连接
{
struct sockaddr_in addr;
socklen_t len;
int acce_fd = accept(fd,(struct sockaddr*)&addr,&len);
if(acce_fd == -1)
{
perror("accept:");
return -1;
}else{
printf("accept success.\n");
printf("ip:%s,port:%d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
}
return acce_fd;
}
void handler(int no)//信号处理函数
{
int pid = waitpid(-1,NULL,WNOHANG);//非阻塞等待子进程推出
if(-1 == pid)
{
perror("waitpid :");
}else{
printf("signal: child quit,pid:%d\n",pid);
}
}
int talk_to_client(int fd1)
{
char buf[128] = {0};
int ret;
while(1)
{
ret = recv(fd1,buf,sizeof(buf),0);
if(ret == -1)
{
perror("recv:");
break;
}else if(ret == 0){
printf("client error quit");
break;
}else{
printf("recv:%s\n",buf);
if(strncmp(buf,"quit",4) == 0)
break;
}
}
close(fd1);
exit(-1);
}
多线程
#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<pthread.h>
int accept_client(int fd);//接收客户端连接
int init_socket(char *port);//初始化套接字
void handler(int no);//信号处理函数
void* talk_to_client(void* fd1);//接收客户端发出的信息
int main(int argc, char* argv[])
{
int fd1,pid,ret;
int fd = init_socket(argv[1]);
if(fd < 0)
{
printf("init_socket failed.\n");
return -1;
}else{
printf("fd = %d\n",fd);
}
// signal(SIGCHLD,handler);//捕捉自进程信号,进行处理
while(1)
{
fd1 = accept_client(fd);
if(fd1 == -1)
{
break;
}
pthread_t tid;
ret = pthread_create(&tid,NULL,talk_to_client,(void *)&fd1);
if(ret < 0)
{
perror("pthread_create:");
break;
}
pthread_detach(tid);
}
close(fd);
return 0;
}
int init_socket(char *port)//初始化套接字
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd)
{
perror("socket:");
return -1;
}else{
printf("socket success.\n");
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port));
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
{
perror("bind:");
return -1;
}else{
printf("bind success.\n");
}
if(listen(fd,13) < 0)
{
perror("listen:");
return -1;
}else{
printf("listen........\n");
}
return fd;
}
int accept_client(int fd)//接收客户端连接
{
printf("fd :%d\n",fd);
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int acce_fd = accept(fd,(struct sockaddr*)&addr,&len);
if(acce_fd == -1)
{
perror("accept:");
return -1;
}else{
printf("accept success.\n");
printf("ip:%s,port:%d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
}
return acce_fd;
}
void handler(int no)//信号处理函数
{
int pid = waitpid(-1,NULL,WNOHANG);//非阻塞等待子进程推出
if(-1 == pid)
{
perror("waitpid :");
}else{
printf("signal: child quit,pid:%d\n",pid);
}
}
void* talk_to_client(void* fd1)
{
int ret;
int fd = *(int*)fd1;
while(1)
{
char buf[128] = {0};
ret = read(fd,buf,sizeof(buf));
if(ret == -1)
{
perror("recv:");
break;
}else if(ret == 0){
printf("client error quit");
break;
}else{
printf("recv:%s\n",buf);
}
}
close(fd);
pthread_exit(NULL);
}
io多路复用
select
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/select.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
int init_sock(char* port);//初始化套接字.
int accpet_fd(int fd);//接受链接
int main(int argc, char *argv[])
{
int ret;
int fd = init_sock(argv[1]);
if(fd == -1)
{
printf("init_sock failed.\n");
return -1;
}
fd_set fid1,fid2;//创建两个文描述表
FD_ZERO(&fid1);//第一个文件描述符表清空
FD_SET(fd,&fid1);//套接字加入文件描述符
int maxfd = fd;
//struct timeval val = {5};//时间结构赋值为5秒
while(1)
{
fid2 = fid1;
struct timeval val = {5};//时间结构赋值为5秒
ret = select(maxfd+1,&fid2,NULL,NULL,&val);
if(ret == -1)
{
perror("select:");
return -1;
}else if(ret == 0)
{
printf("time out\n");
}else{
printf("select_ret= %d\n",ret);
}
for(int i =0 ; i <= maxfd; i++ )
{
if(FD_ISSET(i,&fid2))//检测可用文件描述符是否是i
{
if(i == fd)//判断文件描述符是否是套接字
{
int connetc_fd = accpet_fd(fd);
if(connetc_fd == -1)
{
printf("accpet_fd failed\n");
}else{
FD_SET(connetc_fd,&fid1);//将新客户端加入文件集合
maxfd = (maxfd > connetc_fd)?maxfd:connetc_fd;
}
printf("i = %d, maxfd= %d\n",i,maxfd);
break;
}else{//不是套接字,就需要读取
char buf[128] = {0};
ret = recv(i,buf,sizeof(buf),0);
if(ret == -1)
{
perror("recv:");
}else if(ret == 0)
{
printf("client error quit:\n");
FD_CLR(i,&fid1);
close(i);
maxfd = (maxfd > i)?maxfd:i;
}else{
printf("ret:%d, recv:%s\n",ret,buf);
if(strncmp(buf,"quit",4) == 0)
{
printf("clientd quit\n");
FD_CLR(i,&fid1);
close(i);
maxfd = (maxfd > i)?maxfd:i;
}else if(strncmp(buf,"exit",4) == 0)
{
printf("service quit\n");
return 0;
}
}
break;
}
}
}
}
return 0;
}
int init_sock(char* port)//初始化套接字.
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd == -1)
{
perror("socket:");
return -1;
}else{
printf("socket success.\n");
}
struct sockaddr_in addr;
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
addr.sin_family = AF_INET;
addr.sin_port = htons (atoi(port));
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
perror("bind");
return -1;
}else{
printf("bind success\n");
}
if(listen(fd,13) == -1 )
{
perror("listen");
return -1;
}else{
printf("listen success.\n");
}
return fd;
}
int accpet_fd(int fd)
{
struct sockaddr_in addr;
socklen_t len ;
int fd_accept = accept(fd,(struct sockaddr*)&addr,&len);
if(fd_accept == -1)
{
perror("accept:");
return -1;
}else{
printf("accept success.\n");
}
return fd_accept;
}
poll
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<poll.h>
#include<string.h>
#include<stdlib.h>
int init_socket(char *port);//初始化套接字
int accept_fd(int fd);//等待接受链接
int main(int argc, char* argv[])
{
int ret,i,con_fd;
int connect = 1;
struct pollfd fid[13] ;
char buf[123] = {0};
int fd = init_socket(argv[1]);
if(fd == -1)
{
printf("init_socket failed.\n");
return -1;
}
fid[0].events = POLLIN;
fid[0].fd = fd;
while(1)
{
ret = poll(fid,connect,3000);//检测是否有文件描述符可用
if(ret == -1)
{
perror("poll:");
return -1;
}else if(ret == 0 )
{
printf("time out\n");
}else{
printf("poll_ret = %d\n",ret);
}
for( i=0 ; i < connect; i++ )
{
if(fid[i].revents & POLLIN)//检测第i个文件的revents是否被置位 ,并自动还原置位以便下次检测
{
if(fid[i].fd == fd)
{
con_fd = accept_fd(fd);
if(con_fd == -1)
{
printf("accept_fd failed\n");
return -1;
}else{
fid[connect].fd = con_fd;//新的客户端加入数组
fid[connect].events = POLLIN;
connect++;
}
}else{
bzero(buf,sizeof(buf));
ret = read(fid[i].fd,buf,sizeof(buf));
if(ret == -1)
{
perror("read:");
continue;
}else if(ret == 0)
{
printf("client error quit\n");
continue;
}else{
printf("red:%s\n",buf);
}
}
}
}
}
return 0;
}
int init_socket(char *port)//初始化套接字
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd == -1)
{
perror("socket:");
return -1;
}else{
printf("socket success.\n");
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port));
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
perror("bind");
return -1;
}else{
printf("bind success.\n");
}
if(listen(fd,13) == -1)
{
perror("listen:");
}else{
printf("listen.....\n");
}
return fd;
}
int accept_fd(int fd)
{
struct sockaddr_in addr;
socklen_t len;
int fd1 = accept(fd,(struct sockaddr*)&addr,&len);
if(fd == -1)
{
perror("acept:");
return -1;
}
return fd1;
}
广播、组播只能用UDP
广播
接收
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char* argv[])
{
int socketfd = socket(AF_INET,SOCK_DGRAM,0);
if(socketfd == -1)
{
perror("socket:");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.2.255");
addr.sin_port = htons(8888);
if(bind(socketfd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
{
perror("bind:");
return -1;
}
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
while(1)
{
char buf[128] = {0};
int ret = recvfrom(socketfd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
if(ret == -1)
{
perror("recvfrom:");
return -1;
}
printf("ip:%s,port:%d,recv:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buf);
}
return 0;
}
发送
#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
int main(int argc, char* argv[])
{
int socketfd = socket(AF_INET,SOCK_DGRAM,0);
if(socketfd == -1)
{
perror("socket:");
return -1;
}
int on = 1;
if(setsockopt(socketfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on)) < 0)
{
perror("setsockopt:");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = inet_addr("192.168.2.255");
while(1)
{
char buf[128] = {0};
fgets(buf,sizeof(buf),stdin);
if(sendto(socketfd,buf,strlen(buf)-1,0,(struct sockaddr*)&addr,sizeof(addr))<0)
{
perror("sedto");
}
}
return 0;
}
组播
接收
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char* argv[])
{
int socketfd = socket(AF_INET,SOCK_DGRAM,0);
if(socketfd == -1)
{
perror("socket:");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.2.255");
addr.sin_port = htons(8888);
if(bind(socketfd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
{
perror("bind:");
return -1;
}
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
while(1)
{
char buf[128] = {0};
int ret = recvfrom(socketfd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
if(ret == -1)
{
perror("recvfrom:");
return -1;
}
printf("ip:%s,port:%d,recv:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buf);
}
return 0;
}
发送
#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
int main(int argc, char* argv[])
{
int socketfd = socket(AF_INET,SOCK_DGRAM,0);
if(socketfd == -1)
{
perror("socket:");
return -1;
}
int on = 1;
if(setsockopt(socketfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on)) < 0)
{
perror("setsockopt:");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = inet_addr("192.168.2.255");
while(1)
{
char buf[128] = {0};
fgets(buf,sizeof(buf),stdin);
if(sendto(socketfd,buf,strlen(buf)-1,0,(struct sockaddr*)&addr,sizeof(addr))<0)
{
perror("sedto");
}
}
return 0;
}