linux网络编程1基础概念和CS模型

#相关概念
(1)协议:一组规则
(2)OSI七层模型;TCP/IP四层模型
应用层协议:http;ftp;nfs;ssh;telnet
传输层协议:TCP;UDP
网络层协议:IP;ICMP;IGMP
链路层协议:以太网帧协议(根据MAC地址,完成数据包传输);ARP(根据IP地址获取MAC 地址)
(3)网络传输流程:数据没有封装之前,不能在网络中传递
(4)IP地址:可以在网络环境中,唯一标识一台主机;
端口号:可以在网络的一台主机上,唯一标识一个进程;
IP地址+端口号:可以在网络环境中,唯一标识一个进程;
UDP协议:16位源端口号,16位目的端口号;
IP协议:16位源端口号,16位目的端口号;32位序号,32位确认序号;6个标志位;16位窗口大小
(5)网络套接字socket:
在通信过程中,套接字一定是成对出现的;
一个文件描述符指向一个套接字,该套接字内部由内核借助两个缓冲区实现。
(6)C/S客户端服务器模型和B/S网页服务器模型:
C/S优点:缓存大量数据,协议选择灵活,速度快
C/S缺点:安全性,跨平台,开发工作量大
B/S优点:安全性,跨平台,开发工作量小
B/S缺点:不能缓存大量数据,严格遵守http
(7)以太网帧协议:
ARP协议:根据IP地址获取MAC地址
以太网帧协议:根据MAC地址,完成数据包传输
IP协议:
版本:IPV4、IPV6
TTL:设置数据包在路由节点中的跳转上限,每经过一个路由节点,该值减一
源IP:32位
目的IP:32位
(8)网络字节序:
小端法:(pc本地存储) 高位存高地址,低位存低地址;
大端法:(网络存储) 高位存低地址,低位存高地址;
htonl 本地》网络IP
htons 本地》网络PORT
ntohl 网络》本地IP
ntohs 网络》本地PORT
(9)TCP通信流程分析:
server:socket();bind();listen();accept();read(fd);do;write(fd);close()
client:socket();connect();write();read();显示;close()
(10)三次握手:
主动发起连接请求端,发送SYN标志位,请求建立连接。携带序号号、数据字节数(0)、滑动窗口大小;
被动接受连接请求端,发送ACK标志位,同时携带SYN请求标志位。携带序号、确认序号、数据字节数(0)、滑动窗口大小;
主动发起连接请求端,发送ACK标志位,应答服务器连接请求。携带确认序号。
(11)四次挥手:
主动关闭连接请求端,发送FIN标志位;
被动关闭连接请求端,应答ACK标志位; ------半关闭完成
被动关闭连接请求端,发送FIN标志位;
主动关闭连接请求端,应答ACK标志位。 ------连接全部关闭
(12)滑动窗口:
发送给连接对端,本端缓冲区大小(实时),保证数据不会丢失。
(13)TCP状态时序图
1、主动发起连接请求端:CLOSE–发送SYN–SEND_SYN–接收ACK、SYN–SEND_SYN–发送ACK–ESTABLISHED(数据通信态)
2、主动关闭连接请求端:ESTABLISHED(数据通信态)–发送FIN–FIN_WAIT1–接收ACK–FIN_WAIT2(半关闭)–接收对端发送FIN–FIN_WAIT2(半关闭)–回发ACK–TIME_WAIT–TIME_WAIT(只有主动关闭连接方会经历该状态)–等2MSL时长–CLOSE
3、被动接收连接请求端:CLOSE–LISTEN–接收SYN–LISTEN–发送ACK、SYN–SYN_RCVD–接收ACK–ESTABLISHED(数据通信态)
4、被动关闭连接请求端:ESTABLISHED(数据通信态)–接收FIN–ESTABLISHED(数据通信态)–发送ACK–CLOSE_WAIT(说明对端处于半关闭状态)–发送FIN–LAST_ACK–接收ACK–CLOSE
(14)select多路IO转换也可以实现C/S模式通信,原理是借助内核,使用select来监听客户端连接和数据通信事件
C/S模式服务器代码示例:

#include<stdio.h>
#include<ctype.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define SERV_PORT 9527
void sys_err(const char *str){
	perror(str);
	exit(1);
}
int main(int argc,char *argv[]){
	int lfd=0,cfd=0;
	int ret,i;
	char buf[BUFSIZ],client_IP[1024];
	struct sockaddr_in serv_addr,clit_addr;
	socklen_t clit_addr_len;
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_port=htons(SERV_PORT);
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	
	lfd=socket(AF_INET,SOCK_STREAM,0);
	if(lfd==-1)
		sys_err("socket error");
	bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	listen(lfd,128);
	clit_addr_len=sizeof(clit_addr);
	cfd=accept(lfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
	if(cfd==-1)
		sys_err("accept error");
	printf("client ip:%s,port:%d\n",inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),ntohs(clit_addr.sin_port));
	while(1){
		ret=read(cfd,buf,sizeof(buf));
		write(STDOUT_FILENO,buf,ret);
		for(i=0;i<ret;i++)
			buf[i]=toupper(buf[i]);
		write(cfd,buf,ret);
	}
	close(lfd);
	close(cfd);
	return 0;
}

C/S模式客户端代码示例:

#include<stdio.h>
#include<ctype.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define SERV_PORT 9527
void sys_err(const char * str){
	perror(str);
	exit(1);
}
int main(int argc,char *argv[]){
	int cfd;
	int conter=10;
	char buf[BUFSIZ];
	struct sockaddr_in serv_addr;
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_port=htons(SERV_PORT);
	inet_pton(AF_INET,"113.54.205.185",&serv_addr.sin_addr.s_addr);
	
	cfd=socket(AF_INET,SOCK_STREAM,0);
	if(cfd==-1)
		sys_err("socket error");
	int ret=connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	if(ret!=0)
		sys_err("connect error");
	while(--conter){
		write(cfd,"hello",5);
		sleep(1);
		ret=read(cfd,buf,sizeof(buf));
		write(STDOUT_FILENO,buf,ret);
	}
	close(cfd);
	return 0;
}

多进程通信服务器代码示例

#include<stdio.h>
#include<ctype.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define SRV_PORT 9999
void catch_child(int signum){
	while(waitpid(0,NULL,WNOHANG)>0);
	return;
}
int main(int argc,char *argv[]){
	int lfd,cfd;
	pid_t pid;
	struct sockaddr_in srv_addr,clt_addr;
	socklen_t clt_addr_len;
	char buf[BUFSIZ];
	int ret,i;
	
	bzero(&srv_addr,sizeof(srv_addr));            //将地址结构清零
    srv_addr.sin_family=AF_INET;
	srv_addr.sin_port=htons(SRV_PORT);
	srv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	
	lfd=socket(AF_INET,SOCK_STREAM,0);
	int opt=1;                                                            //设置端口复用
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,(void *)&opt,sizeof(opt));      //设置端口复用
	bind(lfd,(struct sockaddr *)&srv_addr,sizeof(srv_addr));
	listen(lfd,128);
	clt_addr_len=sizeof(clt_addr);
	while(1){
		cfd=accept(lfd,(struct sockaddr *)&clt_addr,&clt_addr_len);
		pid=fork();
		if(pid<0)
			perror("fork error");
		else if(pid==0){
			close(lfd);
			for(;;){
				ret=read(cfd,buf,sizeof(buf));
				for(i=0;i<ret;i++)
					buf[i]=toupper(buf[i]);
				write(cfd,buf,ret);
				write(STDOUT_FILENO,buf,ret);
		//		ret=read(cfd,buf,sizeof(buf));
				if(ret==0){
					close(cfd);
					exit(1);
				}
			}
		}
		else{
			struct sigaction act;
			act.sa_handler=catch_child;
			sigemptyset(&act.sa_mask);
			act.sa_flags=0;
			ret=sigaction(SIGCHLD,&act,NULL);
			if(ret!=0)
				perror("sigaction error");
			close(cfd);
			continue;
		}
	}
	return 0;
}

多线程通信服务器代码示例

#include<stdio.h>
#include<ctype.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<fcntl.h>

#define MAXLINE 8192
#define SERV_PORT 8000
struct s_info{                                      //定义一个结构体,将地址结构跟cfd捆绑
	struct sockaddr_in cliaddr;
	int connfd;
};
void *do_work(void *arg){
	int n,i;
	struct s_info *ts=(struct s_info*)arg;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];
	while(1){
		n=read(ts->connfd,buf,MAXLINE);
		if(n==0){
			printf("the client %d closed...\n",ts->connfd);
			break;
		}
		for(i=0;i<n;i++)
			buf[i]=toupper(buf[i]);
		write(STDOUT_FILENO,buf,n);
		write(ts->connfd,buf,n);
	}
	close(ts->connfd);
	return (void *)0;
} 
int main(void){
	struct sockaddr_in servaddr,cliaddr;
	socklen_t cliaddr_len;
	int listenfd,connfd;
	pthread_t tid;
	struct s_info ts[256];
	int i=0;
	listenfd=socket(AF_INET,SOCK_STREAM,0);
	int opt=1;                                                                //设置端口复用
	setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(void *)&opt,sizeof(opt));    //设置端口复用
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	servaddr.sin_port=htons(SERV_PORT);
	bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
	listen(listenfd,128);
	printf("accepting client connect...\n");
	while(1){
		cliaddr_len=sizeof(cliaddr);
		connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
		ts[i].cliaddr=cliaddr;
		ts[i].connfd=connfd;
		pthread_create(&tid,NULL,do_work,(void*)&ts[i]);
		pthread_detach(tid);
		i++;
	}	
	return 0;
}

select实现C/S通信服务器代码示例

#include<stdio.h>
#include<ctype.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define SERV_PORT 6666
int main(int argc,char *argv[]){
	int lfd,cfd;
	char buf[BUFSIZ];
	struct sockaddr_in clie_addr,serv_addr;
	socklen_t clie_addr_len;
	lfd=socket(AF_INET,SOCK_STREAM,0);
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));    //设置端口复用
	bzero(&serv_addr,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_addr.sin_port=htons(SERV_PORT);
	bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	listen(lfd,128);
	
	fd_set rset,allset;                                         //定义读集合,备份集合allset
	int ret,maxfd=0,n,i,j;
	maxfd=lfd;                                                  //最大文件描述符
	FD_ZERO(&allset);                                           //清空 监听集合
	FD_SET(lfd,&allset);                                        //将待监听的lfd添加到监听集合中
	while(1){
		rset=allset;                                            //备份
		ret=select(maxfd+1,&rset,NULL,NULL,NULL);               //使用select监听
		if(ret<0)
			perror("select error");
		if(FD_ISSET(lfd,&rset)){                                //lfd满足监听的读事件
			clie_addr_len=sizeof(clie_addr);
			cfd=accept(lfd,(struct sockaddr *)&clie_addr,&clie_addr_len);//建立连接,不会阻塞
			FD_SET(cfd,&allset);                                //将新产生的fd添加到监听集合中,监听数据读事件
			if(maxfd<cfd)                                       //修改最大文件描述符
				maxfd=cfd;
			if(ret==1)
				continue;
		}
		for(i=lfd+1;i<=maxfd;i++){                              //处理满足读事件的fd
			if(FD_ISSET(i,&rset)){
				n=read(i,buf,sizeof(buf));
				if(n==0){                                        //检测到客户端已经关闭连接
					close(i);                   
					FD_CLR(i,&allset);                          //将关闭的fd,移除监听集合
				}
				else if(n==-1)
					perror("read error");
				else{
					for(j=0;j<n;j++)
						buf[j]=toupper(buf[j]);
					write(i,buf,n);
					write(STDOUT_FILENO,buf,n);
	                        }
			}
		}
	}
	close(lfd);
	return 0;
}

select实现C/S通信服务器(使用数组优化遍历版本)代码示例

#include<stdio.h>
#include<ctype.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define SERV_PORT 6666
int main(int argc,char *argv[]){
	int i,j,n,maxi;
	int ret,client[FD_SETSIZE];                               //FD_SETSIZE默认为1024
	int maxfd,lfd,cfd,sockfd;
	char buf[BUFSIZ],str[INET_ADDRSTRLEN];                    //INET_ADDRSTRLEN 16
	fd_set rset,allset;
	struct sockaddr_in clie_addr,serv_addr;
	socklen_t clie_addr_len;
	lfd=socket(AF_INET,SOCK_STREAM,0);
	int opt=1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));    //设置端口复用
	bzero(&serv_addr,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_addr.sin_port=htons(SERV_PORT);
	bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	listen(lfd,128);

	maxfd=lfd;                                                  //最大文件描述符
	maxi=-1;                                                    //将来用作client[]的下标,初始值指向0元素之前下标位置
	for(i=0;i<FD_SETSIZE;i++)
		client[i]=-1;
	FD_ZERO(&allset);                                           //清空 监听集合
	FD_SET(lfd,&allset);                                        //将待监听的lfd添加到监听集合中
	while(1){
		rset=allset;                                            //备份
		ret=select(maxfd+1,&rset,NULL,NULL,NULL);               //使用select监听
		if(ret<0)
			perror("select error");
		if(FD_ISSET(lfd,&rset)){                                //lfd满足监听的读事件
			clie_addr_len=sizeof(clie_addr);
			cfd=accept(lfd,(struct sockaddr *)&clie_addr,&clie_addr_len);//建立连接,不会阻塞
			printf("received from %s at PORT %d\n",inet_ntop(AF_INET,&clie_addr.sin_addr,str,sizeof(str)),ntohs(clie_addr.sin_port));
			for(i=0;i<FD_SETSIZE;i++)
				if(client[i]<0){
					client[i]=cfd;                              //找出client[]中没有使用的位置
					break;                                      //保存accept返回的文件描述符到client[]里
				}
			if(i==FD_SETSIZE){                                  //达到select能监控的文件个数上限1024
				fputs("too many clients\n",stderr);
				exit(1);
			}
			FD_SET(cfd,&allset);                                //将新产生的fd添加到监听集合中,监听数据读事件
			if(maxfd<cfd)                                       //修改最大文件描述符
				maxfd=cfd;
			if(i>maxi)                                          //保证maxi存的总是client[]最后一个元素下标
				maxi=i;
			if(ret==1)
				continue;
		}
		for(i=0;i<=maxi;i++){                                   //检测哪个client有数据就绪
			if((sockfd=client[i])<0)
				continue;
			if(FD_ISSET(sockfd,&rset)){
				n=read(sockfd,buf,sizeof(buf));
				if(n==0){                                        //检测到客户端已经关闭连接
					close(sockfd);                   
					FD_CLR(sockfd,&allset);                          //将关闭的fd,移除监听集合
					client[i]=-1;
				}
				else if(n==-1)
					perror("read error");
				else{
					for(j=0;j<n;j++)
						buf[j]=toupper(buf[j]);
					write(sockfd,buf,n);
					write(STDOUT_FILENO,buf,n);
	            }
			}
		}
	}
	close(lfd);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值