广元市建设局官方网站,wordpress 捐助,室内设计公司图片,wordpress虚拟交易模板网络编程模型 c/s 模型#xff1a;客户端服务器模型b/s 模型#xff1a;浏览器服务器模型1.tcp网络流程 服务器流程#xff1a; 1.创建套接字2.完善服务器网络信息结构体3.绑定服务器网络信息结构体4.让服务器处于监听状态5.accept阻塞等待客户端连接信号6.收发数据7.关闭套…网络编程模型 c/s 模型客户端服务器模型b/s 模型浏览器服务器模型1.tcp网络流程 服务器流程 1.创建套接字2.完善服务器网络信息结构体3.绑定服务器网络信息结构体4.让服务器处于监听状态5.accept阻塞等待客户端连接信号6.收发数据7.关闭套接字客户端流程 1.创建套接字2.连接connect3.收发数据4.关闭套接字2.函数
2.1socket
#include sys/types.h
#include sys/socket.hint socket(int domain, int type, int protocol);
功能创建套接字参数domain通信域Name Purpose Man pageAF_UNIX,AF_LOCAL Local communication(本地通信) unix(7)AF_INET IPv4 Internet protocols ip(7)AF_INET6 IPv6 Internet protocols ipv6(7)AF_PACKET 原始套接字 packet(7)type:套接字的类型SOCK_STREAM TCPSOCK_DGRAM UDP使用SOCK_RAW 原始套接字使用protocol附加文件没有写0返回值成功创建的新套接字(文件描述符)失败-1 重置错误码2.2bind
#include sys/types.h
#include sys/socket.hint bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能绑定套接字和网络信息结构体参数sockfd:socket函数产生的套接字文件描述符(socket函数返回值)addr避免冲突警告2.3listen
#include sys/types.h
#include sys/socket.hint listen(int sockfd, int backlog);产生半连接队列功能将套接字设置成被动监听状态只有这个状态的套接字才能等待客户端连接参数sockfd:socket函数返回值backlog半连接队列的长度一般传 5 10 都行 不是0就行返回值成功0失败-1 重置错误码accept
#include sys/types.h
#include sys/socket.hint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);产生全连接队列返回值创 建的文件描述符
功能提取半连接队列中的第一个客户端连接成功放到函数产生的全连接队列中专门 用于和当前客户端通信返回的套接字和原套接字类型相同参数sockfd:listen后的sockfdaddr客户端的网络信息结构体首地址不关心写NULLaddrlen客户端addr长度不关心写NULL返回值成功创建的新的文件描述符 专门用于和当前客户端通信失败-1 重置错误码connect
#include sys/types.h
#include sys/socket.hint connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能与服务器建立连接 参数sockfd:accept函数返回值addr服务器的网络信息结构体addrlenaddr的长度返回值成功0失败-1 重置错误码服务器代码
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include netinet/ip.h
#include string.h
#include arpa/inet.h
#define ERRLOG(msg) do{\printf(%s %s %d\n, __FILE__, __func__, __LINE__);\perror(msg);\exit(-1);\}while(0)int main(int argc, char const *argv[])
{//1.创建套接字int sockfd0;//需要接函数返回值后面函数会用if(-1(sockfdsocket(AF_INET,SOCK_STREAM,0))){ERRLOG(socket error);}//2.服务器网络信息结构体填充//struct sockaddr addr;struct sockaddr_in ad;memset(ad, 0, sizeof(ad));ad.sin_familyAF_INET;ad.sin_porthtons(7788);//自己随意指定不冲突就行冲突就换ad.sin_addr.s_addrinet_addr(192.168.250.100);//3.绑定套接字和服务器网络信息结构体if(-1bind(sockfd,(struct sockaddr*)ad,sizeof(ad))){ERRLOG(bind error);}//4.让套接字处于被动监听状态if(-1listen(sockfd,5)){ERRLOG(listen error);}//5.阻塞等待客户端连接printf(正在等待客户端发来信息\n);int newfd0;if((newfdaccept(sockfd,NULL,NULL))-1){ERRLOG(accept error);}printf(客户端连接成功\n);//6.收发数据char buff[128]{0};int lnum0;char opr0;int rnum0;int result0;read(newfd,buff,128);printf(客户端发来数据%s\n,buff);sscanf(buff,%d%c%d,lnum,opr,rnum);switch(opr){case :resultlnumrnum;break;case -:resultlnum-rnum;break;case *:resultlnum*rnum;break;case /:result(float)lnum/(float)rnum;break;}memset(buff,0,128);sprintf(buff,result%d,result);write(newfd,buff,128);//7.关闭套接字close(sockfd);close(newfd);return 0;
}客户端代码
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include netinet/ip.h
#include string.h
#include arpa/inet.h#define ERRLOG(msg) do{\printf(%s %s %d\n, __FILE__, __func__, __LINE__);\perror(msg);\exit(-1);\}while(0)int main(int argc, char const *argv[])
{//创建套接字int sockfd0;if((sockfdsocket(AF_INET,SOCK_STREAM,0))-1){ERRLOG(socket error);}//填充服务器结构体struct sockaddr_in addr;memset(addr, 0, sizeof(addr));addr.sin_familyAF_INET;addr.sin_porthtons(6666);addr.sin_addr.s_addrinet_addr(192.168.2.84);//连接服务器if(connect(sockfd,(struct sockaddr *)addr,sizeof(addr))-1){ERRLOG(connect error);}//收发数据char buff[128]{0};//写入的数据从终端获取fgets(buff,128,stdin);buff[strlen(buff)-1]\0;write(sockfd,buff,128);memset(buff,0,128);read(sockfd,buff,128);printf(服务器发来的信息是%s\n,buff);//7.关闭套接字close(sockfd);return 0;
}send
#include sys/types.h
#include sys/socket.hssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);功能发送数据参数sockfd:accept函数返回值buf:要发送的数据的缓冲区首地址len要发送数据的长度flags发送的标志位0用法和write就一样了MSG_DONTWAIT 表示非阻塞返回值成功实际发送的字节数失败-1 重置错误码注意如果对方关闭了套接字或者断开连接第一次send没有反应第二次send会产生SIGPIPE信号下面三种用法是等价的write(sockfd, buf, 128);send(sockfd, buf, 128, 0);sendto(sockfd, buf, 128, 0, NULL, NULL);recv
#include sys/types.h
#include sys/socket.hssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);功能接收数据 参数sockfd:accept函数返回值buf:要接收的数据的缓冲区首地址len要接收数据的长度flags发送的标志位flags:接收的标志位0用法和read就一样了 MSG_DONTWAIT 表示非阻塞返回值成功实际接收的字节数失败-1 重置错误码注意 如果对方关闭了套接字或者断开连接 recv 会返回0下面三种用法是等价的read(sockfd, buf, 128);recv(sockfd, buf, 128, 0);recvfrom(sockfd, buf, 128, 0, NULL, NULL);粘包问题产生原因
tcp在传输的时候不是一调用send就直接将数据发送给客户端的而是将数据放到发送缓冲区里
tcp底层的nagle算法会将一定短时间内发送的数据包组装成一个整体发送给对方
而接受方无法区分消息的边界和类型就可能会导致有冲突的情况发生
也就是说当文件只剩最后一部分的时候很有可能将最后的结束字符和文件最后一部分作为一个整体发送给接收方也有可能将三个128和最后的3012一起发给接收方但不管是哪种方式接收方写入的时候是以128为单位接受的所以接收方缓冲区里是无法单独比较over的所以会产生粘包问题。
解决方法
1.既然是一定短时间内的数据成为一个整体那么最后发送的over就不和前面一块发送了在over之前加一个延时函数让他单独发送
但是不常用因为服务器里禁止使用sleep
2.发送定长的数据包
解决方法一代码