首页 > 编程语言 >socket 编程

socket 编程

时间:2023-09-10 18:11:38浏览次数:36  
标签:serv sockaddr socket int 编程 sock 接字 addr

采用 socket 实现服务端和客户端通信

server.c

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

#define BUF_SIZE 1024
#define MAX_PENDING 20

int main() {
  int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (serv_sock == -1) {
    perror("socket() error");
    exit(EXIT_FAILURE);
  }

  struct sockaddr_in serv_addr;
  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  serv_addr.sin_port = htons(1234);

  if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
    perror("bind() error");
    close(serv_sock);
    exit(EXIT_FAILURE);
  }

  if (listen(serv_sock, MAX_PENDING) == -1) {
    perror("listen() error");
    close(serv_sock);
    exit(EXIT_FAILURE);
  }

  char buffer1[BUF_SIZE];
  char buffer2[BUF_SIZE];

  while (1) {
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);
    int clnt_sock =
        accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    if (clnt_sock == -1) {
      perror("accept() error");
      continue;
    }

    memset(buffer2, 0, BUF_SIZE);
    printf("Enter a message: ");
    fgets(buffer2, BUF_SIZE, stdin);
    write(clnt_sock, buffer2, strlen(buffer2));

    memset(buffer1, 0, BUF_SIZE);
    read(clnt_sock, buffer1, BUF_SIZE);
    printf("Client: %s\n", buffer1);

    close(clnt_sock);
  }

  close(serv_sock);
  return 0;
}

client.c

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

#define BUF_SIZE 1024

int main() {
  struct sockaddr_in serv_addr;
  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  serv_addr.sin_port = htons(1234);

  char buffer1[BUF_SIZE];
  char buffer2[BUF_SIZE];

  while (1) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
      perror("socket() error");
      continue;
    }

    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
      perror("connect() error");
      close(sock);
      continue;
    }

    memset(buffer1, 0, BUF_SIZE);
    read(sock, buffer1, BUF_SIZE);
    printf("Server: %s\n", buffer1);

    printf("Enter a message: ");
    memset(buffer2, 0, BUF_SIZE);
    fgets(buffer2, BUF_SIZE, stdin);
    write(sock, buffer2, strlen(buffer2));

    close(sock);
  }

  return 0;
}

这两个程序中的知识点:

socket 函数

函数原型:int socket(int af, int type, int protocol);

(1) af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET (IPv4)和 AF_INET6(IPv6)

也可以使用 PF 前缀,PF 是“Protocol Family”的简写,它和 AF 是一样的。例如,PF_INET 等价于 AF_INET,PF_INET6 等价于 AF_INET6

(2) type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)

(3) protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议

一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。如果只有一种情况,protocol 可以用 0 来代替,例如:

int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);  //创建UDP套接字

perror函数

perror讲解

EXIT_FAILURE

定义EXIT_FAILURE 是一个宏,定义在头文件 stdlib.h 中。

用途:它通常用于 exit() 函数的参数,表示程序因某种失败条件而终止。

:虽然其具体的值可能因实现而异,但通常它的值为 1。这与成功退出的常量 EXIT_SUCCESS(通常值为 0)形成对比。

sockaddr_in

sockaddr_in 定义:

// 在 <netinet/in.h> 定义
struct sockaddr_in {
    short            sin_family;   // 地址族(Address family),对于IPv4,其值为AF_INET
    unsigned short   sin_port;     // 端口号,使用网络字节序,一般在 1024 - 65536 中选择
    struct in_addr   sin_addr;     // IPv4地址结构体
    char             sin_zero[8];  // 用于将此结构体的大小与`sockaddr`对齐,通常设置为0
};

in_addr

// 在 <netinet/in.h> 定义
struct in_addr {
    uint32_t s_addr;  // IPv4地址,使用网络字节序
};

sockaddr

struct sockaddr{
    sa_family_t  sin_family;   //地址族(Address Family),也就是地址类型
    char         sa_data[14];  //IP地址和端口号
};

地址转化

使用 inet_addr()inet_pton() 函数将点分十进制的IPv4地址字符串转换为网络字节序的整数形式。

使用 inet_ntoa()inet_ntop() 函数将网络字节序的整数转换为点分十进制的IPv4地址字符串。

struct in_addr ip_addr;
ip_addr.s_addr = inet_addr("192.168.1.1");  // 将点分十进制字符串转换为网络字节序的整数

bind 函数

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);

socket() 函数用来创建套接字,确定套接字的各种属性,然后服务器端要用 bind() 函数将套接字与特定的 IP 地址和端口绑定起来,只有这样,流经该 IP 地址和端口的数据才能交给套接字处理。

为什么使用 sockaddr_in 而不使用 sockaddr:

sockaddr 和 sockaddr_in 的长度相同,都是16字节,只是将IP地址和端口号合并到一起,用一个成员 sa_data 表示。要想给 sa_data 赋值,必须同时指明IP地址和端口号,例如”127.0.0.1:80“,遗憾的是,没有相关函数将这个字符串转换成需要的形式,也就很难给 sockaddr 类型的变量赋值,所以使用 sockaddr_in 来代替。这两个结构体的长度相同,强制转换类型时不会丢失字节,也没有多余的字节。

connect 函数

connect 函数用来将 client 端和 server 端建立连接:

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);

listen 函数

int listen(int sock, int backlog);

通过 listen() 函数可以让套接字进入被动监听状态,所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。

当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。

缓冲区的长度(能存放多少个客户端请求)可以通过 listen() 函数的 backlog 参数指定,但究竟为多少并没有什么标准,可以根据你的需求来定,并发量小的话可以是10或者20。

如果将 backlog 的值设置为 SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。

accept 函数

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);

accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。

accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。

fgets 函数

fgets讲解

read/write 函数

ssize_t read(int fd, void *buf, size_t nbytes);

ssize_t write(int fd, const void *buf, size_t nbytes);

fd 为要读取的文件的描述符,buf 为要接收数据的缓冲区地址,nbytes 为要读取的数据的字节数。

read() 函数会从 fd 文件中读取 nbytes 个字节并保存到缓冲区 buf,成功则返回读取到的字节数(但遇到文件结尾则返回0),失败则返回 -1。

write() 函数会将缓冲区 buf 中的 nbytes 个字节写入文件 fd,成功则返回写入的字节数,失败则返回 -1。

标签:serv,sockaddr,socket,int,编程,sock,接字,addr
From: https://www.cnblogs.com/hacker-dvd/p/17691617.html

相关文章

  • 【腾讯云 Cloud Studio 实战训练营】 - 云IDE编程之旅化繁为简
    csdn首发文章链接一、前言:随着云计算产业的发展,各种基于云端的IDE相继出现。相比于传统的IDE,云端IDE可以更大程度的提升用户工作的效率。腾讯云与国内领先的一站式软件研发平台CODING联合推出一款完全基于云端的IDE:CloudStudio。作为一款在线云端开发工具,它可以帮助用户减......
  • JavaNote05-面向对象编程01
    0.面向对象内容的三条主线Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类面向对象的特征:封装、继承、多态、(抽象)其他关键字的使用:this、super、package、import、static、final、interface、abstract等1.面向对象编程概述面向对象是软件开发中的一类编程风......
  • 网络编程 1
    网络编程11.1软件架构c/s架构∶全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、美团app、360安全卫士等软件。B/s架构︰全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有IE、谷歌、火狐等。1.要想实现网络通信,需要解决的三个问题:问题1:如何......
  • Unix/Linux 系统编程学习记录
    Unix/Linux系统编程学习记录不定期更新学习笔记、心得与操作过程chapter1引言第一章学习笔记博客链接安装Linux操作系统,学习Linux基础chapter2编程背景第二章学习笔记博客链接课程来源:2021-2022-1信息安全系统设计与实现(上)的平行课程推荐好友20211120destiny......
  • 2023“钉耙编程”中国大学生算法设计超级联赛(5)
    1001Typhoon题意:给你台风的轨迹坐标以及避难所的坐标,台风的半径不可预测,求让每个避难所不安全的最小台风半径是多少。分析:枚举每个点到所有“线段”的距离取个min。代码:附上队友的代码(懒):#include<bits/stdc++.h>#include<math.h>#definerep(i,a,b)for(inti=a;i<......
  • 深入理解Java if判断:提升编程效率的关键步骤
    Java中的if判断语句是一种条件语句,用于根据指定条件执行不同的代码块。if语句通常由一个布尔表达式和一个或多个语句组成。如果布尔表达式的结果为true,则执行if语句后面的语句;否则,跳过if语句后面的语句。下面是一个if语句的示例:intx=10;if(x>5){System.out.println("x......
  • C++编程语言在线学习系统-计算机毕业设计源码+LW文档
    摘要随着互联网技术的推进,我国高等教育逐渐实现信息化。许多精品C++编程语言在线学习系统的开发建设大大提高了教职工的教学效率,也为培养更多的高素质人才提供了途径。但是C++编程语言在线学习系统的发展也存在交互性不强、资源更新缓慢、教学形式单一等问题。因此,笔者设想开发一......
  • Keil C51下载_Keil C51最新版下载「编程软件」新功能介绍
    KeilC51是美国KeilSoftware公司出品的51系列兼容单片机C语言软件开发系统,使用KeilC51编写生成的代码效率非常高,相比其它语言更容易理解些。本站免费提供KeilC51官方版软件,有需要的前来下载试试看吧。软件地址:看置顶贴软件特色-mdkcore–mdk核心mdkcore包含微控制器开发......
  • Linux环境编程-进程管理
    一、进程的基本概念1、进程与程序程序是存储在磁盘上的可执行文件,程序被加载到内存中开始运行称为进程,一个程序可以同时加载成多个进程,进程就是处于活动状态下的程序2、进程的分类进程根据功能不同一般分为三种类型:交互进程、批处理进程、守护进程交互进程:由一个shell终端......
  • Linux网络编程(TCP状态转换关系)
    (文章目录)前言本篇文章来讲解一下TCP的状态转换关系,学习这个状态转换关系对于我们深入了解网络编程是非常有必要的。一、TCP状态转换图二、TCP连接状态转换解析客户端状态转换:1.CLOSED->SYN-SENT:当客户端尝试与服务器建立连接时,客户端从CLOSED状态转换到SYN-SEND状态。......