在C语言中实现网络编程的客户端和服务器,主要涉及TCP/IP协议的使用,以下是对相关概念、代码实现以及常见问题解答的详细阐述。
一、基本概念
1、套接字(Socket):用于描述IP地址和端口,是通信链的句柄,可以实现不同虚拟机或计算机之间的通信。
2、TCP协议:一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
二、服务器端流程
1、初始化并监听:服务器首先调用socket()
函数创建套接字,然后使用bind()
函数将套接字与指定的IP地址和端口号绑定,接着调用listen()
函数声明当前文件描述符为服务器的文件描述符,最后调用accept()
函数阻塞等待客户端连接。
2、建立连接:当客户端发起连接请求时,服务器通过三次握手建立连接。
3、数据传输:连接建立后,服务器从accept()
函数返回,并立即调用read()
函数读取数据,客户端则调用write()
函数发送请求。
4、断开连接:客户端没有更多请求时调用close()
函数关闭连接,服务器收到FIN后回应ACK,并关闭连接。
三、客户端流程
1、连接服务器:客户端调用socket()
函数创建套接字,然后使用connect()
函数向服务器发起连接请求。
2、数据传输:连接建立后,客户端调用write()
函数发送请求,服务器收到请求后处理并返回应答,客户端调用read()
函数接收应答。
3、断开连接:客户端完成数据传输后,调用close()
函数关闭套接字,结束通信。
四、示例代码
1. 客户端代码(client.c)
#include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #define MAX 80 #define PORT 8080 #define SA struct sockaddr void func(int sockfd) { char buff[MAX]; int n; for (;;) { bzero(buff, sizeof(buff)); printf("Enter the string : "); n = 0; while ((buff[n++] = getchar()) != ' '); write(sockfd, buff, sizeof(buff)); bzero(buff, sizeof(buff)); read(sockfd, buff, sizeof(buff)); printf("From Server : %s", buff); if ((strncmp(buff, "exit", 4)) == 0) { printf("Client Exit... "); break; } } } int main(int argc, char const *argv[]){ int sockfd; //连接符 struct sockaddr_in servaddr; // 套接字创建 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("socket creation failed... "); exit(0); } else printf("Socket successfully created.. "); bzero(&servaddr, sizeof(servaddr)); //初始化结构体 //分配IP,端口 servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); servaddr.sin_port = htons(PORT); //连接服务器,如果非0,则连接失败 if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { printf("connection with the server failed... "); exit(0); } else printf("connected to the server.. "); func(sockfd); // 关闭套接字 close(sockfd); }
2. 服务端代码(server.c)
#include <netdb.h> #include <netinet/in.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #define MAX 80 #define PORT 8080 #define SA struct sockaddr void func(int sockfd) { char buff[MAX]; int n; for (;;) { bzero(buff, sizeof(buff)); read(sockfd, buff, sizeof(buff)); printf("From client : %s", buff); bzero(buff, sizeof(buff)); printf("Enter the string : "); n = 0; while ((buff[n++] = getchar()) != ' '); write(sockfd, buff, sizeof(buff)); if ((strncmp(buff, "exit", 4)) == 0) { printf("Server Exit... "); break; } } } int main() { int sockfd, connfd; //连接符 struct sockaddr_in servaddr, cliaddr; // 套接字创建 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("socket creation failed... "); exit(0); } else printf("Socket successfully created.. "); bzero(&servaddr, sizeof(servaddr)); //初始化结构体 //分配IP,端口 servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); //绑定套接字到指定IP和端口 if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { printf("socket bind failed... "); exit(0); } else printf("Socket successfully binded.. "); //开始监听,等待连接请求 if ((listen(sockfd, 5)) != 0) { printf("Listen failed... "); exit(0); } else printf("Server listening.. "); //接受客户端连接请求 while (1) { connfd = accept(sockfd, (SA*)&cliaddr, &cliaddr); if (connfd < 0) { printf("server acccept failed... "); exit(0); } else printf("server acccept the client... "); //调用func函数进行数据交互 func(connfd); //关闭套接字连接 close(connfd); } }
五、相关问题与解答栏目
问题1:为什么客户端通常不需要绑定端口?
答:在大多数情况下,客户端不需要绑定端口号,客户端通常只是连接到服务器的指定IP地址和端口号来发送请求或接收响应,客户端操作系统会为客户端分配一个临时的端口号来进行通信,这个端口号是由操作系统自动选择的,客户端无需手动指定,在某些特殊场景下(如P2P应用、多客户端连接等),客户端可能需要明确绑定端口。
问题2:如何让外部设备访问本地服务器上的服务?
答:要让外部设备能够访问本地服务器上的服务,可以将服务器绑定的IP地址设置为0.0.0.0(表示监听所有网络接口上的请求)或者服务器的真实IP地址,确保防火墙设置允许外部访问该端口。
小伙伴们,上文介绍了“c网络编程客户端服务器”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。