其他分享
首页 > 其他分享> > TCP - C语言实现详解

TCP - C语言实现详解

作者:互联网

原文网址:https://www.jianshu.com/p/7c7fe00d01b1

TCP - C语言实现详解

chiwin 2020.06.09 17:17:46字数 616阅读 589   tcp-三次握手-四次挥手.jpg

【一】服务端

1、创建一个socket,拿到tcp的一个文件描述符

int serverSocket = socket(AF_INET, SOCK_STREAM, 0)

其中,第一个参数表示地址类型,AF_INET为IPV4,AF_INET6可支持IPV6;第二个参数表明是TCP【面向连接的稳定数据传输SOCK_STREAM】连接;第三个参数默认0

2、申请一个服务端结构体并初始化

struct sockaddr_in server_addr; // 结构体
bzero(&server_addr, sizeof(server_addr)); // 判空

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
//server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 本机通信,INADDR_ANY代表0.0.0.0:所有地址
server_addr.sin_addr.s_addr = inet_addr("186.66.66.66"); //非本机通信

申请了结构体sockaddr_in,将协议族设置为ipv4,传入了port,设置了ip

3、绑定socket和结构体ipport

bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)

返回小于0为失败,否则绑定成功
注:
sockaddr在头文件#include <sys/socket.h>中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了
sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中

4、监听socket句柄

listen(serverSocket, 5)

返回小于0为失败,否则监听成功

5、为客户端连接做准备,构造结构体

struct sockaddr_in client_addr;
int addr_len = sizeof(client_addr);

6、等待客户端连接

int clientSocket = accept(serverSocket, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len);

返回小于0为失败,否则接受客户端连接
注:
accept接收服务端的链接请求,并返回一个客户端的socket句柄,参数一为服务端socket句柄,参数二为客户端结构体,参数三位客户端结构体长度,其中参数二和参数三为出参,可以解析到客户端的信息,如下:

printf("Client IP %s\n", inet_ntoa(client_addr.sin_addr));
printf("Client Port %d\n", htons(client_addr.sin_port));

7、客户端连接后,接受msg消息

char buffer[200];
int dataLen = recv(clientSocket , buffer, 1024, 0);
buffer[dataLen] = '\0';

dateLen为接受到的data的大小,buffer为收到的消息

8、服务端收完消息后,回给客户端相同的消息【或者自己写消息到buffer里并发送】

send(clientSocket , buffer, dataLen , 0);

9、继续监听客户端连接请求

使用while循环返回到accept函数

【二】客户端

1、创建一个socket,拿到tcp的一个文件描述符

int clientSocket = socket(AF_INET, SOCK_STREAM, 0);

2、申请一个服务端结构体并填充服务端信息

struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVER_PORT);
//指定服务器端的ip,本地测试:127.0.0.1
//inet_addr()函数,将点分十进制IP转换成网络字节序IP
serverAddr.sin_addr.s_addr = inet_addr("186.66.66.66");

3、尝试连接服务端

connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr))

返回小于0为失败,否则连接成功

4、开始发送数据并在发送成功后接收服务端返回的数据

char sendbuf[200] = {"sendMsg to server"};
char recvbuf[200] = {0};
send(clientSocket, sendbuf, strlen(sendbuf), 0);
int recvData = recv(clientSocket, recvbuf, 200, 0);
recvbuf[recvData] = '\0';

5、完成数据通信后关闭连接

close(clientSocket);

【三】代码

参考:https://blog.csdn.net/lovekun1989/article/details/41042273

服务端代码如下:

/*socket tcp服务器端*/
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
 
#define SERVER_PORT 12345
 
/*
 监听后,一直处于accept阻塞状态,
 直到有客户端连接,
 当客户端输入quit后,客户端主动close与服务端的链接
 */
 
int main()
{
    int serverSocket;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int addr_len = sizeof(client_addr);
    int clientSocket ;
    char buffer[200];
    int dataLen;
    
    if((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return 1;
    }
 
    bzero(&server_addr, sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);

    //本机通信
    //server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //非本机通信
    server_addr.sin_addr.s_addr = inet_addr("186.66.66.66");
  
  
    //对于bind,accept之类的函数,里面套接字参数都是需要强制转换成(struct sockaddr *)
    if(bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        perror("connect");
        return 1;
    }
    //设置服务器上的socket为监听状态
    if(listen(serverSocket, 5) < 0) 
    {
        perror("listen");
        return 1;
    }
 
    while(1)
    {
        printf("Listening on port: %d\n", SERVER_PORT);
        //调用accept函数后,会进入阻塞状态
        //accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符,
        //serverSocket和clientSocket 。
        //serverSocket仍然继续在监听状态,clientSocket 则负责接收和发送数据
        //client_addr是一个传出参数,accept返回时,传出客户端的地址和端口号
        //addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的client_addr的长度,以避免缓冲区溢出。
        //传出的是客户端地址结构体的实际长度。
        //出错返回-1
        clientSocket  = accept(serverSocket, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len);
        if(clientSocket  < 0)
        {
            perror("accept");
            continue;
        }
        printf("\nrecv clientSocket  data...n");
        //inet_ntoa   ip地址转换函数,将网络字节序IP转换为点分十进制IP
        //表达式:char *inet_ntoa (struct in_addr);
        printf("IP is %s\n", inet_ntoa(client_addr.sin_addr));
        printf("Port is %d\n", htons(client_addr.sin_port));
        while(1)
        {
            dataLen = recv(clientSocket , buffer, 1024, 0);
            if(dataLen < 0)
            {
                perror("recv");
                continue;
            }
            buffer[dataLen] = '\0';
            if(strcmp(buffer, "quit") == 0)
                break;
            printf("%drecv data is %s\n", dataLen, buffer);
            send(clientSocket , buffer, dataLen, 0);
        }
    }
    return 0;
}

客户端代码如下:

/*socket tcp客户端*/
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
 
#define SERVER_PORT 12345
 
/*
 连接到服务器后,会不停循环,等待输入,
 输入quit后,断开与服务器的连接
 */
 
int main()
{
    //客户端只需要一个套接字文件描述符,用于和服务器通信
    int clientSocket;
    //描述服务器的socket
    struct sockaddr_in serverAddr;
    char sendbuf[200];
    char recvbuf[200];
    int recvData;
    if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return 1;
    }
 
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    //指定服务器端的ip,本地测试:127.0.0.1
    //inet_addr()函数,将点分十进制IP转换成网络字节序IP
    serverAddr.sin_addr.s_addr = inet_addr("186.66.66.66");
    if(connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
    {
        perror("connect");
        return 1;
    }
 
    printf("connect with destination host...\n");
 
    while(1)
    {
        printf("Input your world:>");
        scanf("%s", sendbuf);
        printf("\n");
 
        send(clientSocket, sendbuf, strlen(sendbuf), 0);
        if(strcmp(sendbuf, "quit") == 0)
            break;
        recvData = recv(clientSocket, recvbuf, 200, 0);
        recvbuf[recvData] = '\0';
        printf("recv data of my world is: %s\n", recvbuf);
    }
    close(clientSocket);
    return 0;
}

标签:sockaddr,addr,TCP,C语言,详解,sin,include,server,clientSocket
来源: https://www.cnblogs.com/bruce1992/p/16560262.html