【Linux网络编程】TCP回射服务器\客户端

过早的优化是万恶之源

ã(åå)鿏©socket tcpç¼ç¨ã

服务器程序(select模型):

这是一个TCP回射服务器程序,绑定套接字,使用select管理监听套接字和连接套接字。处理
    内容是把客户端发过来的数据发回去 具体步骤如下

  1. 创建监听套接字listenfd------------------listenfd = socket(PF_INET, SOCK_STREAM, 0)
  2. 创建并初始化服务器IPv4结构体-------------struct sockaddr_in servaddr
  3. 设置REUSEADDR选项以避免TIMEWAIT状态------setsockopt()
  4. 绑定监听套接字和服务器地址结构(前两者)--bind (listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)
  5. 开始监听--------------------------------listen (listenfd, SOMAXCONN)
  6. select在一个while循环中检测事件,无事件到来就一直循环
  7. select检测到事件,个数为nready:是listenfd吗?--转8 是conn吗?--转9
  8. accept函数建立连接,把得到的conn放到客户端列表中,nready减一 。nready不为空说明有conn,转9,否则没有别的消息到来,回到第6步
  9. readn函数在一个循环中检测哪个conn有事件,找到了就读取conn中的消息,回射回去,nready-- ,nready为空说明消息都处理完,回到第6步,否则在本循环中继续。
  10. 回到第6步

要点

  • 每个函数都有错误检测和处理
  • 发的时候换成网络字节序htonl,收过来换成主机字节序ntohl
  • 使用readn writen封装读写函数
  • 发时直接发4+n;收时先收4字节包头,由包头得知包体长为n,再收n字节包体

 *TCP是一种流协议,像流水一样,没有边界。收方不知道一次该读取多少,再加上TCP的复杂机制就会产生粘包问题。解决方案:
 *1.定长包:双方协定好每次收发多少字节
 *2.包尾加上\r\n,如ftp http协议:确定消息边界
 *3.包头加上包体长度:先接收4字节包头,再接收x字节包体,如下readn函数
 *4.更复杂的应用层协议
 *关键问题在于read/write函数,每次读取的字节数不一定是指定字节数
 *粘包问题只存在广域网中??
 

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ERR_EXIT(m) \
	do{\
		perror(m);\
		exit(EXIT_FAILURE);\
	}while(0)

struct packet{//定义数据包格式
	int len;
	char buf[1024];
};
ssize_t readn(int fd,void *buf,size_t count){//每次读取n字节的函数
	size_t nleft=count;//剩余未被读取的字节数
	size_t nread;//已接收的字节数
	char *bufp=(char *)buf;//记录buf
	while(nleft>0){//只要没读完要求的字节数
		if((nread=read(fd,bufp,nleft))<0){
			//nread记录读取的字节数,不一定=nleft
			if(errno=EINTR) continue;//被信号中断
			return -1;//其他情况的读取失败,退出
		}
		else if(nread==0) return count-nleft;//对等方关闭 返回
		bufp+=nread;//指针偏移,指向未被读取的字节
		nleft-=nread;//剩余nleft未读,回到while循环开始
	}
	return count;//nleft为0  都读完了 返回
}
ssize_t writen(int fd,void *buf,size_t count){
	size_t nleft=count;//剩余未被写入的字节数
	size_t nwrite;//已写入的字节数
	char *bufp=(char *)buf;//记录buf
	while(nleft>0){
		if((nwrite=write(fd,bufp,nleft))<0){
			//nwrite记录写的字节数,不一定=nleft
			if(errno=EINTR) continue;//被信号中断
			return -1;//其他情况的读取失败,退出
		}
		else if(nwrite==0) continue;//什么都没写
		bufp+=nwrite;//指针偏移,指向未被读取的字节
		nleft-=nwrite;//剩余nleft未读,回到while循环开始
	}
return count;//nleft为0  都写完了 返回
}
void handle_sigchld(int sig){
    while(waitpid(-1,NULL,WNOHANG)>0);//wait all child process
}
int main(){
    signal(SIGCHLD,handle_sigchld);
	printf("服务器主进程:%d\n",getpid());
	int listenfd;
	if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
		ERR_EXIT("socket");
	printf("socket()-新建套接字成功\n");
	struct sockaddr_in servaddr;//IPv4地址结构
	memset(&servaddr,0,sizeof(servaddr));//用0填充
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(7777);
	servaddr.sin_addr.s_ad
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值