You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言Socket编程中服务端与客户端通信机制问询

C语言Socket编程中服务端与客户端的通信原理解析

你使用的是TCP流式套接字(SOCK_STREAM),这类通信基于TCP三次握手建立可靠连接,下面结合你提供的代码,一步步拆解通信流程,解答你关于阻塞与连接触发的疑问:

一、服务端启动与等待流程

  1. Winsock环境初始化:Windows平台必须先调用WSAStartup加载套接字库,完成Socket环境的初始化,这是后续所有Socket操作的前提。
  2. 创建监听套接字:socket(AF_INET, SOCK_STREAM, 0)创建一个IPv4的流式套接字,这个套接字的唯一作用是监听客户端的连接请求,不参与实际的数据传输。
  3. 绑定端口与地址:bind函数将监听套接字绑定到指定端口(9909)和INADDR_ANY(表示监听本机所有网卡的IP地址),让客户端能通过这个端口定位到服务端。
  4. 进入监听状态:listen(socketfd, 1)调用后,服务端套接字进入被动监听模式,此时程序会阻塞在这里——因为没有客户端连接请求,listen本身不处理连接,只是让套接字处于可接受连接的状态,第二个参数1表示等待连接的队列最大长度。
  5. 接受连接并创建通信套接字:当客户端发起连接并完成三次握手后,accept会从等待队列中取出该连接,返回一个新的套接字(clientSocketfd),这个新套接字才是服务端与客户端进行数据传输的通道,原监听套接字会继续等待其他连接请求。

二、客户端连接流程

  1. Winsock环境初始化:和服务端一样,先初始化套接字库。
  2. 创建客户端套接字:创建同类型的流式套接字,用于发起连接和后续数据传输。
  3. 配置服务端地址:指定服务端的IP(127.0.0.1,本地回环地址,即本机)和端口(9909),明确连接目标。
  4. 发起连接请求: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 21:10:28