C语言Socket编程中服务端与客户端通信机制问询
C语言Socket编程中服务端与客户端的通信原理解析
你使用的是TCP流式套接字(SOCK_STREAM),这类通信基于TCP三次握手建立可靠连接,下面结合你提供的代码,一步步拆解通信流程,解答你关于阻塞与连接触发的疑问:
一、服务端启动与等待流程
- Winsock环境初始化:Windows平台必须先调用
WSAStartup加载套接字库,完成Socket环境的初始化,这是后续所有Socket操作的前提。 - 创建监听套接字:
socket(AF_INET, SOCK_STREAM, 0)创建一个IPv4的流式套接字,这个套接字的唯一作用是监听客户端的连接请求,不参与实际的数据传输。 - 绑定端口与地址:
bind函数将监听套接字绑定到指定端口(9909)和INADDR_ANY(表示监听本机所有网卡的IP地址),让客户端能通过这个端口定位到服务端。 - 进入监听状态:
listen(socketfd, 1)调用后,服务端套接字进入被动监听模式,此时程序会阻塞在这里——因为没有客户端连接请求,listen本身不处理连接,只是让套接字处于可接受连接的状态,第二个参数1表示等待连接的队列最大长度。 - 接受连接并创建通信套接字:当客户端发起连接并完成三次握手后,
accept会从等待队列中取出该连接,返回一个新的套接字(clientSocketfd),这个新套接字才是服务端与客户端进行数据传输的通道,原监听套接字会继续等待其他连接请求。
二、客户端连接流程
- Winsock环境初始化:和服务端一样,先初始化套接字库。
- 创建客户端套接字:创建同类型的流式套接字,用于发起连接和后续数据传输。
- 配置服务端地址:指定服务端的IP(127.0.0.1,本地回环地址,即本机)和端口(9909),明确连接目标。
- 发起连接请求:
connect函数触发TCP三次握手:客户端向服务端发送SYN报文,服务端回复SYN+ACK报文,客户端再回复ACK报文。三次握手完成后,服务端的accept函数解除阻塞,连接正式建立。
三、对应代码实现
服务端代码(Server.c)
#define _WINSOCK_DEPRECATED_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #pragma comment(lib,"Ws2_32.lib") #include <stdio.h> #include <stdlib.h> #include <string.h> #include<sys/types.h> #include<sys/stat.h> #include <sys/types.h> #include <winsock2.h> #include <fcntl.h> #include <io.h> #include <Ws2tcpip.h> #include <InetReg.h> #define PORT 9909 struct sockaddr_in sockAddress; struct sockaddr_in clientAddress; int main() { int socketfd ; struct addrinfo hints; WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) printf("Failed intialize wsa\n"); else printf("Success intialize wsa\n"); //Create socket socketfd = socket(AF_INET,SOCK_STREAM,0); if (socketfd == -1) printf("fails socket creation"); else printf("Succness socket creation"); //intailize address of sockaddress memset(&sockAddress, 0, sizeof(sockAddress)); sockAddress.sin_family = AF_INET; sockAddress.sin_port = htons(PORT); sockAddress.sin_addr.s_addr = htonl(INADDR_ANY); //Bind the socket to local port int bindfd = bind(socketfd, (struct sockaddr*)&sockAddress,sizeof(sockAddress)); if (bindfd == -1) printf("Fail Binding socket to local port\n"); else printf("Success Binding socket to local port\n"); //listen the request from client int listenfd = listen(socketfd, 1); //return 0 means success if (listenfd != 0) printf("Fail listening\n"); else printf("start listening to local port\n"); //accepting memset(&clientAddress, 0, sizeof(clientAddress)); int clientAddressLength=sizeof(clientAddress); int clientSocketfd = accept(socketfd,(struct sockaddr*)&clientAddress,&clientAddressLength); if (clientSocketfd == -1) printf("failed to accept"); else printf("Accepted..."); //Writing char msg[100] = "Hello my name is ahmed server"; unsigned int i = sizeof(msg); //_write(socketfd,msg,i); send(clientSocketfd, msg, sizeof(msg), true); //printf("sent: %s\n", msg); printf("\n\n\nServer address: %d\n\n\n", sockAddress.sin_addr.s_addr); printf("ServerAddress: %d\n", clientSocketfd); //close closesocket(clientSocketfd); closesocket(socketfd); system("pause"); return 0; }
客户端代码(Client.c)
#define _CRT_SECURE_NO_DEPRECATE #define _WINSOCK_DEPRECATED_NO_WARNINGS #pragma comment(lib,"Ws2_32.lib") #include <stdio.h> #include <winsock2.h> #define PORT 9909 struct sockaddr_in SocketAddress; int main() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) printf("Failed intialize wsa\n"); else printf("Success intialize wsa\n"); //intialize socket int SocketFd = socket(AF_INET,SOCK_STREAM,0); if (SocketFd == -1) printf("Failed intialize socket\n"); else printf("Success intialize socket\n"); //intialize socket address memset(&SocketAddress,0,sizeof(SocketAddress)); SocketAddress.sin_family = AF_INET; SocketAddress.sin_port = htons(PORT); SocketAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); //connectivity int c = connect(SocketFd,(struct sockaddr*)&SocketAddress,sizeof(SocketAddress)); printf("client: %d", SocketFd); printf("\n\n\nClient address: %d\n\n\n", SocketAddress.sin_addr.s_addr); return 0; }
关键疑问解答
- 服务端在
listen()处阻塞:listen仅将套接字切换到监听状态,此时没有客户端连接请求,程序会停在这里等待连接事件触发。 - 客户端调用
connect()后服务端进入accept():connect触发TCP三次握手,握手完成后,操作系统会通知服务端的监听套接字有新连接,accept函数随即解除阻塞,创建用于通信的新套接字,服务端后续即可通过该套接字与客户端交互(比如代码中的send操作)。
内容的提问来源于stack exchange,提问作者Ahmed gamal seliman
相关产品推荐
相关产品推荐

