Linux C之Socket通信(TCP协议)

引言:Linux 中的一切都是文件,进程创建或打开文件,内核都会返回一个整数类型的文件描述符;socket 也是一个文件,所以创建socket,也会返回文件描述符。socket用于IPC通信。

在这里插入图片描述

一 . TCP实现基本流程(C/S架构)

server端
  • socket()
  • bind()
  • listen()
  • accept()
  • read() wirte()
  • close()

client端

  • socket()
  • connet()
  • read() wirte()
  • close()
---->>>>上述函数可以用Linux的man手册进行查询函数原型和所需头文件,man手册分为九个板块 。如socket函数,其属于系统调用函数,命令查询格式为:man 2 socket 。

在这里插入图片描述

socket函数查询结果:

在这里插入图片描述

二 . TCP建立连接过程

在这里插入图片描述

三 . server端分步编程:

1 .函数头文件:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
2 . 宏定义和变量声明:
#define PORT 8888      //绑定端口为8888
int oldfd,newfd,len;   //套接字和数据长度 
struct sockaddr_in server,client;   //server和client的结构体
3 . socket初始化:
void socket_init()
{
   server.sin_family = AF_INET;
   server.sin_port = htons(PORT);  //主机字节序转网络字节序(转化数据为短整型)
   server.sin_addr.s_addr = htonl(0);   ////主机字节序转网络字节序(转化数据为长整型)
    /********   AF_INET表示IPv4 地址,SOCK_STREAM表示流式套接字,protocol 的值设为 0 *****/
   oldfd = socket(AF_INET,SOCK_STREAM,0);  //创建套接字(用于监听客户端连接)
   len = sizeof(client);
   /************* 调用函数的返回值小于0,一般是调用函数失败 *************/
   if(oldfd < 0)  
   { 
      perror("socket:");  //打印错误函数
      exit(1);   //结束进程
   }
   if(bind(oldfd,(struct sockaddr *)&server,sizeof(server)) < 0)
   {
      perror("bind:");
      exit(1);
   }
   if(listen(oldfd,128) < 0)   //监听端口(接受客户端的请求)
   {
     perror("listen:");
     exit(1);
   }
   /************  未有客户端请求,该函数的状态为阻塞 , 
 -              完成TCP三次握手连接,该函数创建新的套接字,
 -              用于与客户端通信。
   ***************/
   newfd = accept(oldfd,(struct sockaddr *)&client,&len);   
   if(newfd < 0)
   {
      perror("accept:");
      exit(1);
   }
}
4 . server main函数:
int main()
{
   char recbuf[1024],sendbuf[1024];  //发送和接收数据的数组
   printf("this is server\n");
   socket_init();   //socket函数初始化
   while(1)
   {
     if(read(newfd,recbuf,sizeof(recbuf))<0)   //读取客户端发来的数据
     {
         perror("read:");
         exit(1); 
     }
     printf("%s\n",recbuf);
     strcpy(sendbuf,recbuf);    //接收什么数据就返回什么数据
     if(write(newfd,sendbuf,sizeof(sendbuf))<0)   //向客户端发送数据
     {
         perror("write:");
         exit(1);
     }
   }
   //关闭套接字
   close(oldfd);
   close(newfd);
   return 0;
}

四 . client端分步编程:

1 . 函数头文件:
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
2 . 宏定义和变量声明:
#define PORT 8888    //目标服务器端口
int socket_fd;       //与服务器通信的套接字
struct sockaddr_in server;    //server结构体
3 . socket初始化:
void socket_init()
{
  server.sin_family = AF_INET;  //AF_INET表示IPv4 地址
  server.sin_port = htons(PORT);  //主机字节序转网络字节序
  /**************  两个进程由于在本机进行通信,即服务器目标地址为回环地址  *******/
  server.sin_addr.s_addr = inet_addr("127.0.0.1");  
  /********   AF_INET表示IPv4 地址,SOCK_STREAM表示流式套接字,protocol 的值设为 0 *****/
  socket_fd = socket(AF_INET,SOCK_STREAM,0);  
  if(socket_fd < 0)
  {
     perror("socket:");
     exit(1);
  }
  /********   connet函数用来连接服务端(客户端的端口随机分配,也能用bind函数来指定端口)****/
  if(connect(socket_fd,(struct sockaddr *)&server,sizeof(server))<0)  //返回值为0,连接失败
  {
     perror("connect:");
     exit(1);
  }
}
4 . client端 main函数:
int main()
{
  char sendbuf[1024],recbuf[1024];
  printf("this is client\n");
  socket_init();
  while(1)
  {
    scanf("%s",sendbuf);
    if(write(socket_fd,sendbuf,sizeof(sendbuf))<0)
    {
       perror("write:");
       exit(1);
    }
    if(read(socket_fd,recbuf,sizeof(recbuf))<0)
    {
         perror("read:");
         exit(1);
    }
    printf("%s\n",recbuf);
  }
  close(socket_fd);  //关闭套接字
  return 0;
}
  • 最后使用gcc对源文件进行编译。
程序最终运行图:

在这里插入图片描述

发布了17 篇原创文章 · 获赞 30 · 访问量 1814

猜你喜欢

转载自blog.csdn.net/qq_37451250/article/details/104229160