目录
一、通信概念
进程间通信(Inter-Process Communication,IPC)是指在操作系统中,不同进程之间进行数据和信息交换的机制。这种通信允许不同的程序或进程在同一系统上进行协作,共享数据和资源。常见的IPC方式包括管道、套接字、信号量、共享内存和消息队列等。这些机制使得多个进程能够相互通信和协调工作,从而实现更复杂的任务。
二、进程间通信机制
1、管道
1.1 匿名管道(Anonymous Pipe)
①概念:
匿名管道用于在具有亲缘关系(例如父子进程)的进程之间传递数据。 匿名管道是单向的,只能用于父进程向子进程传递数据或者子进程向父进程传递数据,不支持双向通信。
在创建匿名管道时,使用 `pipe()` 系统调用来创建管道,不需要指定名称。匿名管道的生命周期限于创建它们的进程及其子进程。
用于传递数据量较小的场景,例如父子进程之间传递简单的命令或数据。
②API:
int pipe(int pipefd[2]);
功能: 创建一个匿名管道。
参数: pipefd 是包含两个整数的数组,用于存储管道的文件描述符。
返回值: 成功时返回0,失败时返回-1。
③示例代码:
#include <stdio.h>
#include <unistd.h>
int main() {
int pipefd[2];
char buffer[20];
pipe(pipefd); // 创建匿名管道
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
return 1;
} else if (child_pid == 0) { // 子进程
close(pipefd[1]); // 关闭写入端
read(pipefd[0], buffer, sizeof(buffer));
printf("Child received: %s\n", buffer);
close(pipefd[0]); // 关闭读取端
} else { // 父进程
close(pipefd[0]); // 关闭读取端
write(pipefd[1], "Hello from parent", 17);
close(pipefd[1]); // 关闭写入端
}
return 0;
}
1.2 命名管道(Named Pipe)
①概念:
命名管道是一种允许不同进程之间通过一个具有唯一名称的文件进行通信的机制。
命名管道是在文件系统中创建的,可以用于不同进程之间的通信,甚至跨越不同的计算机。
命名管道是双向的,可以在两个方向上传递数据。
创建命名管道需要使用 `mkfifo` 命令或者调用 `mkfifo()` 系统调用,指定一个路径和名称。
用于传递较大数据量或者需要持久化的通信场景,例如不同进程之间的实时数据传输。
②API:
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个命名管道。
参数:pathname 是管道的路径和名称,mode 是管道的权限模式。
返回值:成功时返回0,失败时返回-1。
③示例代码:
写进程(要先运行,确保管道创建成功)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
const char *fifoPath = "my_fifo";
// 创建命名管道
mkfifo(fifoPath, 0666);
int fd = open(fifoPath, O_WRONLY); // 打开管道以写入数据
char message[] = "Hello from named pipe!";
write(fd, message, strlen(message) + 1);
close(fd); // 关闭文件描述符
return 0;
}
读进程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
const char *fifoPath = "my_fifo";
int fd = open(fifoPath, O_RDONLY); // 打开管道以读取数据
char buffer[100];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
if (bytesRead > 0) {
buffer[bytesRead] = '\0';
printf("Received message: %s\n", buffer);
}
close(fd); // 关闭文件描述符
return 0;
}
2、信号量
2.1 概念
信号量是一种用于实现进程间同步和互斥的机制。它是一种同步原语,用于协调多个进程对共享资源的访问,以避免竞态条件(Race Condition)和数据冲突。信号量主要用于解决多个进程并发执行时可能出现的问题。
①进程间通信信号量具有以下特点:
同步: 信号量用于确保多个进程按照特定的顺序执行。通过等待和释放信号量,进程可以控制其执行的时间点,从而协调操作的顺序。
互斥: 信号量用于实现互斥,确保在任何时刻只有一个进程能够访问共享资源。通过使用信号量,进程可以避免多个进程同时修改相同的资源而引发冲突。
②进程间通信信号量的操作通常包括以下两种:
P(Wait)操作: 当一个进程想要使用共享资源时,它尝试执行 P 操作。如果信号量的计数值大于零,它会将计数值减少并获得访问权限。如果计数值为零,该进程将被阻塞,直到计数值大于零。
V(Signal)操作: 当进程使用完共享资源时,它执行 V 操作来释放信号量。这会将信号量的计数值增加,并唤醒等待的进程。
③信号量在进程间通信中有许多应用
进程同步: 确保进程按特定顺序执行,避免竞态条件。
互斥访问共享资源: 保证只有一个进程能够访问共享资源,防止数据损坏。
限制资源访问: 控制同时访问某些资源的进程数量,防止过多的进程占用资源。
2.2 API详解
常用的信号量相关的函数是在 POSIX 标准中定义的,用于在进程间通信中创建和操作信号量。下面是一些常用的信号量函数以及信号量的应用举例:
int sem_init(sem_t *sem, int pshared, unsigned int value);
- 初始化信号量。
- 参数 `sem` 是信号量指针,`pshared` 表示信号量的共享方式,`value` 是初始计数值。
- 返回值:成功返回0,失败返回-1。
int sem_wait(sem_t *sem);
- 对信号量执行 P(Wait)操作。
- 参数 `sem` 是信号量指针。
- 返回值:成功返回0,失败返回-1。
int sem_post(sem_t *sem);
- 对信号量执行 V(Signal)操作。
- 参数 `sem` 是信号量指针。
- 返回值:成功返回0,失败返回-1。
int sem_destroy(sem_t *sem);
- 销毁信号量。
- 参数 `sem` 是信号量指针。
- 返回值:成功返回0,失败返回-1。
2.3 使用示例
①生产者进程(共享内存作为进程间通信介质)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/mman.h>
#define BUFFER_SIZE 5
typedef struct {
int buffer[BUFFER_SIZE];
int in;
int out;
sem_t empty;
sem_t full;
sem_t mutex;
} shared_data_t;
int main() {
// 创建共享内存
int shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, sizeof(shared_data_t));
shared_data_t *shared_data = (shared_data_t *)mmap(NULL, sizeof(shared_data_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
// 初始化信号量
sem_init(&(shared_data->empty), 1, BUFFER_SIZE);
sem_init(&(shared_data->full), 1, 0);
sem_init(&(shared_data->mutex), 1, 1);
int data = 1;
while (1) {
sem_wait(&(shared_data->empty)); // 等待缓冲区非满
sem_wait(&(shared_data->mutex)); // 加锁
shared_data->buffer[shared_data->in] = data;
printf("Produced: %d\n", data);
data++;
shared_data->in = (shared_data->in + 1) % BUFFER_SIZE;
sem_post(&(shared_data->mutex)); // 解锁
sem_post(&(shared_data->full)); // 增加缓冲区内数据数量
sleep(1); // 生产数据后等待一段时间
}
munmap(shared_data, sizeof(shared_data_t));
shm_unlink("/my_shm");
sem_destroy(&(shared_data->empty));
sem_destroy(&(shared_data->full));
sem_destroy(&(shared_data->mutex));
return 0;
}
②消费者进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/mman.h>
#define BUFFER_SIZE 5
typedef struct {
int buffer[BUFFER_SIZE];
int in;
int out;
sem_t empty;
sem_t full;
sem_t mutex;
} shared_data_t;
int main() {
// 打开共享内存
int shm_fd = shm_open("/my_shm", O_RDWR, 0666);
shared_data_t *shared_data = (shared_data_t *)mmap(NULL, sizeof(shared_data_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
while (1) {
sem_wait(&(shared_data->full)); // 等待缓冲区非空
sem_wait(&(shared_data->mutex)); // 加锁
int data = shared_data->buffer[shared_data->out];
printf("Consumed: %d\n", data);
shared_data->out = (shared_data->out + 1) % BUFFER_SIZE;
sem_post(&(shared_data->mutex)); // 解锁
sem_post(&(shared_data->empty)); // 增加缓冲区空闲数量
sleep(2); // 消费数据后等待一段时间
}
munmap(shared_data, sizeof(shared_data_t));
close(shm_fd);
return 0;
}
③线程同步应用
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t semaphore1, semaphore2;
void* thread1_function(void* arg) {
sem_wait(&semaphore1); // 等待信号量1
printf("Thread 1 is executing.\n");
sem_post(&semaphore2); // 释放信号量2,允许线程2执行
return NULL;
}
void* thread2_function(void* arg) {
sem_wait(&semaphore2); // 等待信号量2,确保线程1先执行
printf("Thread 2 is executing.\n");
sem_post(&semaphore1); // 释放信号量1,允许线程1再次执行
return NULL;
}
int main() {
sem_init(&semaphore1, 0, 1); // 初始化信号量1为1
sem_init(&semaphore2, 0, 0); // 初始化信号量2为0
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, thread1_function, NULL);
pthread_create(&thread2, NULL, thread2_function, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
sem_destroy(&semaphore1);
sem_destroy(&semaphore2);
return 0;
}
④线程互斥访问共享资源
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t semaphore;
int shared_variable = 0;
void* thread1_function(void* arg) {
sem_wait(&semaphore); // 请求信号量
shared_variable++;
printf("Thread 1: Incremented shared variable to %d.\n", shared_variable);
sem_post(&semaphore); // 释放信号量
return NULL;
}
void* thread2_function(void* arg) {
sem_wait(&semaphore); // 请求信号量
shared_variable--;
printf("Thread 2: Decremented shared variable to %d.\n", shared_variable);
sem_post(&semaphore); // 释放信号量
return NULL;
}
int main() {
sem_init(&semaphore, 0, 1); // 初始化信号量为1
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, thread1_function, NULL);
pthread_create(&thread2, NULL, thread2_function, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
sem_destroy(&semaphore);
return 0;
}
⑤线程限制资源访问
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t semaphore;
int resource_count = 3;
void* thread_function(void* arg) {
sem_wait(&semaphore); // 请求信号量
resource_count--;
printf("Thread %d: Accessed resource. Remaining resources: %d.\n", (int)arg, resource_count);
sem_post(&semaphore); // 释放信号量
return NULL;
}
int main() {
sem_init(&semaphore, 0, 3); // 初始化信号量为3,允许同时访问3个资源
pthread_t threads[5];
for (int i = 0; i < 5; i++) {
pthread_create(&threads[i], NULL, thread_function, (void*)(i + 1));
}
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
sem_destroy(&semaphore);
return 0;
}
3、消息队列
3.1 概念
消息队列是一种进程间通信的机制,用于在不同进程之间传递数据。它允许一个进程向消息队列发送消息,而另一个进程可以从队列中读取这些消息。消息队列是一种异步通信方式,进程可以通过消息队列实现数据的交换,而不需要直接共享内存。
消息队列的特点包括:
1. 异步通信:发送者将消息发送到队列后,不需要等待接收者处理完消息就可以继续自己的工作。
2. 松耦合:消息队列实现了进程间的松耦合,发送者和接收者之间无需直接知道彼此的存在。
3. 缓冲:消息队列可以缓存一定数量的消息,即使接收者暂时不可用,消息也不会丢失。
4. 多对多:多个进程可以同时向一个队列发送消息,也可以从队列读取消息。
5. 消息优先级:消息队列通常支持消息的优先级,以确保重要的消息能够尽快被处理。
消息队列在操作系统中是由内核维护的,通常由消息队列标识符来标识不同的消息队列。
3.2 API函数
消息队列通常使用操作系统提供的API来进行创建、发送和接收消息。以下是System V消息队列的常用API函数及其详细解释:
1. int msgget(key_t key, int msgflg);
- 创建或获取消息队列。
- 参数 `key` 是消息队列键值,`msgflg` 是标志位(如 `IPC_CREAT` 表示创建队列)。
- 返回值:成功返回消息队列标识符(非负整数),失败返回-1。
2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- 发送消息到消息队列。
- 参数 `msqid` 是消息队列标识符,`msgp` 是指向消息结构的指针,`msgsz` 是消息的大小,`msgflg` 是标志位。
- 返回值:成功返回0,失败返回-1。
3. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
- 从消息队列接收消息。
- 参数 `msqid` 是消息队列标识符,`msgp` 是指向消息结构的指针,`msgsz` 是接收缓冲区大小,`msgtyp` 是消息的类型,`msgflg` 是标志位。
- 返回值:成功返回接收的消息大小,失败返回-1。
4. int msgctl(int msqid, int cmd, struct msqid_ds *buf);
- 控制消息队列属性。
- 参数 `msqid` 是消息队列标识符,`cmd` 是控制命令(如 `IPC_RMID` 表示删除队列),`buf` 是指向 `msqid_ds` 结构的指针用于获取或设置属性。
- 返回值:成功返回0,失败返回-1。
5. key_t ftok(const char *pathname, int proj_id);
pathname:一个存在的文件路径名,用于生成键值。通常选择一个在系统中具有唯一性的文件。
proj_id:一个整数,用于在 `pathname` 的基础上生成键值的一部分。这个值应该被选取为正整数,不同的项目可以选择不同的值。
返回值:返回一个 `key_t` 类型的键值,用于后续的系统调用。如果出错,返回 -1。
`ftok` 函数通过结合给定的文件路径名和项目标识号来生成一个键值,这个键值可以被用于创建和访问共享内存、消息队列等。在实际使用中,`ftok` 生成的键值应该在相关的进程间通信操作中保持一致。
3.3 应用代码
①发送进程示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct message {
long type;
char text[50];
};
int main() {
key_t key;
int msgid;
struct message msg;
// 生成键值
key = ftok("message_queue_example", 65);
// 创建消息队列
msgid = msgget(key, 0666 | IPC_CREAT);
// 设置消息
msg.type = 1;
strcpy(msg.text, "Hello from sender!");
// 发送消息
msgsnd(msgid, &msg, sizeof(msg.text), 0);
return 0;
}
②接收进程示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct message {
long type;
char text[50];
};
int main() {
key_t key;
int msgid;
struct message msg;
// 生成键值
key = ftok("message_queue_example", 65);
// 获取消息队列
msgid = msgget(key, 0666 | IPC_CREAT);
// 接收消息
msgrcv(msgid, &msg, sizeof(msg.text), 1, 0);
printf("Received message: %s\n", msg.text);
// 删除消息队列
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
4、共享内存
4.1 概念
共享内存是一种进程间通信机制,允许多个进程共享同一块物理内存区域。相对于其他进程间通信方式,共享内存的特点是高效、快速,适用于大量数据的交换。
4.2 API
①key_t ftok(const char *pathname, int proj_id);
pathname:一个存在的文件路径名,用于生成键值。通常选择一个在系统中具有唯一性的文件。
proj_id:一个整数,用于在 `pathname` 的基础上生成键值的一部分。这个值应该被选取为正整数,不同的项目可以选择不同的值。
返回值:返回一个 `key_t` 类型的键值,用于后续的系统调用。如果出错,返回 -1。
`ftok` 函数通过结合给定的文件路径名和项目标识号来生成一个键值,这个键值可以被用于创建和访问共享内存、消息队列等。在实际使用中,`ftok` 生成的键值应该在相关的进程间通信操作中保持一致。
②int shmget(key_t key, size_t size, int shmflg);
- 创建或获取共享内存段。
- `key`:用于标识共享内存的键值。
- `size`:指定共享内存段的大小。
- `shmflg`:用于控制共享内存的标志,可以包括 `IPC_CREAT` 来创建共享内存。
- 返回值:成功时返回共享内存标识符,失败时返回-1。
③void *shmat(int shmid, const void *shmaddr, int shmflg);
- 连接到共享内存段。
- `shmid`:共享内存标识符。
- `shmaddr`:指定连接地址,通常设为NULL,由系统决定。
- `shmflg`:用于控制共享内存的标志。
- 返回值:连接成功时返回连接地址,失败时返回 `(void*)-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。
4.3 示例代码
创建共享内存
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
key_t key = ftok("shared_memory_example", 65); // 生成键值
int shmid = shmget(key, sizeof(int), 0666 | IPC_CREAT); // 创建共享内存
if (shmid == -1) {
perror("shmget");
exit(1);
}
int *shared_data = (int*)shmat(shmid, NULL, 0); // 连接到共享内存
*shared_data = 10; // 设置共享数据
printf("Shared data set to %d\n", *shared_data);
shmdt(shared_data); // 断开与共享内存的连接
return 0;
}
使用共享内存
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
key_t key = ftok("shared_memory_example", 65); // 生成键值
int shmid = shmget(key, sizeof(int), 0666); // 获取共享内存
if (shmid == -1) {
perror("shmget");
exit(1);
}
int *shared_data = (int*)shmat(shmid, NULL, 0); // 连接到共享内存
printf("Shared data read: %d\n", *shared_data);
shmdt(shared_data); // 断开与共享内存的连接
return 0;
}
5、套接字
5.1 概念
套接字(Socket)是一种用于实现网络通信的编程接口,它提供了一种机制,使得不同计算机之间可以通过网络进行数据交换。套接字允许进程在不同主机上的应用程序通过网络进行通信,从而实现分布式系统和客户端-服务器模型。
套接字提供了一组API函数,用于在应用程序中创建、连接、发送和接收数据。它是网络编程的基础,用于实现各种通信协议,如TCP(传输控制协议)和UDP(用户数据报协议)。
5.2 API
1. int socket(int domain, int type, int protocol);
- 创建一个新的套接字。
- `domain`:指定套接字的协议族,如 `AF_INET` 表示IPv4。
- `type`:指定套接字的类型,如 `SOCK_STREAM` 表示面向连接的流套接字(TCP)。
- `protocol`:指定使用的传输协议,通常设为0以便自动选择合适的协议。
- 返回值:成功时返回套接字文件描述符,失败时返回-1。
2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 将一个本地地址与套接字绑定。
- `sockfd`:套接字文件描述符。
- `addr`:指向本地地址结构的指针。
- `addrlen`:本地地址结构的大小。
- 返回值:成功时返回0,失败时返回-1。
3. int listen(int sockfd, int backlog);
- 将套接字设为监听模式,准备接受连接请求。
- `sockfd`:套接字文件描述符。
- `backlog`:等待连接的队列长度。
- 返回值:成功时返回0,失败时返回-1。
4. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 接受一个连接请求,创建一个新的套接字用于与客户端通信。
- `sockfd`:监听套接字文件描述符。
- `addr`:用于存储客户端地址的结构指针。
- `addrlen`:客户端地址结构的大小。
- 返回值:成功时返回新创建的套接字文件描述符,失败时返回-1。
5. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 建立与远程服务器的连接。
- `sockfd`:套接字文件描述符。
- `addr`:指向远程服务器地址结构的指针。
- `addrlen`:远程服务器地址结构的大小。
- 返回值:成功时返回0,失败时返回-1。
6. ssize_t send(int sockfd, const void *buf, size_t len, int flags);
- 发送数据。
- `sockfd`:套接字文件描述符。
- `buf`:指向待发送数据的缓冲区。
- `len`:待发送数据的大小。
- `flags`:选项标志,通常设为0。
- 返回值:成功时返回发送的字节数,失败时返回-1。
7. ssize_t recv(int sockfd, void *buf, size_t len, int flags);
- 接收数据。
- `sockfd`:套接字文件描述符。
- `buf`:用于存储接收数据的缓冲区。
- `len`:缓冲区的大小。
- `flags`:选项标志,通常设为0。
- 返回值:成功时返回接收的字节数,连接关闭时返回0,失败时返回-1。
8. int close(int sockfd);
- 关闭套接字连接。
- `sockfd`:套接字文件描述符。
- 返回值:成功时返回0,失败时返回-1。
5.3 示例代码
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Error opening socket");
exit(1);
}
bzero((char *)&serv_addr, sizeof(serv_addr));
portno = 12345;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
// 绑定套接字到地址
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Error on binding");
exit(1);
}
// 监听连接
listen(sockfd, 5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if (newsockfd < 0) {
perror("Error on accept");
exit(1);
}
// 读取客户端发送的消息
bzero(buffer, 256);
n = read(newsockfd, buffer, 255);
if (n < 0) {
perror("Error reading from socket");
exit(1);
}
printf("Message from client: %s\n", buffer);
// 关闭连接
close(newsockfd);
close(sockfd);
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
int main() {
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
portno = 12345;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Error opening socket");
exit(1);
}
// 获取服务器信息
server = gethostbyname("localhost");
if (server == NULL) {
fprintf(stderr, "Error, no such host\n");
exit(1);
}
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
// 连接到服务器
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Error connecting");
exit(1);
}
printf("Please enter the message: ");
bzero(buffer, 256);
fgets(buffer, 255, stdin);
n = write(sockfd, buffer, strlen(buffer));
if (n < 0) {
perror("Error writing to socket");
exit(1);
}
// 关闭套接字
close(sockfd);
return 0;
}