ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C语言网络编程——TCP

2021-04-13 21:02:59  阅读:180  来源: 互联网

标签:sockaddr socket 函数 编程 TCP C语言 sockfd 接字 addr


1.1 套接字

C语言网络编程其实本质上也是多进程之间通过socket套接字进行通信,知识进程可能位于不同的服务器上,常用的TCP/IP协议有3种套接字类型,如下所示:

1.1.1 流套接字(SOCK_STREAM)

流套接字用于提供面向连接、可靠的数据传输服务,该服务保证数据能够实现无差错、无重复发送,并按照顺序接受。流套接字之所以能偶实现可靠的数据服务,原因在于使用了TCP传输控制协议。

1.1.2 数据报套接字(SOCK_DGRAM)

数据包套接字提供了一种无连接的服务,该服务不能保证数据传输的可靠性,数据有可能在传输过程中丢失或者出现数据重复,且无法保证顺序的接受数据。数据报套接字使用UDP进行数据传输。

1.1.3 原始套接字(SOCK_RAW)

原始套接字允许对较低层次的协议直接访问,常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为器可以自如控制Window下的多种协议,能够对网络地城的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。如:通过原始套接字接受发向本机的ICMP、IGMP,或者接受TCP/IP栈不能处理的IP包。

1.1.3 C语言套接字数据结构

套接字通常由三个参数构成:IP地址, 端口号、传输层协议。C语言进行套接字编程的时候,通常会使用sockaddr和sockaddr_in两种数据类型,用于保存套接字信息。

struct sockaddr
{
	// 地址族,2字节
	unsigned short sa_family;
	// 存放地址和端口
	char sa_data[14];

}

struct sockaddr_in
{
	// 地址族
	short int sin_family;
	// 端口号
	unsigned short int sin_port;
	// 地址
	struct in_addr sin_addr;
	// 8字节数组,全为0,该字节数组的作用是为了让两种数据结构大小相同而保留的空字节
	unsigned char sin_zero[8];
}

对于sockaddr,大部分的情况下知识用于bind、connect、recvform、sendto等函数的参数,指明地址信息,在一般编程中,并不对此结构体直接操作,而是用sockaddr_in代替。
两种数据结构中,地址族都占2个字节,常见的地址族AF_INET, AF_INET6, AF_LOCAL。这里要注意字节序的问题,建议使用以下函数来对端口和地址进行处理。

uint16_t htons(uint16_t bost16bit)
uint32_t htonl(uint32_t bost32bit)
uint16_t ntons(uint16_t net16bit)
uint32_t ntons(uint32_t net32bit)

1.2 基于TCP的网络编程

客户端和服务器的连接和三次握手发生在accept函数下,listen函数知识创建了socket的监听模式。
在这里插入图片描述
使用socket进行TCP通信时,经常使用的函数如下表所示。

函数 作用
socket 用于建立一个socket连接
bind 将socket与本机的一个端口绑定, 随后可以在该端口监听服务请求
connect 面向连接的客户程序使用connect函数来配置socket,并于远程服务器建立一个连接
listen 是socket处于被动监听模式, 并为该socket建立一个输入数据队列,将到达服务器请求保存在此队列中,直到程序处理他们
accept 让服务器接收客户端的连接请求
close 停止在该socket上的任何操作
send 数据发送函数
recv 数据接收函数

1.2.1 服务端实现

服务端程序流程如下:

  1. 使用socket()函数创建一个socket
  2. 使用bind()函数,绑定ip地址、端口等信息到socket上
  3. 使用listen()函数,设置允许的最大连接数
  4. 使用accept()函数,接收客户端上来的连接
  5. 使用send()和recv()函数或read()和write()函数,收发数据
  6. 使用close()函数关闭连接
    实现代码:
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MAX_SIZE 512
#define PORT 3332

int main(void)
{
    int sockfd;
    int sock_fd;
    int recvnum;
    int addrlen = sizeof(struct sockaddr);
    struct sockaddr_in my_addr;
    struct sockaddr addr;
    char buf[MAX_SIZE];
	// 填充服务器端的数据,用于套接字绑定
    bzero(&my_addr, sizeof(struct sockaddr_in));
    my_addr.sin_family = AF_INET; // 设置为IPV4
    my_addr.sin_port = htons(PORT); // 将端口号主机序转换为网络序
    my_addr.sin_addr.s_addr = inet_addr("192.168.192.128"); // ip设置为192.168.192.128
	// 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        printf("create socket error!\n");
        exit(1);
    }
    // 绑定套接字
    if (bind(sockfd, (struct sockaddr *)&my_addr, addrlen) < 0)
    {
        printf("bind error!\n");
        exit(1);
    }
	// 监听端口和ip,设置最大连接数为3
    if (listen(sockfd, 3) < 0)
    {
        printf("listen error!\n");
        exit(1);
    }
	// 建立服务器端和客户端连接
    sock_fd = accept(sockfd, &addr, &addrlen);
    // 建立连接后,产生新的套接字
    if (sock_fd < 0)
    {
        printf("accept error!\n");
        exit(1);
    }
	// 接收数据
    if ((recvnum = recv(sock_fd, (void *)buf, MAX_SIZE, 0)) < 0)
    {
        printf("recv error!\n");
        exit(1);
    }
    buf[recvnum] = '\0';
    printf("recv from client: %s\n", buf);
    memset(buf, 0, MAX_SIZE);
    // 关闭连接
    close(sockfd);
    close(sock_fd);
    return 0;
}

1.2.2 客户端实现

客户端程序流程如下:

  1. 使用socket()函数,创建一个socket
  2. 设置要连接的服务端ip地址和端口等属性
  3. 使用connect()函数,连接服务器端
  4. 使用send()和recv()函数或read()和write()函数,收发数据
  5. 使用close()函数关闭网络连接
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

#define MAX_SIZE 512
#define PORT 3332

int main()
{
    int sockfd;
    int addrlen = sizeof(struct sockaddr);
    char buf[MAX_SIZE];
    struct sockaddr_in serv_addr;
	// 填充服务器端数据
    bzero(&serv_addr, sizeof(struct sockaddr_in));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = ntohs(PORT);
    serv_addr.sin_addr.s_addr = inet_addr("192.168.192.128");
	// 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        printf("create socket error!\n");
        exit(1);
    }
	// 连接服务器端
    if (connect(sockfd, (struct sockaddr *)&serv_addr, addrlen) < 0)
    {
        printf("connect error!\n");
        exit(1);
    }
	// 发送数据到服务端
    memset(buf, 0, MAX_SIZE);
    printf("enter some text:");
    scanf("%s", buf);

    if (send(sockfd, (void *)buf, MAX_SIZE, 0) < 0)
    {
        printf("send error!\n");
        exit(1);
    }
	// 关闭连接
    close(sockfd);
    return 0;
}

标签:sockaddr,socket,函数,编程,TCP,C语言,sockfd,接字,addr
来源: https://www.cnblogs.com/qq760153699/p/14655241.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有