实验2-1-1

linux的系统调用创建一个文件,并制定文件的访问属性。文件操作是Linux应用程序编程的基础,要求非常的熟练。

  1 学会使用C库函数和Linux系统调用,并理解它们的区别

  2 注意区分C库函数和Linux系统调用对文件操作的方法

  3 比如C库函数fread就没有向Linux系统调用read函数那样返回成功

  4 读取了多少个字节。只有清除了它们之间的区别,才能更好运用

  1 本次实验通过日历和本地时间之间的转换,

  2 让大家熟悉Linux时间编程,这些Linux编程基础在以后经常用到

 

  1 实验2-2-1 fork创建子进程

  2 学会Linux系统调用fork函数的使用

  3 在程序中创建一子进程,分别在父进程和子进程中打印进程ID

  4 总结:fork函数创建子进程后,父子进程是独立、同时运行的,

  5 并没有先后顺序

实验2-2-2 vfork创建子进程

  1 运行程序时可以看到,程序会停一秒然后分别打印出父子进程的ID

  2 也就是说子进程进来就阻塞一秒,但也没有先去运行父进程

  3 总结:调用fork函数产生的父子进程运行顺序是不定的

  4 而调用vfork函数产生的父子进程必定是子进程先运行完,父进程才会开始运行

  1 int  execl(const char * __path, const char * __arg0, ...) __WATCHOS_PROHIBIT    ED __TVOS_PROHIBITED;

  2 execel函数会让一个可执行程序运行并替换本进程,那么这个可执行程序就应该有创建    一个文件的功能。

  3 可以用2-1-1实验中的file_creat.c编译产生的可执行文件file_creat来作为该可执行>    程序。

  4 exec函数族会在一个进程中启动另一个程序执行,并用它来取代原调用进程的数段、代    码段和堆栈段。

  5 在执行完exec函数调用后,原调用进程的内容除了进程号之外,其他全部被新的进程替换了。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, const char * argv[]) {
    if(argc < 2){
        perror("you haven't input the filename,please try again!\n");
        exit(EXIT_FAILURE);
    }
    /*调用execl函数,用可执行程序file_creat替换本进程*/
    if(execl("./file_creat","file_creat",argv[1],NULL) < 0){
        perror("execl error!");
    }
    
}

  1 编写一应用程序,在程序中创建一个子进程,父进程需要等待子进程运行结束后才能执

    行

  2 用fork创建子进程后,父子进程的执行顺序fork函数不能确定。 

  3 我们可以用wait函数和waitpid函数使父进程阻塞等待子进程退出的性质,

  4 来确保子进程先结束。

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<errno.h>
#include<math.h>
#include<sys/types.h>
#include<string.h>
int main(int argc, const char * argv[]) {
    pid_t child;
    /*创建子进程*/
    if((child = fork()) == -1){
        printf("Fork Error:%s\n",strerror(errno));
        exit(1);
    }else if(child == 0){
        printf("the child process is runing\n");
        sleep(1);//子进程睡眠一秒,但并没有去运行父进程
        printf("i am the child:%d\n",getpid());
        exit(0);
    }else{
        wait(NULL);//等到子进程退出,父进程才会运行
        printf("the father process is runing\n");
        printf("i am the father:%d\n",getpid());
        return 0;
    }
}

  1 学会进程间通信方式——无名管道的学会进程间通信方式——无名管道的使用●实验要求: 

  2 在父进程中创建一无名管道,并创建子进程来读该管道,父进

  3 子进程先睡两秒让父进程先运行,然后阻塞等待子进程退出。子进程醒  

  4 读出管道里妁内容并打印:到∷屏幕上再退出∷父进粳捕获到

  5 子进程退出后也退出。

  6 4.总结:

  7 由于fork函数让子进程完整地拷贝了父进程的整个地址空间,所以父子进程都有管道的读端和写端。我们往往希望父子进程中的一个进程

    写一个进程读,那么写的进程最好

  8 关闭掉读端,读∴的:进程蕞好关闭掉写端

#include<stdio.h>
#include<sys/types.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

int main(int argc, const char * argv[]) {
    int pipe_fd[2];
    pid_t pid;
    char buf_r[100];
    long r_num;
    
    
    memset(buf_r,0,sizeof(buf_r));
    /*创建管道*/
    if(pipe(pipe_fd) < 0){
        printf("pipe create error\n");
        return -1;
    }
    /*创建子进程*/
    if((pid = fork()) == 0){// 子进程执行序列
        printf("\n");
        close(pipe_fd[1]);//子进程先关闭了管道的写法
        sleep(2);/*让父进程先运行,这样父进程先写,子进程才有内容读*/
        if((r_num = read(pipe_fd[0],buf_r,100) )> 0){//buf_r来存放从管道读取的内容
            printf("%lu numbers read from the pipe is %s\n",r_num,buf_r);
        }
        close(pipe_fd[0]);
        exit(0);
    }else if(pid > 0){
        close(pipe_fd[0]);//父进程关闭了管道的读端
        if(write(pipe_fd[1],"Hello",5) != -1)//将字符串写入管道
            printf("parent write1 Hello!\n");
        if(write(pipe_fd[1],"Pipe",5)!=-1)
            printf("parent write2 Pipe!\n");
        close(pipe_fd[1]);
        waitpid(pid,NULL,0);/*等待子进程结束*/
        exit(0);
    }
    return 0;
}

  1 进程间通信--有名管道

  2 1启动A进程,创建一有名管道,向其中写入一些数据

  3 2启动B进程,从A创建的有名管道中读出数据

  4 在fifo_write.c文件中,先创建一个有名管道,向其中写入

  5 在fifo_read.c文件中,先打开fifo_write.c文件创建的有名管道,然后循环地从管道>    中读出数据

~               

fifo_write.c

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#define FIFO_SERVER "/tmp/myfifo"
int main(int argc, const char * argv[]) {
    int fd;
    char w_buf[100];
    long nwrite;
    /*创建有名管道*/
    if(mkfifo(FIFO_SERVER, O_CREAT|O_EXCL|O_RDWR) <0 && (errno!=EEXIST))
        printf("cannot create fifoserver\n");
    
    /*打开管道*/
    fd = open(FIFO_SERVER,O_RDWR|O_NONBLOCK,0);
    if(fd == -1){
        perror("open");
        exit(1);
    }
    /*入參检测*/
    if(argc == 1){
        printf("Please send something\n");
        exit(-1);
    }
    strcpy(w_buf,argv[1]);
    /*向管道写入数据*/
    if((nwrite = write(fd,w_buf,100)) == -1){
        if(errno == EAGAIN){
            printf("The FIFO has not ben read yet.Please try later\n");
        }else
            printf("write %s to the FIFO\n",w_buf);
        close(fd);//关闭管道
    }
    return 0;
}

fifo_read.c

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#define FIFO "/tmp/myfifo"
int main(int argc, const char * argv[]) {
    char buf_r[100];
    int fd;
    long nread;
    printf("Preparing for reading bytes...\n");
    memset(buf_r,0,sizeof(buf_r));
    /*打开管道*/
    fd = open(FIFO,O_RDONLY|O_NONBLOCK,0);
    if(fd == -1){
        perror("open");
        exit(1);
    }
    while(1){
        memset(buf_r,0,sizeof(buf_r));
        if((nread = read(fd, buf_r, 100)) == -1){
            if(errno == EAGAIN)
                printf("no data yet\n");
        }
        printf("read %s from FIFO\n",buf_r);
        sleep(1);
    }
    /*〃后面三句话是不会被运行到的,但 不会影响程序运行的效杲当 在上面的死循环中执行时收到信号后会马上结束运行而没有执
     面的三句话。这些会在后面的信号处理中讲到,现 在不理解没 系,这 个问题留给大家学习了信号处理之*/
    close(fd);//关闭管道
    pause();//暂停,等待信号
    unlink(FIFO);//删除文件
    return 0;
}

  1 实验2-3-3信号处理

  2 自定义信号处理函数

  3 在进程中位SIGBUS注册处理函数,并向该进程发送SIGBUS信号

  4 代码分析:

  5 用signal系统调用为SIGBUS信号注册了信号处理函数my_func

  6 然后将进程挂起等待SIGBUS信号,所以需要向该进程发送SIGBUS信号才会执行自定义的    信号处理函数

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
/*自定义信号处理函数*/
void my_func(int sign_no){
    if(sign_no == SIGBUS)
        printf("I have get SIGBUS\n");
}
int main(int argc, const char * argv[]) {
    printf("Waiting for signal SIGBUS\n");
    /*注册新号处理函数*/
    signal(SIGBUS,my_func);
    pause();//将进程挂起直到捕捉到信号为止
    exit(0);
    return 0;
}

 

  1 实验2-3-4 共享内存

  2 学会使用共享内存这种进程间通信方式

  3 实验要求:

  4 1启动A进程,创建一共享内存,并向其写入一些数据

  5 2启动B进程,从A创建的共享内存中读出数据

  6 从运行情况可以看到,虽然是同一块共享内存,但是映射到进程中的起始地址是不一样    的。先运行读程序shm1会没有数据可读,但运行写程序shm2后每发一句shm1就会读出一    句并显示在终端上,直到写入的为“end”为止。

shm_com.h

#define TEXT_SZ 2048

struct shared_use_st{
	int written_by_you;
	char some_text[TEXT_SZ];
};

shm1.c

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include "shm_com.h"
int main(int argc, const char * argv[]) {
    int running = 1;
    void *shared_memory = (void *)0;
    struct shared_use_st *shared_stuff;
    int shmid;
    /*创建共享内存*/
    shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
    if(shmid == -1){
        fprintf(stderr,"shmget failed\n");
        exit(EXIT_FAILURE);
    }
    /*映射共享内存*/
    shared_memory = shmat(shmid, (void *)0, 0);
    if(shared_memory == (void *) - 1){
        fprintf(stderr,"shmat failed");
        exit(EXIT_FAILURE);
    }
    printf("Memeory attached at %X\n",(unsigned int *)shared_memory);
    /*让结构体指针指向这块共享内存*/
    shared_stuff = (struct shared_use_st *)shared_memory;
    /*控制读写顺序*/
    shared_stuff->written_by_you = 0;
    /*循环的从共享内存中读数据,直到读到“end”为止*/
    while(running){
        if(shared_stuff->written_by_you){
            printf("You wrote:%s",shared_stuff->some_text);
            sleep(1);//读进程睡一秒,同时会导致写进程睡一秒,这样做到读了之后再写
            shared_stuff->written_by_you = 0;
            if(strncmp(shared_stuff->some_text,"end",3) == 0){
                running = 0;//结束循环
            }
        }
    }
    /*删除共享内存*/
    if(shmdt(shared_memory) == -1){
        fprintf(stderr,"shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
    return 0;
}

shm2.c

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include "shm_com.h"
int main(int argc, const char * argv[]) {
    int running = 1;
    void *shared_memory = (void *)0;
    struct shared_use_st *shared_stuff;
    char buffer[BUFSIZ];
    int shmid;
    /*创建共享内存*/
    shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
    if(shmid == -1){
        fprintf(stderr,"shmget failed\n");
        exit(EXIT_FAILURE);
    }
    /*映射共享内存*/
    shared_memory = shmat(shmid, (void *)0, 0);
    if(shared_memory == (void *) - 1){
        fprintf(stderr,"shmat failed");
        exit(EXIT_FAILURE);
    }
    printf("Memeory attached at %X\n",(unsigned int *)shared_memory);
    /*让结构体指针指向这块共享内存*/
    shared_stuff = (struct shared_use_st *)shared_memory;
    /*控制读写顺序*/
    shared_stuff->written_by_you = 0;
    /*循环的从共享内存中写数据,直到写入的为“end”为止*/
    while(running){
        while(shared_stuff->written_by_you ==1){
            sleep(1);//等到进程读完之后再写
            printf("waiting for client...\n");
        }
        printf("Enter sometext:");
        fgets(buffer,BUFSIZ,stdin);
        strncpy(shared_stuff->some_text,buffer,TEXT_SZ);
        shared_stuff->written_by_you = 1;
        if(strncmp(buffer, "end", 3) == 0){
            running = 0;//结束循环
        }
    }
    /*删除共享内存*/
    if(shmdt(shared_memory) == -1){
        fprintf(stderr,"shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
    return 0;
}


学会使用消息队列这种进程间通信方式

  2 实验要求:创建一消息队列,实现向队列中存放数据与读取数据

  3 先运行msg1,在没有运行msg2向消息队列中添加消息时,msg1会阻塞在msgrcv函数上,    直到添加的消息为“end”,程序结束。

msg1.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
struct my_msg_st{
    long int my_msg_type;
    char some_text[BUFSIZ];
};
int main(int argc, const char * argv[]) {
    int running = 1;
    int msgid;
    struct my_msg_st some_data;
    long int msg_to_receive = 0;
    /*创建消息队列*/
    msgid = msgget((key_t)1234,0666|IPC_CREAT);
    if(msgid == -1){
        fprintf(stderr,"msgget failed with error:%d\n",errno);
        exit(EXIT_FAILURE);
    }
    /*循环d从队列中接受消息*/
    while(running){
        /*读取消息*/
        if(msgrcv(msgid,(void *)&some_data,BUFSIZ,msg_to_receive,0) == -1){
            fprintf(stderr,"msgrcv failed with error:%d\n",errno);
            exit(EXIT_FAILURE);
        }
        
        printf("You wrote:%s",some_data.some_text);
        /*接受到的消息为“end”时结束循环*/
        if(strncmp(some_data.some_text, "end", 3) == 0){
            running = 0;
        }
    }
    /*从系统内核移走消息队列*/
    if(msgctl(msgid,IPC_RMID,0) == -1){
        fprintf(stderr,"msgctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_FAILURE);
    return 0;
}

msg2.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
#define MAX_TEXT 512
struct my_msg_st{
    long int my_msg_type;
    char some_text[MAX_TEXT];
};
int main(int argc, const char * argv[]) {
    int running = 1;
    struct my_msg_st some_data;
    int msgid;
    char buffer[BUFSIZ];
    /*创建消息队列*/
    msgid=msgget((key_t)1234,0666|IPC_CREAT);
    if(msgid==-1){
        fprintf(stderr,"msgget failed with error:%d\n",errno);
        exit(EXIT_FAILURE);
    }
    /*循环向消息队列中添加消息*/
    while(running){
        printf("Enter some text:");
        fgets(buffer,BUFSIZ,stdin);
        some_data.my_msg_type = 1;
        strcpy(some_data.some_text, buffer);
        /*添加消息*/
        if(msgsnd(msgid,(void *)&some_data,MAX_TEXT,0) == -1){
            fprintf(stderr,"msgsed failed\n");
            exit(EXIT_FAILURE);
        }
        if(strncmp(buffer, "end", 3) == 0){
            running = 0;
        }
    }
    exit(EXIT_SUCCESS);
    return 0;
}


实验2-5-1 创建线程

目的:学会线程创建并使用

要求:编写应用程序,创建一线程,并向该线程处理函数传递以结构体

可以看到线程执行函数中打印出了,主函数中赋值的结构体,即将这个结构体传递进了线程执行函数

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

struct menber{
    int a;
    const char *s;
};
/*线程执行函数*/
void *create(void *arg){
    struct menber *temp;
    temp = (struct menber *)arg;
    printf("menber->a = %d \n",temp->a);
    printf("menber->s = %s \n",temp->s);
    return (void *)0;
}
int main(int argc, const char * argv[]) {
    pthread_t tidp;
    int error;
    struct menber *b;
    /*为结构体指针b分配内存并赋值*/
    b = (struct menber *)malloc(sizeof(struct menber));
    b->a = 4;
    b->s = "zieckey";
    /*创建线程并运行线程执行函数*/
    error = pthread_create(&tidp,NULL,create,(void *)b);
    if(error){
        printf("pthread is not created...\n");
        return -1;
    }
    sleep(1);//进程睡眠一秒使线程执行完后才会结束
    printf("pthread is created...\n");
    return 0;
}


通过pthread_join系统调用,理解线程和进程的执行顺序

要求:

编写应用程序,创建一线程,进程需要等待该线程结束后才能继续执行

进程在创建线程之后是各自独立运行的,也就是说线程和进程都在运行。pthread_join系统调用会使进程阻塞等待线程,所以进程会出现运行一句打印语句后阻塞,直到线程退出后才继续运行。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void *thread(void *str){
    int i;
    for(i=0;i<3;i++){
        sleep(2);
        printf("This in the thread:%d\n",i);
    }
    return NULL;
}

int main(){
    pthread_t pth;
    /*创建线程并执行线程执行函数*/
    int ret = pthread_create(&pth, NULL, thread, NULL);
    printf("The main process will be to run,but will be blocked soon\n");
    /*进程阻塞等待线程退出之后进程被唤醒*/
    pthread_join(pth, NULL);
    printf("thread was exit\n");
    for(int i=0;i<3;i++){
        sleep(1);
        printf("This is the main:%d\n",i);
    }
    return 0;
}


实验2-6-1 TCP程序设计

学会Linux的socket套接字网络编程,熟悉使用TCP传输协议的网络编程流程

要求:

编写使用TCP协议的服务器程序

编写使用TCP协议的客户端程序

客户端向服务器发送字符串,服务器打印收到的字符串

从运行情况可以看出,在没有客户端连接上来时服务器程序阻塞在accept函数上,等待连接。当客户端程序连接上来时,阻塞在read函数上,等待读取消息。客户端发送一条消息后结束,服务器读出消息并打印出来,继续等待新的连接

tcp_server.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>//这里定义的有read函数
#define portnumber 3333
/*线程清理函数*/

int main(int argc,char *argv[]){
    int sockfd,new_fd;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    unsigned int sin_size;
    long nbytes;
    char buffer[1024];
    /*服务器开始建立sockfd描述符*/
    if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1){
        //AF_INET:IPV4;SOCK_STREAM:TCP
        fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
        exit(1);
    }
    /* 服务器填充 sockaddr结构 */
    bzero(&server_addr,sizeof(struct sockaddr_in));//初始化,置0
    server_addr.sin_family = AF_INET; //IPV4
    
    /*将本机器上的long数据转化为网络上的long数据,服务器程序能运行在任何ip的主机上。
     INADDR_ANY表示主机可以是任意IP地址,即服务器程序可以绑定到所有IP上*/
     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    /*
     server_addr.sin_addr.s_addr=inet_addr("192.168.1.1");用于绑定到一个固定
     ip的主机上,inet_addr用于把数字加格式的ip转化为整型ip
     server_addr.sin_port=htons(portnumber);用于将本机器上的short数据转化为网络上的short数据(端口号)*/
    server_addr.sin_port=htons(portnumber);
    
    /*捆绑sockfd描述符到IP地址*/
    if((bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))) == -1){
        fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
        exit(1);
    }
    /*设置允许连接的最大客户端数*/
    if(listen(sockfd,5) == -1){
        fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
        exit(1);
    }
    while(1){
        /*服务器阻塞,直到客户端程序建立连接*/
        sin_size = sizeof(struct sockaddr_in);
        if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size)) == -1){
            fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
            exit(1);
        }
        //将网络地址转换成字符串
        fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
        if((nbytes = read(new_fd,buffer,1024)) == -1){
            fprintf(stderr,"Read Error:%s\n",strerror(errno));
            exit(1);
        }
        buffer[nbytes] = '\0';//添加终止符
        printf("Server received %s\n",buffer);
        /*这个通讯已经结束*/
        close(new_fd);
        /* 循环下一个 */
    }
    /* 结束通讯 */
    close(sockfd);
    exit(0);
}

tcp_client.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>//这里定义的有read函数
#define portnumber 3333
/*线程清理函数*/

int main(int argc,char *argv[]){
    int sockfd;
    char buffer[1024];
    struct sockaddr_in server_addr;
    struct hostent *host;
    if(argc!=2){
        fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
        exit(1);
    }
    /*使用hostname查询host名字*/
    if((host = gethostbyname(argv[1])) == NULL){
        fprintf(stderr,"Get hostname error\n");
        exit(1);
    }
    /*客户端开始建立sockfd描述符*/
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){//AF_INET:internet;SOCK_STREAM:TCP
        fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
        exit(1);
    }
    /*客户端程序填充服务端的资料*/
    bzero(&server_addr,sizeof(server_addr));//初始化,置 0
    server_addr.sin_family = AF_INET;//IPV4
    server_addr.sin_port=htons(portnumber);//将本机上的short数据转化为网络上的short数据端口号
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);//IP地址
    /*客户端程序发起连接请求*/
    if((connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))) == -1){
        fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
        exit(1);
    }
    /*连接成功*/
    printf("Please input char:\n");
    /*发送数据*/
    fgets(buffer,1024,stdin);
    write(sockfd,buffer,strlen(buffer));
    /*结束通讯*/
    close(sockfd);
    exit(0);
}

客户端输入的ip地址是


实验2-6-2 UDP程序设计

目的:学会Linux的socket套接字网络编程,熟悉使用UDP传输协议的网络编程流程

要求:编写使用UDP协议的服务器程序;编写使用UDP协议的客户端程序

客户端向服务端发送字符串,服务端打印收到的字符串

运行:先运行服务器程序server_udp,然后在另一个终端中运行客户端程序client_udp

运行程序可以看到,客户端没有连接上来时,服务器处于阻塞状态,阻塞在recvfrom函数上。客户端和服务器建立连接之后,客户端每发送一条消息,服务器收到并打印出来,由于收发都在死循环中进行,没有结束条件,所以只有按Ctrl+c来结束程序的运行

upd_server.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>//这里定义的有read函数
/*线程清理函数*/
#define SERVER_PORT 8888
#define MAX_MSG_SIZ 1024
void udps_respon(int sockfd){
    struct sockaddr_in addr;
    unsigned int addrlen,n;
    char msg[MAX_MSG_SIZ];
    while(1){
        /*从网络上读,并写到网络上*/
        bzero(msg,sizeof(msg));//初始化,清零
        addrlen = sizeof(struct sockaddr);
        n = recvfrom(sockfd,msg,MAX_MSG_SIZ,0,(struct sockaddr*)&addr,&addrlen);//从客户端接受消息
        msg[n] = 0;//将收到的字符串尾端添加上字符串结束标志
        /*显示服务器已经收到了信息*/
        fprintf(stdout,"Server have received %s",msg);//显示消息
    }
}
int main(void){
    int sockfd;
    struct sockaddr_in addr;
    /*1  服务器端开始建立socket描述符*/
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0){
        fprintf(stderr,"Socket Error:%s\n",strerror(errno));
        exit(1);
    }
    /*2  服务端填充。sockadddr结构*/
    bzero(&addr,sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(SERVER_PORT);
    /*3  捆绑sockfd描述符*/
    if((bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))) < 0){
        fprintf(stderr,"Bind Error:%s\n",strerror(errno));
        exit(1);
    }
    /*4  */
    udps_respon(sockfd);//进行读写操作
    close(sockfd);
}

udp_client.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>//这里定义的有read函数
/*线程清理函数*/
#define SERVER_PORT 8888
#define MAX_MSG_SIZ 1024
void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len){
    char buffer[MAX_MSG_SIZ];
    int n;
    while(1){
        /*从键盘读入,写到服务端*/
        printf("Please input char:\n");
        fgets(buffer,MAX_MSG_SIZ,stdin);
        sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr *)addr,len);
        bzero(buffer,MAX_MSG_SIZ);
    }
}
int main(int argc,char **argv){
    int sockfd;
    struct sockaddr_in addr;
    if(argc != 2){
        fprintf(stderr,"Usage:%s server_ip\n",argv[0]);
        exit(1);
    }
    /*1  客户端开始建立socket描述符*/
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0){
        fprintf(stderr,"Socket Error:%s\n",strerror(errno));
        exit(1);
    }
    /*2  服务端填充。sockadddr结构*/
    bzero(&addr,sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    if((inet_aton(argv[1],&addr.sin_addr)) < 0){
        /*inet_aton函数用于把字符串的ip转化为网络2进制数字*/
        fprintf(stderr,"IP error:%s\n",strerror(errno));
        exit(1);
    }

    udpc_requ(sockfd, &addr, sizeof(struct sockaddr_in));//进行读写操作
    close(sockfd);
}


实验2-6-3 并发服务器设计

目的:学会多线程或者多线程结合网络编程的综合应用,实现能同时接受多客户端连接的服务器程序

要求:实现基于TCP协议的并发服务器,比较并发服务器与循环服务器的区别

运行应用程序:为了看到多客户端连接的效果,需要至少开三个终端,一个终端运行服务器程序,另外两个终端运行客户端程序。由于服务器使用了多线程,一个线程处理一个客户端连接,所以我们看到服务器可以同时接受多个客户端的连接

总结:并发服务器使用了多线程或者多进程,所以能同时接受多个客户端的连接。循环服务器只有在一个连接端掉之后才能接受另一个连接,因为服务进程正在执行客户端的消息收发而不能继续循环去接受新的连接。

server_thread.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/un.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>
#include<ctype.h>//其中定义的有toupper函数
#include<unistd.h>//这里定义的有read函数
//线程执行函数负责读写
void *thr_fn(void *arg){
    long size,j;
    char recv_buf[1024];
    int *parg = (int *)arg;
    int new_fd = *parg;
    printf("new_fd=%d\n",new_fd);
    while((size = read(new_fd,recv_buf,1024)) > 0){
        if(recv_buf[0] == '@') break;
        printf("Message from client(%ld):%s\n",size,recv_buf);
        for(j=0;j<size;j++){
            recv_buf[j] = toupper(recv_buf[j]);
        }
        write(new_fd,recv_buf,size);
    }
    close(new_fd);
    return 0;
}
int main(int argc,char *argv[]){
    socklen_t clt_addr_len;
    int listen_fd;
    int com_fd;
    int ret;
    int i;
    static char recv_buff[1024];
    //int len;
    unsigned int len;
    int port;
    pthread_t tid;
    
    struct sockaddr_in clt_addr;
    struct sockaddr_in srv_addr;
    //服务器端运行时要给出端口信息,该端口为监听端口
    if(argc!=2){
        printf("Usage:%s port\n",argv[0]);
        return 1;
    }
    //获得输入的端口
    port = atoi(argv[1]);
    
    //创建套接字用于服务器的监听
    listen_fd = socket(PF_INET,SOCK_STREAM,0);
    if(listen_fd < 0){
        perror("cannot create listening socket");
        return 1;
    }
    //填充关于服务器的套接字信息
    memset(&srv_addr,0,sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    srv_addr.sin_port = htons(port);
    //将服务器和套接字绑定
    ret = bind(listen_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr));
    if(ret == -1){
        perror("cannot bind server socket");
        close(listen_fd);
        return 1;
    }
    //监听指定端口,连接5个客户端
    ret = listen(listen_fd,5);
    if(ret == -1){
        perror("cannot listen the client connect request");
        close(listen_fd);
        return 1;
    }
    //对每个连接来的客户端创建一个线程,单独与其进行通信
    /*首先调用read函数读取客户端发送来的信息,将其转换成大写后发送回客户端
     当输入“@”时,程序退出*/
    while(1){
        len = sizeof(clt_addr);
        com_fd = accept(listen_fd,(struct sockaddr *)&clt_addr,&len);
        if(com_fd < 0){
            if(errno == EINTR){
                continue;
            }else{
                perror("cannot accept client connect request");
                close(listen_fd);
                return 1;
            }
        }
        printf("com_fd=%d\n",com_fd);//打印建立连接的客户端产生的套接字
        if((pthread_create(&tid,NULL,thr_fn,&com_fd)) == -1){
            perror("pthread_create error");
            close(listen_fd);
            close(com_fd);
            return 1;
        }
    }
    return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/un.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>
#include<ctype.h>//其中定义的有toupper函数
#include<unistd.h>//这里定义的有read函数

int main(int argc,char *argv[]){
    int connect_fd;
    int ret;
    char snd_buf[1024];
    int i;
    int port;
    long len;
    
    static struct sockaddr_in srv_addr;
    //客户端运行需要给出具体的链接地址和端口
    if(argc != 3){
        printf("Usage:%s server_ip_address port\n",argv[0]);
        return 1;
    }
    //获得输入的端口
    port = atoi(argv[2]);
    //创建套接字用于客户端的连接
    connect_fd = socket(PF_INET,SOCK_STREAM,0);
    if(connect_fd < 0){
        perror("cannot create communication socket");
        return 1;
    }
    //填充关于服务器的套接字信息
    memset(&srv_addr,0,sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_addr.s_addr = inet_addr(argv[1]);
    srv_addr.sin_port = htons(port);
    //连接指定的服务器
    ret = connect(connect_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr));
    if(ret == -1){
        perror("cannot connect to the server");
        close(connect_fd);
        return 1;
    }
    memset(snd_buf,0,1024);
    /*用户输入信息后,程序将输入的信息通过套接字发送给服务器,然后调用read函数从
     服务器中读取发送来的信息。当输入“@”时,程序退出*/
    while(1){
        write(STDOUT_FILENO,"input message:",14);
        len = read(STDIN_FILENO,snd_buf,1024);
        if(len > 0) write(connect_fd,snd_buf,len);
        len = read(connect_fd,snd_buf,len);
        if(len > 0){
            printf("Message from server:%s\n",snd_buf);
        }
        if(snd_buf[0] == '@') break;
    }
    close(connect_fd);
    return 0;
}

 

Logo

更多推荐