4412 进程间通信

无名管道

从最早的UNIX 系统开始,无名管道的通信方式就存在,有点类似硬
件中的串口,从最初的设计者定型之后,这种通信模型就一直延续到
今天,说明无名管道当初的设计就极具科学性
无名管道有一定的局限性
它是属于半双工的通信方式
只有具有亲缘关系的的进程才能使用这种通信方式,也就是父进程和
子进程之间
man 2 pipe

int pipe(int pipefd[2])
参数pipefd[0]:用于读管道
参数pipefd[1]:用于写管道
返回值:执行成功返回0,失败返回-1
man 7 pipe
官方文档中的例程
编写编译运行测试

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

//进程读函数
void read_data(int *);
//进程写函数 
void write_data(int *);


int main(int argc,char *argv[])
{
    int pipes[2],rc;
    pid_t pid;
        
    rc = pipe(pipes);    //创建管道                 
    if(rc == -1){
        perror("\npipes\n");
        exit(1);
    }
        
    pid = fork();    //创建进程 

    switch(pid){
        case -1:
            perror("\nfork\n");
            exit(1);
        case 0:
            read_data(pipes);    //相同的pipes
        default:
            write_data(pipes);    //相同的pipes
    }    
    return 0;
}

//进程读函数
void read_data(int pipes[])
{
    int c,rc;
    
    //由于此函数只负责读,因此将写描述关闭(资源宝贵)
    close(pipes[1]);
    
    //阻塞,等待从管道读取数据
    //int 转为 unsiged char 输出到终端
    while( (rc = read(pipes[0],&c,1)) > 0 ){          
        putchar(c);                                      
    }

    exit(0);
}

//进程写函数
void write_data(int pipes[])
{
    int c,rc;

    //关闭读描述字
    close(pipes[0]);                          

    while( (c=getchar()) > 0 ){
        rc = write( pipes[1], &c, 1);    //写入管道
        if( rc == -1 ){
            perror("Parent: write");
            close(pipes[1]);
            exit(1);
        }
    }

    close( pipes[1] );
    exit(0);
}
pipe

 

有名管道

无名管道只能用于有亲缘关于的进程通信,有名管道可以实现无亲缘
关系的通信
有名管道fifo 给文件系统提供一个路径,这个路径和管道关联,只要
知道这个管道路径,就可以进行文件访问,fifo 是指先进先出,也就
是先写入的数据,先读出来
有名管道的读写速度非常快
man 3 mkfifo

 

int mkfifo(const char *pathname, mode_t mode)
参数*pathname:路径名,管道名称
参数mode:管道的权限
返回值:成功返回0,错误返回-1
编写编译运行测试
代码有点多,切分为多个视频

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

void filecopy(FILE *ifp, char *buf);

int main()
{
        FILE *fp1;
        long int i = 100000;
        char buf[] = "I want to study Linux!\n";
        char *file1 = "data.txt";

        printf("begin!\n");

        if((fp1 = fopen(file1, "a+")) == NULL) {
                printf("can't open %s \n", file1);
        }

        while(i--) {
                filecopy(fp1, buf);
        }

        fclose(fp1);
        printf("over!\n");
        return 0;
}


void filecopy(FILE *ifp, char *buf)
{
        char c;
        int i,j;
        j = 0;
        i = strlen(buf) - 1;
        while(i--) {
                putc(buf[j], ifp);
                j++;
        }
        putc('\n',ifp);
}
创建数据文件

写管道

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PIPE_BUF 1024

int main()
{
        const char *fifo_name = "my_fifo";
        char *file1 = "data.txt";
        int pipe_fd = -1;
        int data_fd = -1;
        int res = 0;
        const int open_mode = O_WRONLY;
        int bytes_send = 0;
        char buffer[PIPE_BUF + 1];

        if(access(fifo_name, F_OK) == -1) {
                //管道文件不存在
                //创建命名管道
                res = mkfifo(fifo_name, 0777);
                if(res != 0) {
                        fprintf(stderr, "Could not create fifo %s\n", fifo_name);
                        exit(EXIT_FAILURE);
                }
        }

        printf("Process %d opening FIFO O_WRONLY\n", getpid());
        //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
        pipe_fd = open(fifo_name, open_mode);
        data_fd = open(file1, O_RDONLY);
        printf("Process %d result %d\n", getpid(), pipe_fd);

        if(pipe_fd != -1) {
                int bytes_read = 0;
                bytes_read = read(data_fd, buffer, PIPE_BUF);
                buffer[bytes_read] = '\0';
                while(bytes_read > 0) {
                        //向FIFO文件写数据
                        res = write(pipe_fd, buffer, bytes_read);
                        if(res == -1) {
                                fprintf(stderr, "Write error on pipe\n");
                                exit(EXIT_FAILURE);
                        }

                        //累加写的字数,并继续读取数据
                        bytes_send += res;
                        bytes_read = read(data_fd, buffer, PIPE_BUF);
                        buffer[bytes_read] = '\0';
                }
                close(pipe_fd);
                close(data_fd);
        } else
                exit(EXIT_FAILURE);

        printf("Process %d finished\n", getpid());
        exit(EXIT_SUCCESS);
}
writepipe

读管道

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

#define PIPE_BUF 1024

int main()
{
        const char *fifo_name = "my_fifo";
        int pipe_fd = -1;
        int data_fd = -1;
        int res = 0;
        int open_mode = O_RDONLY;
        char buffer[PIPE_BUF+1];
        int bytes_read = 0;
        int bytes_write = 0;

        //清空缓冲数组
        memset(buffer, '\0', sizeof(buffer));

        printf("Process %d opening FIFO O_RDONLY\n", getpid());
        //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
        pipe_fd = open(fifo_name, open_mode);
        //以只读方式创建保存数据文件
        data_fd = open("DataFromFIFO.txt", O_WRONLY|O_CREAT, 0644);
        printf("Process %d result %d\n", getpid(), pipe_fd);

        if(pipe_fd != -1) {
                do {
                        //读取FIFO中的数据,并把它保存在文件DataFromFIFO.txt文件中
                        res = read(pipe_fd, buffer, PIPE_BUF);
                        bytes_write = write(data_fd, buffer, res);
                        bytes_read += res;
                } while(res >0);

                close(pipe_fd);
                close(data_fd);
        } else
                exit(EXIT_FAILURE);

        printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
        exit(EXIT_SUCCESS);

}
readpipe

 

消息队列

消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级
对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;
对消息队列有读权限的进程则可以从消息队列中读走消息。
man 2 msgrcv
函数ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long
msgtyp,int msgflg)
参数msqid:消息队列的标识码
参数*msgp:指向消息缓冲区的指针
参数msgsz:消息的长短
参数msgflg:标志位
返回值:成功返回数据长度,错误返回-1

 

函数int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
参数msqid:消息队列的标识码
参数*msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构
参数msgsz:消息的长短
参数msgflg:标志位
返回值:成功返回0,错误返回-1

 

结构体msgp,是一个标准的通用结构
struct msgstru{
long mtype; //大于0
char mtext[nbyte];}
函数int msgget(key_t key, int msgflg)
参数key:消息队列关联的标识符
参数msgflg:消息队列的建立标志和存取权限。IPC_CREAT 如果内核中没有此队列则创建它;IPC_EXCL 当和IPC_CREAT 一起使用时,如果队列已经存在,则失败
返回值:执行成功则返回消息队列的标识符,否则返回-1
编写编译运行测试
代码有点多,切分为多个视频

 补充

函数ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg)
参数msgtyp
msgtyp等于0 ,则返回队列的最早的一个消息
msgtyp大于0,则返回其类型为mtype的第一个消息
msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息
参数msgflg:标志位为0,则表示忽略

 发送

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

#define MAX_TEXT 512

//需要自己设置
struct msg_st {
    long int msg_type;
    char text[MAX_TEXT];
};

int main(void)
{
    struct msg_st data;
    char buffer[BUFSIZ];        //BUFSIZ是通用size
    int msgid = -1;
    int running = 1;

    msgid = msgget((key_t)1234, IPC_CREAT|0666);
    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);
        data.msg_type = 1;
        strcpy(data.text, buffer);
        if(msgsnd(msgid, (void *)&data, MAX_TEXT, 0) < 0) {
            fprintf(stderr, "msgsnd failed\n");
            exit(EXIT_FAILURE);
        }
        if(strncmp(buffer, "end", 3) == 0) {
            running = 0;
        }
        sleep(1);
    }

    return 0;
}
msgsend

接收

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

#define MAX_TEXT 512

//需要自己设置
struct msg_st {
    long int msg_type;
    char text[MAX_TEXT];
};

int main()
{
    struct msg_st data;
    char buffer[BUFSIZ];        //BUFSIZ是通用size
    int msgid = -1;
    int running = 1;
    long int msgtype = 0;

    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) {
        if(msgrcv(msgid, (void *)&data, MAX_TEXT, msgtype, 0) < 0) {
            fprintf(stderr, "msgrcv failed with error:%d\n", errno);
            exit(EXIT_FAILURE);
        }
        printf("You wrote:%s\n", data.text);
        if(strncmp(data.text, "end", 3) == 0)
            running = 0;
    }

    if(msgctl(msgid, IPC_RMID, NULL) < 0) {
        fprintf(stderr, "msgctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}
msgrcv

 

信号

信号用于处理异步事件,信号的通信方式理解起来还是有一定难度的,
它既可以在一个进程内进行通信,发送信号给进程,又可以用于进程
外通信
man 2 alarm
man 2 signal

unsigned int alarm(unsigned int seconds)
参数seconds:闹钟的时间,单位为秒
返回值:成功返回0 或者返回剩余时间;错误返回-1


sighandler_t signal(int signum, sighandler_t handler);
参数signum:等待的信号
参数handler:信号到来之后,触发的处理方式
返回值:成功返回0,错误返回-1
编写编译运行测试
两个独立程序

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void alarmhandler(int sig)
{
    printf("hello\n");
}

int main()
{
    int i;
    signal(SIGALARM, alarmhandler);
    alarm(5);

    for(i=1;i<7;i++) {
        printf("sleep %d ...\n", i);
        sleep(1);
    }

    return 0;
}
signal_hello

 

以下情况会产生信号
按下按键;硬件异常;kill函数或者命令等
常见信号
SIGALRM:闹钟
SIGHUP:终端发出的结束信号
SIGINT:键盘的ctrl+c
SIGKILL:kill命令产生的信号
SIGSTOP:键盘ctrl+z

函数pause
用于捕捉进程挂起直到捕捉到信号
编写编译运行测试

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void handler(int sig)
{
    printf("Handler the signal %d\n"); 
}

int main(void)
{
    sigset_t sigset;        //记录屏蔽字
    sigset_t ign;           //记录被阻塞的信号
    struct sigaction act;

    //清空信号集
    sigemptyset(&sigset);
    sigemptyset(&ign);

    //向信号集添加信号SIGINT
    sigaddset(&sigset, SIGINT);

    //设置处理函数和信号集
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT, &act,  0);

    printf("Wait the signal SIGINT...\n"); 
    pause();        //挂起进程等待信号

    //设置进程屏蔽字,屏蔽字为SIGINT
    sigprocmask(SIG_SETMASK, &sigset, 0);
    printf("Wait the signal Ctrl+c ...\n");
    sleep(10);

    //测试信号SIGINT是否被屏蔽
    sigpending(&ign);
    //测试是否加入了信号集
    if(sigismember(&ign, SIGINT))
        printf("The SIGINT signal has ignored\n");

    //在信号集中删除信号
    sigdelset(&sigset, SIGINT);
    printf("Wait the signal SIGINT ...\n");

    //将进程的屏蔽字重新设置,取消对SIGINT的屏蔽
    //挂起进程
    sigsuspend(&sigset);

    printf("The app exit in 5 seconds!\n");
    sleep(5);
    exit(0);
}
signal

 

信号量 Semaphore

接着介绍一下 semget 函数的用法。
int semget(key_t key, int nsems, int semflg);
参数 key:一个用来允许不相关的进程访问相同信号量的整数值。
参数 nsems:需要的信号量数目。这个值通常总是 1。

参数 semflg:标记集合,与 open 函数的标记十分类似。
返回值:成功返回标识符,用于其它信号函数,错误返回-1。

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
};

int sem_id = 0;

int set_semvalue();
void del_semvalue();
int semaphore_p();
int semaphore_v();

int main(int argc, char *argv[])
{
    char message = 'X';
    int i = 0;
    //创建信号量
    sem_id = semget((key_t)1234, 1, 0666|IPC_CREAT);

    if(argc > 1) {
        //程序第一次被调用,初始化信号量
        if(!set_semvalue()) {
            fprintf(stderr, "Failed to initialize semaphore\n");
            exit(EXIT_FAILURE);
        }
        //设置要输出到屏幕中的信息,即其参数的第一个字符
        message = argv[1][0];
        sleep(2);
    }

    for(i=0;i<10;i++) {
        //进入临界区
        if(!semaphore_p())
            exit(EXIT_FAILURE);
        //想屏幕中输出数据
        printf("%c", message);
        //清理缓冲区,然后休眠随机时间
        fflush(stdout);
        sleep(rand()%3);
        //离开邻接区前再一次向屏幕输出数据
        printf("%c", message);
        fflush(stdout);
        //离开临界区,休眠随机事件后继续循环
        if(!semaphore_v())
            exit(EXIT_FAILURE);
        sleep(rand()%2);
    }

    sleep(10);
    printf("\n%d - finished\n", getpid());

    if(argc > 1) {
        //如果程序是第一次被调用,则在退出前删除信号量
        sleep(3);
        del_semvalue();
    }
    exit(EXIT_SUCCESS);
}

int set_semvalue()
{
    //用于初始化信号量,在使用信号量前必须这样做
    union semun sem_union;

    sem_union.val = 1;
    if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
        return 0;
    return 1;
}

void del_semvalue()
{
    //删除信号量
    union semun sem_union;

    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
        fprintf(stderr, "Failed to delete semaphore\n");
}

int semaphore_p()
{
    //对信号量做减1操作,即等待P(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_p failed\n");
        return 0;
    }
    return 1;
}

int semaphore_v()
{
    //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semphore_v failed\n");
        return 0;
    }
    return 1;
}
semaphore

 

共享内存 shmdata

共享内存是进程间通信中最简单的方式之一。共享内存在各种进程间通信方式中具有最高
的效率。因为系统内核没有对访问共享内存进行同步,您必须提供自己的同步措施。解决这些
问题的常用方法是通过使用信号量进行同步。

函数 int shmget(key_t key, size_t size, int shmflg);
参数 key:建立新的共享内存对象
参数 size:新建立的内存大小
参数 shmflg:标识符
返回值:成功 shmget 返回一个共享内存标识符或创建一个共享内存对象,错误返回-1。

void *shmat(int shmid, const void *shmaddr, int shmflg)
参数 shmid:共享内存标识符
参数 shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为 NULL 让内核
自己决定一个合适的地址位置
参数 shmflg :SHM_RDONLY,为只读模式,其他为读写模式
返回值:成功返回共享的内存地址,否则返回-1

 

int shmdt(const void *shmaddr)
参数 shmaddr:连接的共享内存的起始地址。
返回值:成功返回 0,错误返回-1。

 

int shmctl(int shmid, int cmd, struct shmid_ds *buf)
参数 shmid:共享内存标识符
参数 cmd IPC_RMID:删除这片共享内存
参数 buf:共享内存管理结构体
返回值:成功返回 0,错误返回-1。

#ifndef __SHMDATA_H_HEADER
#define __SHMDATA_H_HEADER

#define TEXT_SZ 2048

struct shared_use_st {
    int written;            //最为标志,非0:表示可读,0表示可写
    char text[TEXT_SZ];     //记录吸入和读取的文本
};


#endif
shmdata.h

shmwrite

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include "shmdata.h"

int main(void)
{
    int running =1;
    void *shm = NULL;
    struct shared_use_st *shared = NULL;
    char buffer[BUFSIZ + 1];        //用于保存输入的文本
    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);
    }

    //将共享内存连接到当前进程地址空间
    shm = shmat(shmid, (void*)0, 0);
    if(shm == (void *)-1) {
        fprintf(stderr, "shmat failed\n");
        exit(EXIT_FAILURE);
    }
    printf("Memory attached at %p\n", shm);
    //设置共享内存
    shared = (struct shared_use_st *)shm;
    while(running)      //向共享内存中写数据
    {
        while(shared->written == 1) {
            sleep(1);
            printf("Waiting ...\n");
        }
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        strncpy(shared->text, buffer, TEXT_SZ);
        //写完数据,设置written使共享内存段可读
        shared->written = 1;
        //输入了end,退出循环(程序)
        if(strncmp(buffer, "end", 3) == 0) 
            running = 0;
    }
    //把共享内存从当前进程中分离
    if(shmdt(shm) == -1) {
        fprintf(stderr, "shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    sleep(2);
    exit(EXIT_SUCCESS);
}
shmwrite

shmread

#include "shmdata.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <string.h>

int main(void)
{
    int running = 1;        //程序是否继续运行的标志
    void *shm = NULL;       //分配的共享内存的原始首地址
    struct shared_use_st *shared;       //指向shm
    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);
    }
    //将共享内存连接到当前进程的地址空间
    shm = shmat(shmid, 0, 0);
    if(shm == (void *)-1) {
        fprintf(stderr, "shmat failed\n");
        exit(EXIT_FAILURE);
    }
    printf("\nMemory attached at %p\n", shm);
    //设置共享内存
    shared = (struct shared_use_st*)shm;
    shared->written = 0;
    while(running) {        //读取共享内存中的数据
        //没有进程向共享内存定数据有数据可读取
        if(shared->written != 0) {
            printf("You wrote:%s", shared->text);
            sleep(rand()%3);
            //读取完数据,设置written使共享岑村段可写
            shared->written = 0;
            //输入了end,退出循环(程序)
            if(strncmp(shared->text, "end", 3) == 0)
                running = 0;
        } else  //尤其他进程在写数据,不能读取数据
            sleep(1);
    }
    //把共享内存从当前进程中分离
    if(shmdt(shm) == -1) {
        fprintf(stderr, "shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    //删除共享内存
    if(shmctl(shmid, IPC_RMID, 0) == -1) {
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}
shmread

 

TCP

TCP 是一种面向连接的、可靠的、基于 IP 的传输层协议。通过 TCP 可以保证我们传送的数据的正确性。
Linux 下网络通信程序基本上都是采用 socket 的方式。socket 起源于 Unix,而Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开 open->读写 read/write-> 关闭 close”模式来操作。Socket 就是该模式的一个实现,socket 即是一种特殊的文件,一些socket 函数就是对其进行的操作(读/写 IO、打开、关闭)。说白了 socket 是应用程序与 TCP/IP协议族通信的中间软件抽象层,它是一组接口。
现在我们看一下基于 TCP/IP 应用程序通信的流程,如下图:

 

通过上图我们可以看到 TCP/IP 通信是基于服务器/客户端的模式来实现的,首先是服务器(server)端调用 socket 函数创建一个套接字,然后调用 bind 绑定函数,绑定函数主要是设置通信时使用哪种地址族(IPv4,IPv6 等),使用的端口号。然后调用 listen 函数来监听客户端的连接请求。
现在我们来看下客户端(client)端的流程,首先调用 socket 函数创建一个套接字,然后调用 connect 函数连接服务器,这时服务器端的 listen 函数监听到客户端的连接请求就会调用 accept 函数去接受请求,这样连接就建立好了。之后双方就可以调用 read/write 函数收发数据了,在完成通信以后服务器(server)和客户端(client)调用 close 函数关闭创建的套接字 。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
    int sfp, nfp, num = 0;
    struct sockaddr_in s_add, c_add;
    int sin_size;
    unsigned short portnum = 0x8888;

    char buffer[100] = {0};

    printf("Hello,welcome to my server !\r\n");

    sfp = socket(AF_INET, SOCK_STREAM, 0);
    if(sfp == -1) {
        printf("socket failed!\r\n");
        return -1;
    }

    printf("socket ok !\r\n");

    bzero(&s_add, sizeof(struct sockaddr_in));
    s_add.sin_family = AF_INET;
    s_add.sin_addr.s_addr = htonl(INADDR_ANY);
    s_add.sin_port = htons(portnum);

    if(bind(sfp, (struct sockaddr*)(&s_add), sizeof(struct sockaddr))==-1) {
        printf("bind fail !\r\n");
        return -1;
    }

    printf("bind ok !\r\n");

    if(listen(sfp, 5) == -1) {
        printf("listen fail !\r\n");
        return -1;
    }

    printf("listen ok \r\n");

    sin_size = sizeof(struct sockaddr_in);
    nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
    if(nfp == -1) {
        printf("accept fail !\r\n");
        return -1;
    }

    printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",
        ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
    while(1) {
        memset(buffer, 0, 100);
        sprintf(buffer, "Hello, welcome to my server(%d) \r\n", num++);
        send(nfp, buffer, strlen(buffer), 0);
        usleep(500000);
    }
    close(nfp);

    close(sfp);

    return 0;
}
server

client.c

#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int cfd;
    int recbyte;
    int sin_size;
    char buffer[1024] = {0};

    struct sockaddr_in s_add, c_add;
    unsigned short portnum = 0x8888;

    printf("Hello, welcome to client!\r\n");

    if(argc != 2) {
        printf("usage: echo ip\n");
        return -1;
    }

    cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd == -1) {
        printf("socket fail !\r\n");
        return -1;
    }

    printf("socket ok !\r\n");

    bzero(&s_add, sizeof(struct sockaddr_in));
    s_add.sin_family = AF_INET;
    s_add.sin_addr.s_addr = inet_addr(argv[1]);
    s_add.sin_port = htons(portnum);
    printf("s_addr = %#x, port: %#x\r\n", s_add.sin_addr.s_addr, s_add.sin_port);

    if(connect(cfd, (struct sockaddr *)(&s_add), sizeof(struct sockaddr)) == -1) {
        printf("connect fail !\r\n");
        return -1;
    }

    printf("connect ok !\r\n");

    while(1) {
        if((recbyte = read(cfd, buffer, 1024)) == -1) {
            printf("read data fail !\r\n");
            return -1;
        }

        printf("read ok\rRECV\n");
        buffer[recbyte] = '\0';
        printf("%s\n", buffer);
    }

    close(cfd);

    return 0;

}
client

 

posted @ 2018-08-03 14:27  习惯就好233  阅读(252)  评论(0编辑  收藏  举报