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); }
有名管道
• 无名管道只能用于有亲缘关于的进程通信,有名管道可以实现无亲缘
关系的通信
• 有名管道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); }
读管道
#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); }
消息队列
• 消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级
• 对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;
对消息队列有读权限的进程则可以从消息队列中读走消息。
• 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; }
接收
#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; }
信号
• 信号用于处理异步事件,信号的通信方式理解起来还是有一定难度的,
它既可以在一个进程内进行通信,发送信号给进程,又可以用于进程
外通信
• 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; }
• 以下情况会产生信号
– 按下按键;硬件异常;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); }
信号量 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; }
共享内存 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
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); }
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); }
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; }
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; }