UnixC的第十三天

回忆昨天内容
一、基于TCP的编程实现
模型

实现
服务器端  socket(2)   bind(2)   listen(2)  accept(2)
客户端    socket     connect
地址家族
    通用地址家族
    ipv4地址家族   
    ipv6地址家族
将代码封装,头文件   源文件    多文件编译链接

今天内容:
一、基于TCP的实现
    一次连接,多次通话
    客户端和服务器端连接上以后,如果客户端不发送exit,就一直保持着连接。也就是说
客户端发送exit时终止和服务器端的连接。
二、并发服务器的实现
    使用多进程实现服务器的并发。
    进程模型   一个父进程  多个子进程
    父进程负责的任务:
        负责受理客户端的连接    
        cfd=accept(2)
        创建子进程
        关闭cfd
        负责回收子进程的资源
    子进程负责的任务:
    负责具体的和客户端的沟通
    close(sfd)
    业务处理
    关闭(cfd)
    exit(3)
    

    setsockopt(2)
三、基于udp的编程实现
    模型:
    服务器端
    1 创建一个通讯设备 返回该设备的文件描述符(sfd) socket(2)
    2 将sfd绑定本地地址和端口  bind(2)
     while(1){
        接收客户端的数据 recvfrom(2)
        处理客户端的数据
        响应客户端    sendto(2)
    }
    客户端模型
    1 创建一个通讯设备 返回该设备的文件描述符(sfd)
    2 向服务器发送数据  sendto(2)
    3 阻塞等待服务器的响应消息   recvfrom(2)
    4 处理服务器响应
    5 关闭文件描述符 close(sfd)

recvfrom(2)    sendto(2)

recvfrom(2)
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
    功能:从socket上接收一个消息
    参数:
        sockfd  指定socket  从这个socket上接收消息
        buf  存储接收到的结果的地址
        len  指定了buf的大小
        flags  0
        src_addr 存储消息的来源地址
        addrlen 值——结果参数   src_addr的尺寸
    返回值:成功 返回接收到的字节数
        失败 -1  errno被设置

sendto(2)    
  ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

功能:在socket上发送一条消息
参数:
    sockfd 指定了文件描述符  向这个文件描述符写数据
    buf  要写的数据的地址
    len    指定buf中的有效字节数
    flags   0
    dest_addr    存放目标地址
    addrlen    指定目标地址的大小
返回值: 成功 返回发送出去的字节数
    失败 -1  errno被设置
    
    服务器端 参见 userver.c
    客户端   参见 uclient.c

四、线程的基础
    进程  进程是资源分配的基本单位。每个进程都有自己的pid PCB
    线程  是执行的基本单位  每个线程有自己的tid 也有自己的TCB
    
    一个进程中可以有多个线程  进程中的线程共享进程的资源  每个线程也有自己的私有资源
    线程间的通讯和切换比进程间的通讯和切换开销要小很多
    但是注意一个进程中多个线程争抢共享资源
    
    多个线程争抢的共享资源称作 临界资源
    如何在一个进程中创建一个线程
    pthread_create(3)
    #include <pthread.h>

       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
    功能:创建一个新的线程
    参数:
        thread  新创建的线程的id存储到这个参数指定的地址空间里
        attr  NULL   默认属性
        
        start_routine 线程函数
        arg   是线程函数的唯一参数
    返回值:成功0
        错误  非0的错误码   thread未定义
    Compile and link with -pthread 编译和链接使用pthread库
    void *(*start_routine) (void *)

    举例说明   新线程的创建  pthread_c.c

    获取自己的tid
    pthread_self(3)
     #include <pthread.h>

       pthread_t pthread_self(void);

    Compile and link with -pthread 编译和链接使用pthread库
    功能:获取当前线程的tid
    参数:无
    返回值:总是成功 返回调用的线程的tid
    
    
五、线程的终止、汇合、分离
    
    1.线程的终止
        1)使用return结束线程函数   不能使用exit(3)
        2)使用pthread_exit(3) 终止当前线程
        3)可以使用pthread_cancel(3)取消某一个线程
pthread_exit
       #include <pthread.h>

       void pthread_exit(void *retval);

       Compile and link with -pthread.
    功能:终止当前线程
    参数:
        retval 通过这个值传递给另外一个线程(同一进程中的线程,该线程调用了pthread_join(3))
    返回值: void 不返回给调用者
pthread_cancel(3)
 #include <pthread.h>

       int pthread_cancel(pthread_t thread);

       Compile and link with -pthread.
    功能:发送一个取消请求给线程,将其终止   取消线程终止时终止状态标记为PTHREAD_CANCELED,按照int类型访问,输出为-1。代码参见day14,pthread_e.c
    参数:
    thread 目标线程的tid
    返回值:成功 0
        错误 非0
pthread_detach(3) 线程的分离
       #include <pthread.h>

       int pthread_detach(pthread_t thread);

       Compile and link with -pthread.
    功能:将线程标记为分离的线程,当其终止时,资源自动释放回系统
    参数:
    thread  指定要分离的线程tid
    返回值:成功  返回0
        错误 错误编号

  t_net.h

#ifndef T_NET_H_
#define T_NET_H_


#include<sys/socket.h>
 #include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
	typedef struct sockaddr SA;//通用地址
	typedef struct sockaddr_in SA4;//ipv4地址
	//完成socket bind(2)
	int s_bind(int domain,int type,in_port_t port);
	int no_accept(int fd);
	int h_accept(int fd);
	int t_listen(int domain,int type,in_port_t port,int backlog);
#endif


  t_net.c

#include<t_stdio.h>
#include"t_net.h"

int s_bind(int domain,int type,in_port_t port){
		SA4 serv;//具体ipv4地址
	//创建socket 返回该设备的文件描述符	
	int sfd=socket(domain,type,0);
	if(sfd==-1) E_MSG("socket",-1);
	//初始化服务器的ip地址和端口
	serv.sin_family=AF_INET;
	serv.sin_port=htons(port);
	serv.sin_addr.s_addr=htonl(INADDR_ANY);//本地所有端口的ip地址

	//将sfd绑定到本地地址
	int b=bind(sfd,(SA *)&serv,sizeof(serv));
	if(b==-1) E_MSG("bind",-1);

	return sfd;
}

int no_accept(int fd){//没有来电显示
	int cfd=accept(fd,NULL,NULL);
	if(cfd==-1) E_MSG("accept",-1);
	return cfd;
}
int h_accept(int fd){//有来电显示
	SA4 cli;
	char ip[32];
	socklen_t len;
	len=sizeof(SA4);
	int cfd=accept(fd,(SA *)&cli,&len);
	if(cfd==-1) E_MSG("accept",-1);
	printf("%s\n",inet_ntop(AF_INET,&cli.sin_addr,ip,32));

	return cfd;
}
int t_listen(int domain,int type,in_port_t port,int backlog){
	int fd=s_bind(domain,type,port);
	if(fd==-1) return -1;
	listen(fd,backlog);
	return fd;
}

doit.h

#ifndef DO_IT_H_
#define DO_IT_H_
	#include<unistd.h>
	#include<ctype.h>
	void do_main(int cfd);


#endif

doit.c

#include"doit.h"
	void do_main(int fd){
		char buf[128];
		//从连接描述符里读取客户端发送来的数据到buf中
		int r=read(fd,buf,128);
		//处理客户端数据
		for(int i=0;i<r;i++)
			buf[i]=toupper(buf[i]);
		//响应客户端
		write(fd,buf,r);
		return;
	}
	


pthread_c.c

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
//线程执行函数
void *doit(void *arg){
	printf("%s,pid=%d\n",(char *)arg,getpid());	
	printf("tid:%lu\n",pthread_self());
	return NULL;
}
int main(void){
	pthread_t tid;
	//创建新的线程
	pthread_create(&tid,NULL,doit,"new");
	//这个时候,是几个线程在执行?
	//这两个线程是异步的,没有sleep就不确定谁先执行
	sleep(1);
	doit("main");	
	return 0;
}

userver.c

#include<t_net.h>
#include<t_stdio.h>
#include<ctype.h>
#include<unistd.h>
int main(){
	char buf[256];
	SA4 cli;
	socklen_t len;
	//socket bind
	int fd=s_bind(AF_INET,SOCK_DGRAM,4444);
	if(fd==-1) return -1;
	while(1){
		len=sizeof(SA4);
		//阻塞等待客户端请求的数据
		int rcv=recvfrom(fd,buf,256,0,(SA *)&cli,&len);
		if(rcv==-1) E_MSG("recvfrom",-1);
		//获取到客户端请求的数据,处理数据
		for(int i=0;i<rcv;i++)
			buf[i]=toupper(buf[i]);
		//将处理结果响应给客户端
		sendto(fd,buf,rcv,0,(SA *)&cli,sizeof(SA4));
		
	}
		close(fd);
	return 0;
}

uclient.c

#include<t_net.h>
#include<t_stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char* argv[]){
	char *msg="hello beijing\n";
	SA4 serv;
	char buf[256];
	//创建socket设备 返回该设备的文件描述符
	int fd=socket(AF_INET,SOCK_DGRAM,0);
	if(fd==-1) E_MSG("socket",-1);
	//初始化serv
	serv.sin_family=AF_INET;
	serv.sin_port=htons(4444);
	inet_pton(AF_INET,argv[1],&serv.sin_addr);

	//向服务器发送消息
	int s=sendto(fd,msg,strlen(msg),0,(SA *)&serv,sizeof(SA4));
	if(s==-1) E_MSG("sendto",-1);
	//阻塞等待服务器的响应
	int rcv=recvfrom(fd,buf,256,0,NULL,NULL);
	if(rcv==-1) E_MSG("recvfrom",-1);
	//处理响应的消息
	write(1,buf,rcv);
	//关闭fd
	close(fd);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值