socket的概念

socket用于网络中一台计算机中的程序与其他计算机的程序之间需要交换数据。

socket也称作“套接字”,用于描述IP地址和端口,是一个通信链路的描述符。应用程序通常通过“套接字”向对端发出请求或者应答网络请求。

socket是连接运行在网络上的两个程序之间的通信端点。通信的两端都有socket,它是一个通道,数据在两个socket之间进行传输。socket把复杂的TCP/IP协议族隐藏在socket接口后面,对程序员来说,只要用好socket相关的函数,就可以完成数据通信。

套接字(socket)

TCP提供了流(stream)和数据报(datagram)两种通信机制,所以套接字也分为流套接字和数据报套接字,本章节讲流套接字,在实际开发中,数据报套接字的应用场景很少。

流套接字的类型是SOCK_STREAM,采用TCP/IP协议实现。它提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有出错后重新发送的机制。类似于打电话。

数据报套接的类型是SOCK_DGRAM,它不需要建立和维持一个连接,采用UDP/IP协议实现。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并不需要总是要建立和维持一个连接。类似于短信。

socket通信的过程

1)服务端程序将一个套接字绑定到一个指定的地址和端口,并通过此套接字等待和监听客户的连接请求。

2)客户程序向服务端程序绑定服务端的地址和端口发出连接请求。

3)服务端接受连接请求。并获得一个新的套接字。

4)服务端通过读、写新的套接字与客户端进行通信。

客户/服务端模式

在TCP/IP网络应用中,两个程序之间通信模式是客户/服务端模式(client/server),即客户端向服务端发出请求,服务端接受到请求后,提供相应的服务。

客户/服务端也叫作客户/服务器,各人习惯。

1、服务端

1)服务端要先启动,准备好一个通信通道,表示可以在某地址和端口上可以接收客户连接。

2)等待客户端的连接请求。

3)等待并接收客户端发过来的数据,处理该客户的数据,处理完成后,向客户端返回处理结果。

4)不断的重复第3)步,直到客户端断开连接。

5)关闭服务端

示例(book242.cpp)

2、客户端

1)打开一个通信通道,连接到服务端已准备好的端口。

2)向服务端发送数据,等待并接收服务端处理结果。

3)不断的重复第2)步,直到全部的数据被处理完。

4)关闭与服务器的连接。

示例(book241.cpp)

先启动服务端程序book242,服务端启动后,进入等待客户端连接状态,然后启动客户端。

服务端的输出如下:

客户端的输出如下:

注意事项

1、别去纠缠细节

在socket通信的客户端和服务器,出现了多种数据结构,调用了多个函数,涉及到很多方面的知识,对初学者来说,更重要的是了解socket通信的过程、每段代码的用途和函数调用的功能,不要去纠缠这些结构体和函数的参数,这些函数的参数虽然很多,但可以调整的非常少,别抄错就可以了。需要程序员注意的地方我会列出。

2、服务端程序绑定的地址

一般情况下,服务器有多个网卡,多个IP地址,socket通信可以指定用哪个地址来进行通信,对程序员来说有两种选择:1)任意ip地址;2)指定ip地址。

1)任意ip地址的代码

m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // 本主机的任意ip地址

2)指定ip地址的代码

m_servaddr.sin_addr.s_addr = inet_addr("192.168.149.129"); // 指定ip地址

在实际开发中,采用任意ip地址的做法比较多。

3、服务端程序绑定的通信端口

m_servaddr.sin_port = htons(5000);  // 通信端口

4、客户端程序指定服务端的ip地址

struct hostent* h; // ip地址信息的数据结构

if ( (h = gethostbyname("192.168.149.129")) == 0 )

{ perror("gethostbyname"); close(sockfd); return -1; }

5、客户端程序指定服务端的通信端口

servaddr.sin_port = htons(5000);

6、send函数

send函数用于把数据通过socket发送给对端。不论是客户端还是服务端,应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

包含头文件:

#include <sys/types.h>

#include <sys/socket.h>

函数声明:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

sockfd为已建立好连接的socket。

buf为需要发送的数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,内存中有什么就发送什么。

len需要发送的数据的长度,为buf中有效数据的长度。

flags填0, 其他数值意义不大。

函数返回已发送的字符数。出错时返回-1,错误信息errno被标记。

注意,就算是网络断开,或socket已被对端关闭,send函数不会立即报错,要过几秒才会报错。

如果send函数返回的错误(<=0),表示通信链路已不可用。

7、recv函数

recv函数用于接收对端socket发送过来的数据。

recv函数用于接收对端通过socket发送过来的数据。不论是客户端还是服务端,应用程序都用recv函数接收来自TCP连接的另一端发送过来数据。

包含头文件:

#include <sys/types.h>

#include <sys/socket.h>

函数声明:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd为已建立好连接的socket。

buf为用于接收数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,只要是一块内存就行了。

len需要接收数据的长度,不能超过buf的大小,否则内存溢出。

flags填0, 其他数值意义不大。

如果socket的对端没有发送数据,recv函数就会等待,如果对端发送了数据,函数返回接收到的字符数。出错时返回-1,错误信息errno被标记。如果socket被对端关闭,返回值为0。

如果recv函数返回的错误(<=0),表示通信链路已不可用。

8、服务端有两个socket

对服务端来说,有两个socket,一个是用于监听的socket,还有一个就是客户端连接成功后,由accept函数创建的用于与客户端收发报文的socket。

9、程序退出时先关闭socket

socket是系统资源,操作系统打开的socket数量是有限的,在程序退出之前必须关闭已打开的socket,就像关闭文件指针一样,就像delete已分配的内存一样,极其重要。

值得注意的是,关闭socket的代码不能只在main函数的最后,那是程序运行的理想状态,还应该在main函数的每个return之前关闭。

相关的库函数

1、socket函数

socket函数用于创建一个新的socket,也就是向系统申请一个socket资源。socket函数用户客户端和服务端。

函数声明:

int socket(int domain, int type, int protocol);

参数说明:

domain:协议域,又称协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

type:指定socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式socket(SOCK_STREAM)是一种面向连接的socket,针对于面向连接的TCP服务应用。数据报式socket(SOCK_DGRAM)是一种无连接的socket,对应于无连接的UDP服务应用。

protocol:指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

说了一大堆废话,第一个能数只能填AF_INET,第二个参数只能填SOCK_STREAM,第三个参数只能填0。

除非系统资料耗尽,socket函数一般不会返回失败。

2、gethostbyname函数

把ip地址或域名转换为hostent 结构体表达的地址。

函数声明:

struct hostent *gethostbyname(const char *name);

参数name,域名或者主机名,例如"192.168.1.3"、"www.google.com"等。

返回值:如果成功,返回一个hostent结构指针,失败返回NULL。

gethostbyname只用于客户端。

gethostbyname只是把字符串的ip地址转换为结构体的ip地址,只要地址格式没错,一般不会返回错误。函数失败不会设置errno的值。

3、connect函数

向服务器发起连接请求。

函数声明:

int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);

函数说明:connect函数用于将参数sockfd 的socket 连至参数serv_addr 指定的服务端,参数addrlen为sockaddr的结构长度。

返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中。

connect函数只用于客户端。

如果服务端的地址错了,或端口错了,或服务端没有启动,connect一定会失败。

4、bind函数

服务端把用于通信的地址和端口绑定到socket上。

函数声明:

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

参数sockfd,需要绑定的socket。

参数addr,存放了服务端用于通信的地址和端口。

参数addrlen表示addr结构体的大小。

如果绑定的地址错误,或端口已被占用,bind函数一定会报错,否则一般不会返回错误。

5、listen函数

listen函数把主动连接套接口变为被连接套接口,使得这个socket可以接受其它socket的连接请求,从而成为一个服务端的socket。

函数声明:

int listen(int sockfd, int backlog)
       返回:0──成功, -1──失败

参数sockfd是已经被bind过的套接字。socket函数返回的套接字是一个主动连接的套接字,在服务端的编程中,程序员希望这个套接字可以接受外来的连接请求,也就是被动等待客户端来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,程序员通过调用listen函数来完成这件事。

参数backlog,这个参数涉及到一些网络的细节,比较麻烦,填5、10都行,一般不超过30。

当调用listen之后,服务端的套接字就可以调用accept来接受客户端的连接请求。

listen函数一般不会返回错误。

6、accept函数

服务端接受客户端的连接。

函数声明:

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

参数sockfd是已经被listen过的套接字。

参数addr用于存放客户端的地址信息,用sockaddr结构体表达,如果不需要客户端的地址,可以填0。

参数addrlen用于存放addr参数的长度,如果addr为0,addrlen也填0。

accept函数等待客户端的连接,如果没有客户端连上来,它就一直等待,这种方式称之为阻塞。

accept等待到客户端的连接后,创建一个新的套接字,函数返回值就是这个新的套接字,服务端使用这个新的套接字和客户端进行报文的收发。

accept在等待的过程中,如果被中断或其它的原因,函数返回-1,表示失败,如果失败,可以重新accept。

7、函数小结

服务端函数调用的流程是:socket->bind->listen->accept->recv/send->close

客户端函数调用的流程是:socket->connect->send/recv->close

版权声明

C语言技术网原创文章,转载请说明文章的来源、作者和原文的链接。

来源:C语言技术网(www.freecplus.net)

作者:码农有道

如果这篇文章对您有帮助,请点赞支持,或在您的博客中转发我的文章,谢谢!!!

如果文章有错别字,或者内容有误,或其他的建议或意见,请您留言指正,非常感谢!!!

socket通信基础知识相关推荐

  1. 计算机信息通信基础知识,计算机基础-(数据通信基础知识)

    计算机基础(数据通信基础知识) 通信协议是计算机之间交换信息所使用的一种公共语言的规范和约定,因特网的通信协议包含100多个相互关联的协议,由于TCP和IP是其中两个最核心的关键协议,故把因特网协议簇 ...

  2. 2.1.2 数据通信基础知识

    2.1.2 数据通信基础知识

  3. Socket编程知识必学/SELECT 编程

    Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect.accept.recv或recvfrom这样的阻塞程序 ...

  4. 2.1.2数据通信基础知识

    文章目录 2.1.2数据通信基础知识 0 思维导图 1. 典型的数据通信模型 2 数据通信相关术语 3 三种通信方式 4 两种数据传输方式 2.1.2数据通信基础知识 0 思维导图 1. 典型的数据通 ...

  5. 【计算机网络复习 物理层】2.1.2 数据通信基础知识

    数据通信基础知识 一.典型的数据通信模型 二.数据通信相关术语 三.三种通信方式 四.两种数据传输方式 一.典型的数据通信模型 二.数据通信相关术语 通信的目的是传送消息. 数据:传送信息的实体,通常 ...

  6. 第二章 物理层 2,3 数据通信基础知识 [计算机网络笔记]

    第二章 物理层 2,3 数据通信基础知识 本笔记参考书目: 计算机网络(第8版)谢希仁 2021王道计算机网络视频公开课 本节重点: (了解即可) 通信方式:单工/半双工/全双工 传输方式:并行/串行 ...

  7. 计算机的数据通信的概念,计算机数据通信基础知识.ppt

    计算机数据通信基础知识 第2章 数据通信基础知识 2.1 数据通信的基本概念 2.2 数据的传输方式 2.3 数据交换 2.4 多路复用技术 2.5 差错控制技术 2.6 数据传输设备 2.7 实训 ...

  8. 相干光通信基础知识(一)

    相干光通信基础知识(一) 光通信的发展与现状 直接检测光通信系统 其他技术 综述 光通信系统中的信号劣化 损耗 吸收 散射 辐射 失真 线性失真 非线性失真(非线性效应) 受激散射 非线性折射 光通信 ...

  9. 量子通信基础知识简介(一)

    1.量子定义: 一个物理量如果存在最小的不可分割的基本单位,那么称该最小的单位为量子.量子是一个整体概念,它指的是研究对象的最基本单元.它最早是由德国物理学家 M·普朗克在 1900 年提出的. 2. ...

最新文章

  1. 破解phpstorm,webstorm ,pycharm ,intellij IDEA
  2. 宁波大红鹰学院计算机毕设,网络文化对价值观的影响调查报告马克思主义基本原理概论》课程调查实践报告大学毕业设计.doc...
  3. Linux下安装MongoDB单节点
  4. 微软超融合私有云测试28-SCDPM2016部署之SCDPM基础配置(添加备份介质、推送代理)...
  5. HTML 限制 input 输入类型
  6. 随机排列实现 -shuffle算法
  7. sqlserver management studio 2014 升级2016_画质提升音质更佳:Insta360影石ONE R迎来最强升级...
  8. C++dll导出类的方式__declspec(dllexport)
  9. C++序列化模拟三 -----酒后日志
  10. Android四大组件每个组件的作用?它们都可以开启多进程吗?
  11. 示波器的使用和二极管充放电过程
  12. 整个AppData目录挪到D盘方法
  13. Python拉勾网爬虫实现
  14. 无穷小带来什微积分么大礼包
  15. Linux 上配置 SQL Server Always On Availability Group
  16. Android破解锁屏密码(已root)
  17. win10 jdk1.8/jdk11安装及卸载
  18. ❤️❤️❤️帮好朋友度过情人节,好友直说女友真好哄...每天一个脱单小技巧,赶快收藏起来吧❤️❤️❤️
  19. 回顾2019展望2020
  20. Labview与阿特拉斯开放式通讯 网口读取扭矩值 包括Labview程序

热门文章

  1. Python计算有向图中所有节点出度和入度
  2. 非计算机专业《Python程序设计基础》教学参考大纲
  3. 详解Python序列解包(3)
  4. secp256k1 php,区块链中的数学-secp256k1 签名可锻性以及解决方案
  5. (3~4):C实现数组选择排序
  6. linux 查看文件工具,lsof---Linux查看文件信息的强大工具
  7. 精通开关电源设计第二版pdf_11、秋招年35月准备期——Verilog HDL高级数字设计(第二版)...
  8. mysql 多键sequence_MySQL增多Sequence管理功能
  9. 微软服务器离线补丁工具包,wsus offline update
  10. scrapy python下载图片_使用Scrapy自带的ImagesPipeline下载图片,并对其进行分类。