C 网络编程

一、套接字

套接字:源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。

常用的TCP/IP协议的3种套接字类型如下所示:

  • 流套接字(SOCK_STREAM):
    流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP(The Transmission ControlProtocol)协议。
  • 数据报套接字(SOCK_DGRAM):
    数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP(User Datagram Protocol)协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。
  • 原始套接字(SOCK_RAW):
    原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW。
二、相关函数、结构体

2.1 套接字函数

套接字函数描述
Linux:
        int socket(int af, int type, int protocol);
Windows:
        SOCKET socket(int af, int type, int protocol);
创建套接字
Linux:
        int bind(int sock, const struct sockaddr *myaddr, socklen_t addrlen);
Windows:
        int bind(SOCKET sock, const struct sockaddr *addr, int addrlen);
绑定套接字
Linux:
        int listen(int sockfd, int backlog);
Windows:
        int listen(SOCKET sock, int backlog);
进入监听状态
Linux:
        int accept(int sockfd, struct sockaddr *client_addr, socklen_t *len);
Windows:
        SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
等待客户端连接
Linux:
        int connect(int sock_fd, struct sockaddr *serv_addr,int addrlen);
Windows:
        int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);
客户端连接服务器

2.2 接受、发送函数

函数描述
Linux:
        ssize_t write(int fd, const void *buf, size_t nbytes);
Windows:
        int send(SOCKET sock, const char *buf, int len, int flags);
发送数据
Linux:
        ssize_t read(int fd, void *buf, size_t nbytes);
Windows:
        int recv(SOCKET sock, char *buf, int len, int flags);
读取数据

2.3 相关结构体

结构体描述
Linux:
        struct sockaddr_in
Windows:
        SOCKADDR_IN
设置IP地址,端口等信息
Linux:
        struct sockaddr
Windows:
        SOCKET
设置IP地址,端口等信息

2.4 IP、端口函数

函数描述
*int inet_pton(int family, const char *strptr, void *addrptr);将IP地址在“点分十进制”和“二进制整数”之间转换。而且,能够处理ipv4和ipv6
*const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);将数值格式转化为点分十进制的ip地址格式。而且,能够处理ipv4和ipv6
u_short htons(u_short hostshort);将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian)
ntohs():network to host short,将short类型数据从网络字节序转换为主机字节序。
htonl():host to network long,将long类型数据从主机字节序转换为网络字节序。
ntohl():network to host long,将long类型数据从网络字节序转换为主机字节序。
三、Windows示列

服务端代码

#include <stdio.h>
#include <WinSock2.h>
#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.lib
#define BUF_SIZE 100

int main() {
    //
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2), &wsaData);

    // 创建套接字
    SOCKET serverSock = socket(AF_INET, SOCK_STREAM, 0);

    // 绑定套接字
    SOCKADDR_IN sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = PF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockAddr.sin_port = htons(9080);
    bind(serverSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));

    // 进入监听状态
    listen(serverSock, 20);

    // 接受客户端请求
    SOCKADDR cltAddr;
    int nSize = sizeof(cltAddr);
    char buffer[BUF_SIZE] = {0};
    while(1) {
        // 阻塞直到客户端发来消息
        SOCKET clientSock = accept(serverSock, (SOCKADDR*)&cltAddr, &nSize);

        // 接受客户端消息
        int strLen = recv(clientSock, buffer, BUF_SIZE, 0);
        printf("Message form client: %s\n", buffer);

        // 给客户端回复消息
        printf("Input a string: \n");
        gets_s(buffer, BUF_SIZE);
        send(clientSock, buffer, strLen, 0);

        // 关闭套接字
        closesocket(clientSock);
        // 重置缓冲区
        memset(buffer, 0, BUF_SIZE);
    }

    // 关闭套接字
    closesocket(serverSock);
    // 终止DLL使用
    WSACleanup();
}

客户端代码

#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.lib

#define BUF_SIZE 100

int main(){
    //初始化DLL
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    // 创建套接字
    SOCKADDR_IN sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = PF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockAddr.sin_port = htons(9080);

    char bufSend[BUF_SIZE] = {0};
    char bufRecv[BUF_SIZE] = {0};

    while (1) {
        // 创建套接字
        SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        // 连接服务器
        connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));

        // 获取用户输入的字符串并发送给服务器
        printf("Input a string:");
        gets_s(bufSend, sizeof(bufSend));
        send(sock, bufSend, BUF_SIZE, 0);

        // 接受服务器传回来的消息
        recv(sock, bufRecv, BUF_SIZE, 0);
        printf("Message form server: %s\n", bufRecv);

        // 重置缓冲区
        memset(bufSend, 0, BUF_SIZE);
        memset(bufRecv, 0, BUF_SIZE);

        // 关闭套接字
        closesocket(sock);
    }

    // 终止DLL使用
    WSACleanup();

    return 0;
}
四、Linux示列

服务端

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>


#define BUF_SIZE 100

int main() {
    // 创建套接字
    int  serverSock = socket(AF_INET, SOCK_STREAM, 0);

    // 绑定套接字
    struct sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = PF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockAddr.sin_port = htons(9080);
    bind(serverSock, (struct sockaddr*)&sockAddr, sizeof(sockAddr));

    // 进入监听状态
    listen(serverSock, 20);

    // 接受客户端请求
    struct sockaddr cltAddr;
    socklen_t nSize = sizeof(cltAddr);
    char buffer[BUF_SIZE] = {0};
    while(1) {
        // 阻塞直到客户端发来消息
        int clientSock = accept(serverSock, (struct sockaddr*)&cltAddr, &nSize);

        // 接受客户端消息
        int strLen = read(clientSock, buffer, BUF_SIZE);
        printf("Message form client: %s\n", buffer);

        // 给客户端回复消息
        printf("Input a string: \n");
        fgets(buffer, sizeof(buffer), stdin);
        write(clientSock, buffer, strLen);

        // 关闭套接字
        close(clientSock);
        // 重置缓冲区
        memset(buffer, 0, BUF_SIZE);
    }

    // 关闭套接字
    close(serverSock);
}

客户端

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BUF_SIZE 100

int main(){
    // 创建套接字
    struct sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockAddr.sin_port = htons(9080);

    char bufSend[BUF_SIZE] = {0};
    char bufRecv[BUF_SIZE] = {0};

    while (1) {
        // 创建套接字
        int sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        // 连接服务器
        connect(sock, (struct sockaddr*)&sockAddr, sizeof(sockAddr));

        // 获取用户输入的字符串并发送给服务器
        printf("Input a string:");
        fgets(bufSend, sizeof(bufSend), stdin);
        send(sock, bufSend, BUF_SIZE, 0);

        // 接受服务器传回来的消息
        recv(sock, bufRecv, BUF_SIZE, 0);
        printf("Message form server: %s\n", bufRecv);

        // 重置缓冲区
        memset(bufSend, 0, BUF_SIZE);
        memset(bufRecv, 0, BUF_SIZE);

        // 关闭套接字
        close(sock);
    }

    return 0;
}
  • 11
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书香水墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值