网络编程——TCP编程

这篇具有很好参考价值的文章主要介绍了网络编程——TCP编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

流程

c语言tcp网络编程,嵌入式学习,网络,tcp/ip,服务器
在C语言中进行TCP编程的一般步骤如下:

(1)包含头文件:
在代码中包含必要的头文件,以便使用TCP编程所需的函数和数据类型。通常情况下,你需要包含 <sys/socket.h>、<netinet/in.h> 和 <arpa/inet.h>。

(2)创建套接字:
使用 socket() 函数创建一个套接字,该套接字将用于网络通信。套接字是一个整数值,它表示一个打开的文件描述符,用于在网络上发送和接收数据。

(3)设置地址和端口:
创建一个 struct sockaddr_in 结构体,并设置其中的成员变量,包括地址和端口号。这个结构体用于指定服务器的地址和端口。

(4)绑定套接字
使用 bind() 函数将套接字绑定到指定的地址和端口上。这将使服务器能够监听指定的端口并接受客户端的连接。

(5)监听连接:
使用 listen() 函数开始监听连接请求。这将使服务器进入被动等待状态,等待客户端连接。

(6)接受连接:
使用 accept() 函数接受客户端的连接请求。当有客户端连接到服务器时,accept() 函数将返回一个新的套接字,该套接字用于与客户端进行通信。

(7)通信:
使用 send() 和 recv() 函数发送和接收数据。服务器和客户端都可以使用这些函数来发送和接收数据。

(8)关闭连接:
在通信结束后,使用 close() 函数关闭套接字,释放资源。

这些步骤提供了一个基本的框架来进行TCP编程。你可以根据需要进行适当的修改和扩展。同时还需要处理错误和异常情况,并确保适当地释放资源,以避免内存泄漏和其他问题。

c语言tcp网络编程,嵌入式学习,网络,tcp/ip,服务器

服务器

(1)socket:创建一个用与链接的套接字(用于链接)

(2)bind:绑定自己的ip地址和端口

(3)listen:监听,将主动套接字转为被动套接字

(4)accept:阻塞等待客户端链接,链接成功返回一个用于通信套接字

(5)recv:接收消息

(6)send:发送消息

(7)close:关闭文件描述符

客户端

(1)socket:创建一个套接字

(2)填充结构体:填充服务器的ip和端口

(3)connect:阻塞等待链接服务器

(4)recv/send:接收/发送消息

(5)close:关闭文章来源地址https://www.toymoban.com/news/detail-754056.html

函数接口

1、socket

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
 功能:创建套接字文件  
 参数:
     domain:协议族 ,选择通信方式
        AF_UNIX, AF_LOCAL   本地通信
	AF_INET             IPv4 
	AF_INET6            IPv6  
    type:通信协议-套接字类型
	SOCK_STREAM   流式套接字
	SOCK_DGRAM    数据报套接字
	SOCK_RAW      原始套接字
    protocol:协议  填0,自动匹配底层TCP或UDP等协议。根据type匹配系统默认自动帮助匹配对应协议
     传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
     网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)

返回值:成功。返回同于链接的文件描述符
       失败 -1,更新errno

2、bind

```c
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
 功能:绑定套接字 - ip和端口
 功能:
   sockfd:套接字文件描述符
   addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信结构体由socket第一个参数确定)

  addrlen:结构体大小
返回值: 成功0
      失败:-1 更新errno

 通用结构体:  
struct sockaddr{
	sa_family_t sa_family;
	char        sa_data[14];
}

 ipv4的通信结构体:
struct sockaddr_in{
	sa_family_t    sin_family;/*AF_INET */
	in_port_t      sin_port;/* 端口 */
	struct in_addr sin_addr;/* ip地址 */
};
struct in_addr{
	uint32_t       s_addr;
};

本地通信结构体:
struct sockaddr_un{
	sa_family_t sun_family;/* AF_UNIX */
	char        sun_path[108];/* 套接字文件 */
};


3、listen

int listen(int sockfd,int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
 sockfd:套接字
 backlog:同时响应客户端请求链接的最大个数,不能写0.
  不同平台可同时链接的数不同,一般写6-8(队列1:保存正在连接)
(队列2,连接上的客户端)
   返回值:成功 0   失败-1,更新errno 

4、accept

int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen);
accept(sockfd,NULL,NULL);
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
则accept()函数返回,返回一个用于通信的套接字文件;
参数:
   Sockfd :套接字
   addr: 链接客户端的ip和端口号
      如果不需要关心具体是哪一个客户端,那么可以填NULL;
   addrlen:结构体的大小
     如果不需要关心具体是哪一个客户端,那么可以填NULL;
  返回值: 
     成功:文件描述符;//用于通信
失败:-1,更新errno
 //监听套接字,将主动套接字转为被动套接字
    if (listen(sockfd, 5) < 0)
    {
        perror("listern error.");
        return -1;
    }
    //4.阻塞等待客户端链接,链接成功返回一个用于通信的文件描述符
    acceptfd = accept(sockfd, NULL, NULL);
    if (acceptfd < 0)
    {
        perror("accept error.");
        return -1;
    }
    printf("acceptfd=%d\n", acceptfd);

5、recv

ssize_t recv(int sockfd,void*buf, size_t len,int flags);
功能: 接收数据 
参数: 
    sockfd: acceptfd ;
    buf  存放位置
    len  大小
    flags  一般填0,相当于read()函数
    MSG_DONTWAIT  非阻塞
返回值: 
	<0  失败出错  更新errno
	==0  表示客户端退出
	>0   成功接收的字节个数
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //创建套接字
    int sockfd;
    int acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);
    //填充ipv4的通信结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8888);
    serveraddr.sin_addr.s_addr = inet_addr("192.168.50.83");

    //绑定套接字
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind error.");
        return -1;
    }
    printf("bind ok.\n");
    //3.监听套接字,将主动套接字转为被动套接字
    if (listen(sockfd, 5) < 0)
    {
        perror("listern error.");
        return -1;
    }
    //4.阻塞等待客户端链接,链接成功返回一个用于通信的文件描述符
    acceptfd = accept(sockfd, NULL, NULL);
    if (acceptfd < 0)
    {
        perror("accept error.");
        return -1;
    }
    printf("acceptfd=%d\n", acceptfd);
    //5.循环接收消息
    char buf[128];
    int recvbyte;
    while (1)
    {
        recvbyte = recv(acceptfd, buf, sizeof(buf), 0);
        if (recvbyte < 0)
        {
            perror("recv error.");
            return -1;
        }
        else if (recvbyte == 0)
        {
            printf("client exit.\n");
            break;
        }
        else
        {
            buf[recvbyte] = '\0';
            printf("buf:%s\n", buf);
        }
    }
    close(acceptfd);
    close(sockfd);
    return 0;
}

6、send

ssize_t send(int sockfd,constvoid*buf, size_t len,int flags);
功能:发送数据
参数:
    sockfd:socket函数的返回值
    buf:发送内容存放的地址
    len:发送内存的长度
    flags:如果填0,相当于write();
返回值: 
<0  失败出错  更新errno
>0   成功发送的字节个数

7、connet

int connect(int sockfd,const struct sockaddr*addr,socklen_t addrlen);
功能:用于连接服务器;
参数:
     sockfd:socket函数的返回值
     addr:填充的结构体是服务器端的;
     addrlen:结构体的大小
返回值 
-1 失败,更新errno
      正确 0
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //创建套接字
    int sockfd;
    int acceptfd;
    char buf[128];
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);
    //填充ipv4的通信结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8888);
    serveraddr.sin_addr.s_addr = inet_addr("192.168.50.83");
   
    //连接到服务器端
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("connect err.");
        return -1;
    }
    printf("connect success.\n");
    ssize_t printbyte;
    while (1)
    {
        memset(buf, 0, sizeof(buf));                         
        printf("send:");
        scanf("%s", buf);
      
        if ((printbyte = send(sockfd, buf, sizeof(buf), 0)) <0)
        {
            perror("send err.");
        }
    }

    return 0;
}

实现双工通信

server.c

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

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("please input %s <port>\n", argv[0]);
        return -1;
    }
    int sockfd, acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    struct sockaddr_in serveraddr, caddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[1]));
    // serveraddr.sin_addr.s_addr=inet_addr(argv[1]);

    //自动获取ip
    //serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
    // serveraddr.sin_addr.s_addr=INADDR_ANY; //0.0.0.0
    serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    socklen_t len = sizeof(caddr);

    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }

    if (listen(sockfd, 5) < 0)
    {
        perror("listen err.");
        return -1;
    }
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err.");
            return -1;
        }
        printf("client:ip=%s  port=%d\n",
               inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

        char buf[128];
        pid_t pid = fork();
        if (pid < 0)
        {
            perror("fork err.");
            return -1;
        }
        else if (pid == 0) //发送
        {
            int sendbyte;
            while (1)
            {
                fgets(buf, sizeof(buf), stdin);
                if (buf[strlen(buf) - 1] == '\n')
                    buf[strlen(buf) - 1] = '\0';
                sendbyte = send(acceptfd, buf, sizeof(buf), 0);

                if (sendbyte < 0)
                {
                    perror("send error.");
                    return -1;
                }
            }
        }
        else
        {

            int recvbyte;
            while (1)
            {
                recvbyte = recv(acceptfd, buf, sizeof(buf), 0);
                if (recvbyte < 0)
                {
                    perror("recv err.");
                    return -1;
                }
                else if (recvbyte == 0)
                {
                    printf("client exit.\n");
                    kill(pid,SIGKILL);
                    wait(NULL);
                    break;
                }
                else
                {
                    printf("buf:%s\n", buf);
                }
            }
            close(acceptfd);
        }
    }
    close(sockfd);
    return 0;
}

celient.c

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


int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        printf("please input %s <ip> <port>\n", argv[0]);
        return -1;
    }
    //1.创建套接子
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);
    //填充ipv4的通信结构体  服务器的ip和端口
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    //2.请求链接服务器
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("connect error.");
        return -1;
    }

    //5.循环发送消息  通信
    char buf[128];
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.");
        return -1;
    }
    else if (pid == 0)
    {
        int recvbyte;
        while (1)
        {
            recvbyte = recv(sockfd, buf, sizeof(buf), 0);
            if (recvbyte < 0)
            {
                perror("recv err.");
                return -1;
            }
            printf("buf:%s\n", buf);
        }
    }
    else
    {
        int sendbyte;
        while (1)
        {
            fgets(buf, sizeof(buf), stdin);
            if (buf[strlen(buf) - 1] == '\n')
                buf[strlen(buf) - 1] = '\0';
            sendbyte = send(sockfd, buf, sizeof(buf), 0);
            if (sendbyte < 0)
            {
                perror("send error.");
                return -1;
            }
            if(strncmp(buf,"quit",4)==0)
              {
                  kill(pid,SIGKILL);
                   wait(NULL);
                  exit(-1);
              }
        }
    }
    close(sockfd);
    return 0;
}

优化代码

1.去掉fget获取多余的‘\n’
if(buf[strlen(buf)-1]=='\n')
         buf[strlen(buf)-1]='\0';
2.端口和ip地址通过命令行传参到代码中。
3.设置客户端退出,服务器结束循环接收。
    通过recv返回值为0判断客户端是否退出。
4.设置来电显示功能,获取到请求链接服务器的客户端的ip和端口。
5.设置服务器端自动获取自己的ip地址。
   INADDR_ANY  "0.0.0.0"
6.实现循环服务器,服务器不退出,当链接服务器的客户端退出,服务器等到下一个客户端链接。

到了这里,关于网络编程——TCP编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式学习-网络编程-Day5

    1.使用poll实现TCP服务器的并发 使用select实现TCP客户端的并发

    2024年01月20日
    浏览(62)
  • 【嵌入式Qt开发入门】Qt如何网络编程——获取本机的网络信息

            Qt 网络模块为我们提供了编写TCP/IP客户端和服务器的类。它提供了较低级别的类,例如代表低级网络概念的 QTcpSocket,QTcpServer 和 QUdpSocket,以及诸如 QNetworkRequest, QNetworkReply 和 QNetworkAccessManager 之类的高级类来执行使用通用协议的网络操作。它 还提供了诸如QNet

    2024年02月16日
    浏览(57)
  • 【嵌入式-网络编程】vmware中使用UDP广播失败问题

    问题描述: 自己在vmware中搭建了2台虚拟机,虚拟机A向虚拟机A和虚拟机B发送广播信息,接收端在虚拟机A和虚拟机B,这个时候,由于没配置 sin.sin_addr.s_addr = htonl(INADDR_ANY); ,而是配置的 inet_pton(AF_INET, SERV_IP, sin.sin_addr.s_addr); ,导致虚拟机A的广播信号发出去了,但是虚拟机B和

    2024年01月23日
    浏览(51)
  • lv8 嵌入式开发 网络编程开发 21 私有云盘项目

    目录 1云盘项目简介 2 项目实现 2.1 首先实现TCP客户端、服务端 2.2 实现客户端函数简化 2.3 实现服务端函数简化 2.4 TCP数据连包现象 2.5 封装send函数和recv函数 2.6 建立readme说明 2.7 实现文件传输 2.8 读取配置文件种的ip、端口号,通过argv[1]参数实现文件传输 3 最终项目  常见的

    2024年02月08日
    浏览(52)
  • 嵌入式学习第二十五天!(网络的概念、UDP编程)

        可以用来: 数据传输 、 数据共享     1. OSI协议模型: 应用层 实际收发的数据 表示层 发送的数据是否加密 会话层 是否建立会话连接 传输层 数据传输的方式(数据包,流式) 网络层 数据的路由(如何从一个局域网到达另一个局域网) 数据链路层 局域网下如何通信

    2024年03月17日
    浏览(62)
  • [嵌入式系统-25]:RT-Thread -12- 内核组件编程接口 - 网络组件 - HTTP编程

    目录 一、HTTP编程概述 1.1 概述 1.2 HTTP 服务器和 HTTP 客户端 二、HTTP Client 2.1 如何配置HTTP Client 2.2 HTTP Client代码实例1:socket发送http报文 2.3 HTTP Client代码实例2:httpc_xx接口收发HTTP报文 2.3.1 接口函数描述 2.3.2 代码实例:httpc_get 2.3.3 代码实例:httpc_post 2.3.4 代码实例:httpc

    2024年02月19日
    浏览(50)
  • lv7 嵌入式开发-网络编程开发 13 UNIX域套接字

    目录 1 UNIX 域流式套接字 2 UNIX 域数据报套接字 UNIX 域流式套接字(UNIX domain stream socket)是一种在同一台主机上的进程之间进行通信的机制。它不依赖于网络协议栈,而是使用文件系统作为通信的基础。 UNIX 域流式套接字提供可靠的、双向的、面向连接的通信方式。与传统的

    2024年02月07日
    浏览(49)
  • 嵌入式培训机构四个月实训课程笔记(完整版)-Linux网络编程第三天-UDP编程(物联技术666)

    技术咨询:wulianjishu666 上午:UDP网络编程  下午:UDP聊天程序的设计、select超时控制 教学内容: 1、udp和tcp都是在传输层上的协议,它们的区别 UDP协议与TCP协议的差异: TCP:面向连接,可靠 UDP:无连接,不可靠 ----------------------- UDP协议的优势: 支持广播和多播 UDP没有连接

    2024年01月16日
    浏览(99)
  • 嵌入式培训机构四个月实训课程笔记(完整版)-Linux网络编程第一天-socket编程练习(物联技术666)

    点赞+关注,功德无量。更多配套资料,欢迎私信。 网盘链接:https://pan.baidu.com/s/1NIrDmbm8EtFkB1G8s7E3Sg?pwd=qsoh 提取码:qsoh 1、建立一个服务器和一个客户端,二个之间通信 //--------------------服务器 #include stdio.h #include stdlib.h #include string.h                                    

    2024年02月01日
    浏览(55)
  • 嵌入式培训机构四个月实训课程笔记(完整版)-Linux网络编程第三天-UDP编程练习题(物联技术666)

    网盘链接:https://pan.baidu.com/s/1TKdHdeuDI8XPaakepvSLZQ?pwd=1688  提取码:1688 利用UDP实现双人不同机器聊天(服务器可以被多人连接,显示多人聊天记录) //-------------------------服务器 #include string.h #include sys/types.h #include sys/socket.h #include unistd.h #include netinet/in.h #include stdio.h #include stdlib

    2024年02月02日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包