在Linux系统上实现TCP(socket)通信

一.什么TCP

TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

二.TCP****通信流程

三. TCP****服务器端

复制代码
1 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM tcp通信

2 绑定(bind)
struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(8888);
myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 自动提取本机ip地址
bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr));

3 监听 (设置允许同时连接的客户端的最大值 同时连接:已经连上的,不算同时连接)
int listen(int sockfd, int backlog);
listen(sockfd, 5);

4 阻塞等待连接 accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd :socket 的返回值
addr :客户端的ip地址和端口号存在addr中 (通常为NULL)//udp recvfrom倒数第二个参数一
样
addrlen :客户端的ip地址和端口号长度 (通常为NULL)//udp recvfrom倒数第一个参数一样
返回值(重点)
是一个newfd :(一个新的fd,此fd用来标识客户端,第一个连接的 4,下一个5,....)

5 接收数据 (阻塞接收)
ssize_t recv(int newfd, void *buf, size_t len, int flags);
参数: newfd accept的返回值, newfd
buf 接收的数据存放的位置
len 将要接收的数据的长度
flags 暂时为0
返回值: 实际接收的数据的长度,如果<=0,则证明客户端已经断开连接

6 发送数据
ssize_t send(int newfd, const void *buf, size_t len, int flags);
参数: newfd accept的返回值, newfd
buf 发送数据首地址
len 发送数据长度
flags 暂时为0
返回值: 实际发送的数据的长度

7 关闭socket
close(newfd);
close(fd);

实例 :
server.c

复制代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(8888);
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 得到当前计算机的ip地址
    int ret = bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr));
    printf("ret %d\n",ret);

    listen(fd, 5);

    int newfd = accept(fd, NULL, NULL); //等待客户端连接

    char buf[100] = { 0 };
    ret = recv(newfd, buf, sizeof(buf), 0); //newfd 代表连接的客户端
    printf("ret %d, newfd is %d, buf is %s\n",ret, newfd, buf);

    close(newfd);
    close(fd);
}

执行:gcc hello.c -o server ./server
另外起一个终端,执行:nc 127.0.0.1 8888 ( 模拟出一个客户端 ),在这里发送信息,服务器端就会收到信息。

四. TCP****客户端

复制代码
1 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM tcp通信 

2 连接服务端
struct sockaddr_in youaddr;
youaddr.sin_family = AF_INET;
youaddr.sin_port = htons(8888);
youaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd socket的返回值
addr 保存对方的ip地址和端口
addrlen sizeof(youaddr);
返回值:成功 返回0 失败返回 -1
一旦连接成功,服务端解除阻塞(accpet)

3 发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd socket的返回值
buf 发送的数据存放的位置,
len 发送的数据的长度(以字节为单位)
flags 暂时为0

4 接收数据 //同样用recv()

5 关闭socket
close(fd);

实例 :
client.c

复制代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in youaddr;
    youaddr.sin_family = AF_INET;
    youaddr.sin_port = htons(8888);
    youaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    connect(fd, (struct sockaddr *)&youaddr, sizeof(youaddr));

    char buf[] = "hello";
    send(fd, buf, sizeof(buf), 0);

    close(fd);
}

执行:gcc hello.c -o client ./client
另外起一个终端,执行:nc -l 8888(模拟服务器)
之后在执行./client的终端就可以给模拟的服务器发消息了。

五.练习

实现服务器端循环收数据打印,客户端从 main函数的 参数中提取 ip 地址和端口号 , 可以循环从键盘输入数据,发数据, 如果客户端输入 '0' ,则客户端退出。
server.c (服务器端)

复制代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(8888);
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 得到当前计算机的ip地址
    int ret = bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr));
    listen(fd, 5);
    int newfd = accept(fd, NULL, NULL); //等待客户端连接
    while(1)
    {
        char buf[100] = { 0 };
        if(recv(newfd, buf, sizeof(buf), 0) > 0) //newfd 代表连接的客户端
        printf("buf is %s\n", buf);
        else
        break;
    }
    close(fd);
}

client.c (客户端)

复制代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in youaddr;
    youaddr.sin_family = AF_INET;
    youaddr.sin_port = htons(atoi(argv[2]));
    youaddr.sin_addr.s_addr = inet_addr(argv[1]);
    connect(fd, (struct sockaddr *)&youaddr, sizeof(youaddr));
    char buf[100] = "hello";
    while(1)
    {
        gets(buf);
        if (buf[0] =='0')
        {
            break;
        }
        send(fd, buf, sizeof(buf), 0);
    }
    close(fd);
}

起两个终端一个执行服务器,一个执行客户端。

一个执行:gcc hello.c -o server ./server
另一个执行:gcc hello.c -o client ./client 192.168.133.5 8080
//这里需要输入你的本地IP地址和端口号
这样客户端就能发送消息给服务器,服务器能一直接收消息。(实测成功)

六. 结语

这就是TCP套接字在Linux上使用的方法与步骤,本次的代码分享到此结束,感谢大家观看,希望大家点点赞,点点关注,后续还会发Linux系统上的TCP并发服务器(服务器能同时连多个客户端),谢谢!

相关推荐
虹科网络安全1 小时前
解读CRA横向标准EN 40000系列:从法规文本到工程落地 | CRA合规之路(七)
网络·安全·web安全
huainingning2 小时前
华三防火墙web界面ipsec配置
网络
爱丶狸4 小时前
Grafana_Zabbix_ImageRenderer_部署与前端操作手册
linux·前端·zabbix·grafana·kylin
阿里云云原生5 小时前
可用性从 99.9% 跃升至 99.995%:畅捷通如何用 AI 重塑运维底座?
运维·网络·人工智能
k4m7v2pz6 小时前
把 Bevy 0.14 游戏移植到 R36S 掌机:一场与“无窗口系统“的搏斗
linux·rust·bevy·r36s·rk3326·开源掌机
kisloy6 小时前
【爬虫入门第13讲】Scrapy 框架深度解析(四)核心配置与自定义扩展
网络·爬虫·scrapy
木子欢儿7 小时前
Intel 架构的 MacBook Pro 运行 Linux 发热量高解决
linux·运维·服务器
夜雪一千7 小时前
TCP数据包长什么样?拆解TCP报文头部结构
网络·网络协议·tcp/ip
德迅云安全杨德俊8 小时前
应用加速:一站式解决企业跨域网络加速与安全双重难题
网络·安全·ddos
瓦学妹8 小时前
什么是网络索引?它是如何工作的?
大数据·网络·python·网络爬虫