socket server.c & client.c

本文详细介绍了如何使用TCP协议实现服务器与客户端之间的通信。包括创建服务器端套接字、绑定端口、监听连接、接收并发送数据的过程,以及客户端如何通过指定服务器地址和端口进行连接,并发送接收信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



/* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno;
     socklen_t clilen;
     char buffer[256];
     struct sockaddr_in serv_addr, cli_addr;
     int n;
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0)
        error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     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)
              error("ERROR on binding");
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     newsockfd = accept(sockfd,
                 (struct sockaddr *) &cli_addr,
                 &clilen);
     if (newsockfd < 0)
          error("ERROR on accept");
     bzero(buffer,256);
     n = read(newsockfd,buffer,255);
     if (n < 0) error("ERROR reading from socket");
     printf("Here is the message: %s\n",buffer);
     n = write(newsockfd,"I got your message",18);
     if (n < 0) error("ERROR writing to socket");
     close(newsockfd);
     close(sockfd);
     return 0;
}

 

 

//client.c

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

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[256];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    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)
        error("ERROR connecting");
    printf("Please enter the message: ");
    bzero(buffer,256);
    fgets(buffer,255,stdin);
    n = write(sockfd,buffer,strlen(buffer));
    if (n < 0)
         error("ERROR writing to socket");
    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    if (n < 0)
         error("ERROR reading from socket");
    printf("%s\n",buffer);
    close(sockfd);
    return 0;
}

 

&lt;think&gt;我们使用C语言和BSD套接字API来实现TCP双向通信。以下代码示例包括两个文件:server.c(服务器端)和client.c(客户端)。服务器将监听指定端口,客户端连接后,双方可以互相发送消息。 注意:此代码在Linux环境下编译运行(Windows下需稍作修改,如使用Winsock2)。 ### 1. 服务器端代码 (server.c) ```c #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;sys/socket.h&gt; #define PORT 8888 #define BUFFER_SIZE 1024 int main() { int server_fd, client_fd; struct sockaddr_in server_addr, client_addr; socklen_t client_addr_len = sizeof(client_addr); char buffer[BUFFER_SIZE] = {0}; // 1. 创建TCP socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror(&quot;socket创建失败&quot;); exit(EXIT_FAILURE); } // 2. 绑定IP和端口 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; // 接受任意IP的连接 server_addr.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr*)&amp;server_addr, sizeof(server_addr)) &lt; 0) { perror(&quot;绑定失败&quot;); close(server_fd); exit(EXIT_FAILURE); } // 3. 监听连接 if (listen(server_fd, 5) &lt; 0) { // 最大等待队列为5 perror(&quot;监听失败&quot;); close(server_fd); exit(EXIT_FAILURE); } printf(&quot;服务器正在监听端口 %d...\n&quot;, PORT); // 4. 接受客户端连接 if ((client_fd = accept(server_fd, (struct sockaddr*)&amp;client_addr, &amp;client_addr_len)) &lt; 0) { perror(&quot;接受连接失败&quot;); close(server_fd); exit(EXIT_FAILURE); } printf(&quot;客户端已连接: %s\n&quot;, inet_ntoa(client_addr.sin_addr)); // 5. 发送欢迎消息给客户端 const char *welcome_msg = &quot;欢迎连接服务器!请输入消息:&quot;; send(client_fd, welcome_msg, strlen(welcome_msg), 0); // 6. 循环进行双向通信 while (1) { // 接收客户端消息 memset(buffer, 0, BUFFER_SIZE); ssize_t bytes_received = recv(client_fd, buffer, BUFFER_SIZE, 0); if (bytes_received &lt;= 0) { perror(&quot;接收失败或连接关闭&quot;); break; } printf(&quot;客户端: %s&quot;, buffer); // 检查是否收到退出指令 if (strncmp(buffer, &quot;exit&quot;, 4) == 0) { printf(&quot;客户端请求退出。\n&quot;); break; } // 发送响应消息 printf(&quot;请输入回复消息 (输入exit退出): &quot;); fgets(buffer, BUFFER_SIZE, stdin); send(client_fd, buffer, strlen(buffer), 0); if (strncmp(buffer, &quot;exit&quot;, 4) == 0) { printf(&quot;服务器退出。\n&quot;); break; } } // 7. 关闭连接 close(client_fd); close(server_fd); return 0; } ``` ### 2. 客户端代码 (client.c) ```c #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;sys/socket.h&gt; #define SERVER_IP &quot;127.0.0.1&quot; #define PORT 8888 #define BUFFER_SIZE 1024 int main() { int sock; struct sockaddr_in server_addr; char buffer[BUFFER_SIZE] = {0}; // 1. 创建TCP socket if ((sock = socket(AF_INET, SOCK_STREAM, 0)) &lt; 0) { perror(&quot;socket创建失败&quot;); exit(EXIT_FAILURE); } // 2. 配置服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); if (inet_pton(AF_INET, SERVER_IP, &amp;server_addr.sin_addr) &lt;= 0) { perror(&quot;地址转换失败&quot;); close(sock); exit(EXIT_FAILURE); } // 3. 连接服务器 if (connect(sock, (struct sockaddr*)&amp;server_addr, sizeof(server_addr)) &lt; 0) { perror(&quot;连接服务器失败&quot;); close(sock); exit(EXIT_FAILURE); } printf(&quot;已连接到服务器 %s:%d\n&quot;, SERVER_IP, PORT); // 4. 接收服务器欢迎消息 memset(buffer, 0, BUFFER_SIZE); recv(sock, buffer, BUFFER_SIZE, 0); printf(&quot;服务器: %s\n&quot;, buffer); // 5. 循环进行双向通信 while (1) { // 发送消息给服务器 printf(&quot;请输入消息 (输入exit退出): &quot;); fgets(buffer, BUFFER_SIZE, stdin); send(sock, buffer, strlen(buffer), 0); if (strncmp(buffer, &quot;exit&quot;, 4) == 0) { printf(&quot;客户端退出。\n&quot;); break; } // 接收服务器响应 memset(buffer, 0, BUFFER_SIZE); ssize_t bytes_received = recv(sock, buffer, BUFFER_SIZE, 0); if (bytes_received &lt;= 0) { perror(&quot;接收失败或连接关闭&quot;); break; } printf(&quot;服务器: %s&quot;, buffer); // 检查服务器是否退出 if (strncmp(buffer, &quot;exit&quot;, 4) == 0) { break; } } // 6. 关闭连接 close(sock); return 0; } ``` ### 3. 编译与运行方法 1. **编译服务器端**: ```bash gcc server.c -o server ``` 2. **编译客户端**: ```bash gcc client.c -o client ``` 3. **运行**: ```bash # 先启动服务器 ./server # 再启动客户端(新终端) ./client ``` ### 4. 通信流程说明 1. **连接建立**: - 服务器启动监听8888端口 - 客户端连接服务器 - 服务器发送欢迎消息 2. **双向通信**: - 客户端输入消息 -&gt; 服务器接收并显示 - 服务器输入回复 -&gt; 客户端接收并显示 - 任意一方输入&quot;exit&quot;可终止连接 ### 5. 关键函数说明 1. **服务器端**: - `socket()`:创建TCP套接字 - `bind()`:绑定IP和端口 - `listen()`:开始监听 - `accept()`:接受客户端连接 - `recv()`/`send()`:接收/发送数据 2. **客户端**: - `connect()`:连接服务器 - `recv()`/`send()`:接收/发送数据 ### 6. 注意事项 1. **阻塞调用**: - `recv()`和`accept()`是阻塞调用,程序会等待直到有数据或连接到达 2. **错误处理**: - 每个系统调用后都进行了错误检查,确保程序健壮性 3. **多客户端支持**: - 当前服务器仅支持单客户端,如需多客户端需使用多线程/多进程或IO复用 4. **数据边界**: - TCP是流式协议,无消息边界,实际应用中需设计协议(如长度前缀) ### 7. 扩展建议 1. **多线程服务器**: ```c // 在accept后创建线程 pthread_t thread_id; pthread_create(&amp;thread_id, NULL, handle_client, (void*)&amp;client_fd); ``` 2. **非阻塞IO**: - 使用`fcntl()`设置非阻塞模式,配合`select()`/`poll()`实现高并发 3. **数据序列化**: - 使用JSON或Protocol Buffers进行结构化数据传输 4. **加密通信**: - 集成OpenSSL实现TLS加密(参考引用[^2]) &sect;&sect; 相关问题 &sect;&sect; 1. 如何实现服务器同时处理多个客户端连接? 2. 在C语言网络编程中如何处理TCP粘包问题? 3. 如何用select()实现非阻塞IO的TCP服务器? 4. 如何将上述TCP通信示例改为UDP通信? 5. 如何在Windows平台使用Winsock实现相同的双向通信?[^1]&lt;/think&gt;下面我将提供完整的C语言实现TCP双向通信的代码示例,包含服务器端(server.c)和客户端(client.c)代码。这个实现允许双方互相发送消息,解决了您提到的服务器无法向客户端发送消息的问题。 ### 服务器端代码 (server.c) ```c #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;sys/socket.h&gt; #include &lt;pthread.h&gt; #define PORT 8888 #define BUFFER_SIZE 1024 // 客户端处理线程 void *handle_client(void *arg) { int client_socket = *((int *)arg); char buffer[BUFFER_SIZE] = {0}; // 向客户端发送欢迎消息 const char *welcome = &quot;服务器: 欢迎连接! 输入消息开始对话(输入&#39;exit&#39;退出)\n&quot;; send(client_socket, welcome, strlen(welcome), 0); while (1) { // 接收客户端消息 memset(buffer, 0, BUFFER_SIZE); ssize_t bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0); if (bytes_received &lt;= 0) { perror(&quot;接收失败或连接关闭&quot;); break; } // 打印客户端消息 printf(&quot;客户端: %s&quot;, buffer); // 检查退出指令 if (strncmp(buffer, &quot;exit&quot;, 4) == 0) { printf(&quot;客户端请求退出\n&quot;); break; } // 准备服务器回复 printf(&quot;请输入回复消息: &quot;); fgets(buffer, BUFFER_SIZE, stdin); // 发送回复 ssize_t bytes_sent = send(client_socket, buffer, strlen(buffer), 0); if (bytes_sent &lt; 0) { perror(&quot;发送失败&quot;); break; } } close(client_socket); pthread_exit(NULL); } int main() { int server_fd, client_socket; struct sockaddr_in server_addr, client_addr; socklen_t client_addr_len = sizeof(client_addr); // 创建TCP socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror(&quot;socket创建失败&quot;); exit(EXIT_FAILURE); } // 配置服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); // 绑定端口 if (bind(server_fd, (struct sockaddr *)&amp;server_addr, sizeof(server_addr)) &lt; 0) { perror(&quot;绑定失败&quot;); close(server_fd); exit(EXIT_FAILURE); } // 监听连接 if (listen(server_fd, 3) &lt; 0) { perror(&quot;监听失败&quot;); close(server_fd); exit(EXIT_FAILURE); } printf(&quot;服务器启动,监听端口 %d...\n&quot;, PORT); while (1) { // 接受客户端连接 if ((client_socket = accept(server_fd, (struct sockaddr *)&amp;client_addr, &amp;client_addr_len)) &lt; 0) { perror(&quot;接受连接失败&quot;); continue; } printf(&quot;客户端已连接: %s:%d\n&quot;, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // 创建线程处理客户端 pthread_t thread_id; if (pthread_create(&amp;thread_id, NULL, handle_client, (void *)&amp;client_socket) &lt; 0) { perror(&quot;线程创建失败&quot;); close(client_socket); } // 分离线程,使其结束后自动释放资源 pthread_detach(thread_id); } close(server_fd); return 0; } ``` ### 客户端代码 (client.c) ```c #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;sys/socket.h&gt; #include &lt;pthread.h&gt; #define SERVER_IP &quot;127.0.0.1&quot; // 服务器IP #define PORT 8888 #define BUFFER_SIZE 1024 // 接收消息线程 void *receive_messages(void *arg) { int sock = *((int *)arg); char buffer[BUFFER_SIZE] = {0}; while (1) { // 接收服务器消息 memset(buffer, 0, BUFFER_SIZE); ssize_t bytes_received = recv(sock, buffer, BUFFER_SIZE, 0); if (bytes_received &lt;= 0) { perror(&quot;接收失败或连接关闭&quot;); break; } // 打印服务器消息 printf(&quot;%s&quot;, buffer); } pthread_exit(NULL); } int main() { int sock = 0; struct sockaddr_in serv_addr; pthread_t recv_thread; // 创建TCP socket if ((sock = socket(AF_INET, SOCK_STREAM, 0)) &lt; 0) { perror(&quot;socket创建失败&quot;); exit(EXIT_FAILURE); } // 配置服务器地址 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 转换IP地址 if (inet_pton(AF_INET, SERVER_IP, &amp;serv_addr.sin_addr) &lt;= 0) { perror(&quot;地址转换失败&quot;); close(sock); exit(EXIT_FAILURE); } // 连接服务器 if (connect(sock, (struct sockaddr *)&amp;serv_addr, sizeof(serv_addr)) &lt; 0) { perror(&quot;连接服务器失败&quot;); close(sock); exit(EXIT_FAILURE); } printf(&quot;已连接到服务器 %s:%d\n&quot;, SERVER_IP, PORT); // 创建接收消息线程 if (pthread_create(&amp;recv_thread, NULL, receive_messages, (void *)&amp;sock) &lt; 0) { perror(&quot;线程创建失败&quot;); close(sock); exit(EXIT_FAILURE); } // 主线程处理用户输入 char buffer[BUFFER_SIZE] = {0}; while (1) { // 获取用户输入 printf(&quot;请输入消息: &quot;); fgets(buffer, BUFFER_SIZE, stdin); // 发送消息到服务器 ssize_t bytes_sent = send(sock, buffer, strlen(buffer), 0); if (bytes_sent &lt; 0) { perror(&quot;发送失败&quot;); break; } // 检查退出指令 if (strncmp(buffer, &quot;exit&quot;, 4) == 0) { printf(&quot;退出程序...\n&quot;); break; } } // 清理资源 close(sock); pthread_cancel(recv_thread); // 取消接收线程 pthread_join(recv_thread, NULL); // 等待线程结束 return 0; } ``` ### 编译与运行方法 1. **编译服务器端**: ```bash gcc server.c -o server -lpthread ``` 2. **编译客户端**: ```bash gcc client.c -o client -lpthread ``` 3. **运行**: ```bash # 先启动服务器 ./server # 再启动客户端(新终端) ./client ``` ### 通信流程说明 1. **服务器启动**: - 创建TCP socket并绑定端口 - 监听客户端连接 - 为每个客户端创建独立线程处理通信 2. **客户端连接**: - 创建TCP socket - 连接服务器 - 创建独立线程接收服务器消息 3. **双向通信**: - 客户端主线程处理用户输入并发送 - 客户端接收线程显示服务器消息 - 服务器线程接收客户端消息并发送回复 ### 关键功能实现 1. **服务器主动发送消息**: ```c // 服务器发送消息 send(client_socket, buffer, strlen(buffer), 0); ``` 2. **客户端接收消息**: ```c // 客户端接收线程 bytes_received = recv(sock, buffer, BUFFER_SIZE, 0); printf(&quot;%s&quot;, buffer); ``` 3. **多线程处理**: ```c // 服务器为每个客户端创建线程 pthread_create(&amp;thread_id, NULL, handle_client, (void *)&amp;client_socket); // 客户端创建接收线程 pthread_create(&amp;recv_thread, NULL, receive_messages, (void *)&amp;sock); ``` ### 常见问题解决 1. **连接失败**: - 检查服务器IP是否正确 - 确保防火墙允许端口8888通信 - 验证服务器是否已启动 2. **消息乱码**: - 确保双方使用相同的字符编码 - 检查网络字节序转换(htons/ntohs) 3. **资源泄漏**: - 使用close()关闭所有socket - 使用pthread_detach()或pthread_join()管理线程 4. **端口占用**: ```bash # 查看端口占用情况 netstat -tuln | grep 8888 ``` ### 扩展功能建议 1. **多客户端支持**: ```c // 使用线程池管理多个客户端 #define MAX_CLIENTS 10 int client_sockets[MAX_CLIENTS] = {0}; ``` 2. **消息广播**: ```c // 向所有客户端广播消息 for (int i = 0; i &lt; MAX_CLIENTS; i++) { if (client_sockets[i] &gt; 0) { send(client_sockets[i], buffer, strlen(buffer), 0); } } ``` 3. **加密通信**: ```c // 使用OpenSSL添加TLS加密(参考引用[^2]) #include &lt;openssl/ssl.h&gt; #include &lt;openssl/err.h&gt; ``` 4. **协议设计**: ```c // 添加消息头标识 struct MessageHeader { uint32_t length; uint8_t type; }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值