目录
1、tcp协议和udp协议
2、多线程并发和多进程并发:
(1)多进程并发服务端
(2)多进程并发客户端:
3、tcp:
4、粘包
5、UDP协议编程流程
(1)服务器端:
(2)客户端:
6、tcp状态:
7、tcp状态转移图:
1、tcp协议和udp协议
tcp协议:面向连接 可靠 流式服务
udp协议:无连接 不可靠 数据报
根据场景来决定使用什么协议
2、多线程并发和多进程并发:
多线程并发,如果线程出现失误可能导致整个进程失败,多进程互相不影响
(1)多进程并发服务端
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include<signal.h>int socket_init();
void do_run(int c)
{while(1){char buff[128]={0};int num=recv(c,buff,127,0);if(num<=0){break;}printf("child read:%s",buff);send(c,"ok",2,0);}
}
int main()
{signal(SIGCHLD,SIG_IGN);//处理僵死进程 一个是忽略信号,一个是wait();int sockfd=socket_init();if(sockfd==-1){printf("socket err\n");exit(1);}while(1){struct sockaddr_in caddr;int len=sizeof(caddr);int c=accept(sockfd,(struct sockaddr*)&caddr,&len);if(c<0){continue;}printf("c=%d\n",c);pid_t pid=fork();if(pid==-1){close(c);continue;}if(pid==0){close(sockfd);do_run(c);close(c);printf("child exit pid=%s\n",getpid());exit(0);}close(c);}}
int socket_init()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){return -1;}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){return -1;}res=listen(sockfd,5);if(res==-1){return -1;}return sockfd;}
(2)多进程并发客户端:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){exit(1);}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("connect err\n");exit(1);}while(1){char buff[128]={0};printf("input\n");fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}send(sockfd,buff,strlen(buff),0);memset(buff,0,sizeof(buff));recv(sockfd,buff,127,0);printf("recv=%s\n",buff);}close(sockfd);}
父进程没有关闭链接,子进程close()不会完成四次挥手
3、tcp:
先建立连接TCP三次握手
最后断开,TCP四次挥手
tcp的可靠性是以牺牲了开销为代价的
4、粘包
多次发送的数据被一次性收到了,误以为是一次性收到的
解决办法:让接收的时候能区分出来,用不同的报文、在报文前面描述数据有多大、不连续send
5、UDP协议编程流程
(1)服务器端:
1、创建套接字socket()
2、指定IP和端口bind()
3、接受数据recvfrom()
4、发送数据sendto()
5、关闭close()
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include<signal.h>int main()
{int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(sockfd==-1){return -1;}struct sockaddr_in saddr,caddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("bind err\n");exit(1);}int len=sizeof(caddr);while(1){char buff[128]={0};recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);printf("recv=%s\n",buff);sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));}}
(2)客户端:
1、创建套接字socket()
2、发送sendto()//需要指定对方的IP和端口
3、接收recvfrom()//需要指定对方的IP和端口
4、关闭close()
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include<signal.h>int main()
{int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(sockfd==-1){exit(1);}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");while(1){char buff[128]={0};printf("input\n");fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));memset(buff,0,128);int len=sizeof(saddr);recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);printf("recv=%s\n",buff);}close(sockfd);
}
对于udp编程,因为是无连接的,所以可以多个客户端发送,客户端关闭,服务器端不回收到任何数据,服务器端关闭后,对于客户端无影响。
协议不同可以使用同一个端口
6、tcp状态:
只有在握手和挥手的时候回引起TCP协议的变化,稳定收发连接的时候状态时不会发生改变的。
7、tcp状态转移图:
三次握手完成有一个established状态,四次挥手完成有一个time_wait()状态