C++/C socket编程
网络编程就是编写程序使得两个计算机之间可以交换数据。
socket()函数
何为socket
socket原意为“插座”,在计算机领域被翻译成“套接字”,它是计算机之间进行通信的一种约定。在 UNIX/Linux 系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件,UNIX/Linux 中的一切都是文件。为了表示和区分已经打开的文件,UNIX/Linux 会给每个文件分配一个文件描述符,而网络连接也是一个文件,它也有文件描述符。而Windows 也有类似“文件描述符”的概念,但通常被称为“文件句柄”。
我们利用socket()函数来创建一个网络连接,得到返回值就是相应的文件描述符。有了文件描述符,我们就可以使用read()来读取远端计算机传来的数据,用write()向远端计算机发送数据。
Internet套接字
套接字有很多种,这里只讨论Internet套接字。通过 socket() 函数创建连接时,必须告诉它使用哪种数据传输方式。最常用的是流格式套接字和数据报格式套接字。
流格式套接字SOCK_STREAM
流格式套接字是“面向连接的套接字”,对应TCP,SOCK_STREAM是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。
SOCK_STREAM有以下特点:
- 数据按序到达
- 发送数据没有数据边界
- 数据传输过程中不会丢失
SOCK_STREAM类型的套接口为全双向的字节流,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()和recv()传送数据。当会话结束后,调用closesocket()。带外数据根据规定用send()和recv()来接收。
数据报格式套接字SOCK_DGRAM
数据报格式套接字是“无连接的套接字”,对应UDP,SOCK_DGRAM,只管传输数据,不作数据校验,若数据损坏不会重传。
SOCK_DGRAM有以下特点:
- 数据快速传输,可能乱序
- 每次传输数据比较少
- 发送数据有数据边界
SOCK_DGRAM类型套接口允许使用sendto()和recvfrom()从任意端口发送或接收数据报。如果这样一个套接口用connect()与一个指定端口连接,则可用send()和recv()与该端口进行数据报的发送与接收。
TCP/IP协议族
TCP/IP 模型包含了 TCP、IP、UDP、Telnet、FTP、SMTP 等上百个互为关联的协议,其中 TCP 和 IP 是最常用的两种底层协议,所以把它们统称为“TCP/IP 协议族”。
创建套接字
利用int socket(int af,int type,int protocol)创建套接字,返回值为对应句柄(描述符)。
- af:一个地址描述。仅支持AF_INET格式,也就是说ARPA Internet地址格式
- type:socket的类型,如SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)
- protocol:协议号,默认为0,常用协议为IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。
若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()获取相应错误代码。
示例代码:
SOCKET tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //IPPROTO_TCP表示TCP协议
SOCKET udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //IPPROTO_UDP表示UDP协议
//上面两种情况都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议
SOCKET tcp_socket2 = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字
SOCKET udp_socket2 = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
if (tucp_socket == INVALID_SOCKET || udp_socket == INVALID_SOCKET) {
printf_s("Socket error #%d.\n", WSAGetLastError());
exit(1);
}
加载套接字库
在使用套接字前,请先加载套接字库
Windows下使用WSAStartup()函数加载DLL
WSAStartup,即WSA(Windows Sockets Asynchronous,Windows异步套接字)的启动命令,WSAStartup必须是应用程序或DLL调用的第一个Windows Sockets函数,允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节
#include "winsock.h"
#include "windows.h"
#pragma comment(lib,"ws2_32.lib") //静态加入一个lib文件
WORD sockVersion = MAKEWORD(2, 2);
/*
WORD是微软SDK中的无符号16位整形数,MAKEWORD(a,b)是一个宏,这里用来指定使用的Winsock版本 ,MAKEWORD(2,2)即版本2.2
*/
WSADATA wsaData;//WSADATA 结构被用来保存函数 WSAStartup 返回的 Windows Sockets初始化信息
if (WSAStartup(sockVersion, &wsaData) != 0) {
/*
使用 Socket 的程序在使用 Socket 之前必须调用 WSAStartup 函数, 当一个应用程序调用 WSAStartup 函数时,
操作系统根据请求的 Socket 版本来搜索相应的 Socket 库,
然后绑定找到的 Socket 库到该应用程序中。以后应用程序就可以调用所请求的 Socket 库中的其它 Socket 函数了。
*/
printf_s("WSAStartup failed.\n"); // 初始化失败
exit(1);
}
/*socket相关操作
....
*/
WSACleanup()函数释放资源
在套接字使用完毕后使用WSACleanup()函数来主动释放资源
连接建立相关的API函数
SOCKADDR_IN结构体
SOCKADDR_IN是一个数据结构,用作bind,connect,recvfrom,sendto函数的参数,指明地址信息
SOCKADDR_IN结构体定义如下:
typedef struct in_addr {//地址定义,32位
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
struct sockaddr_in {
short sin_family;//地址族(Address Family),也就是地址类型
u_short sin_port;//端口号
struct in_addr sin_addr;//32位IP地址
char sin_zero[8];//不使用,一般为0
};
typedef struct sockaddr_in SOCKADDR_IN;
有关sockaddr_in相关的定义有些嵌套和冗余,这些可能是历史原因,习惯就好。对于IPV6的地址,有sockaddr_in6这个结构来保存IPV6地址,本文不进行深入。
bind()函数
函数原型为:int bind(SOCKET socket,const struct sockaddr* addr,int namelen)
- sock 为 socket 文件描述符
- addr 为 sockaddr 结构体变量的指针(IP地址+端口号)
- addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。
bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。服务器端要用 bind() 函数将套接字与特定的 IP 地址和端口绑定起来,只有这样,流经该 IP 地址和端口的数据才能交给套接字处理。类似地,客户端也要用 connect() 函数建立连接。
connect()函数
函数原型为:int connect(SOCKET s,const struct sockaddr* addr,int namelen)
参数定义与bind()函数相同,本函数用于创建与指定外部端口的连接。有连接的socket客户端通过调用connect函数进行连接,无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候打开端口。
listen()函数
函数原型为:int listen(SOCKET sock, int backlog)
- sock 为 socket 文件描述符
- backlog 为 请求队伍的最大长度
对于服务器端程序,使用 bind() 绑定套接字后,还需要使用 listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求了。
当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。如果将 backlog 的值设置为SOMAXCONN,就由系统来决定请求队列长度。
注意:listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept() 函数。
accept()函数
函数原型为SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen)
参数定义与bind()函数和connect()函数一样。当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。accept()返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而sock是服务器端的套接字,后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
数据收发相关的API函数
Windows 和 Linux 不同,Windows 区分普通文件和套接字,并定义了专门的接收和发送的函数。
send()函数/recv()函数
send函数原型为int send(SOCKET sock, const char *buf, int len, int flags)
- sock 为 要发送数据的套接字
- buf 为 发送数据的缓冲区地址
- len 为 要发送的数据字节数
- flags 一般设置为0/NULL
若无错误发生,send()返回总共发送的字节数,否则返回SOCKET_ERROR
recv函数原型为int recv(SOCKET s, char char *buf, int len, int flags)
参数定义同send函数,如果没有错误发生,recv()返回总共接收的字节数;如果连接被关闭,返回0;否则返回SOCKET_ERROR。
sendto()函数/recvfrom()函数
recvfrom()函数和recv()是可以替换的,只不过recvfrom多了两个参数
recvfrom函数原型为int recvfrom(SOCKET sock,char *buf,int len,unsigned int flags, struct sockaddr *from,socket_t *fromlen)
多余的参数用来接收对端的地址,对于UDP这种无连接的服务可以很方便的进行回复,如果在UDP服务中不需要进行回复,也可以使用recv函数。对于TCP这种面向连接的服务,在accept的时候就已经纪录了地址,无需额外再接收一次地址。
sendto函数原型为int sendto(SOCKET sock,char *buf,int len,unsigned int flags,const struct sockaddr *from,int token)
sendto函数返回总共发送的字节数,sendto()函数和recvfrom()函数一般用于UDP,而recv()函数和send()函数一般用于TCP
socket编程实例
C++/C socket编程的更多相关文章
- Linux下的C Socket编程 -- server端的继续研究
Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...
- java socket编程(li)
一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输.在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以 ...
- Python Socket 编程——聊天室示例程序
上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...
- Linux下的C Socket编程 -- server端的简单示例
Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...
- Linux下的C Socket编程 -- 获取对方IP地址
Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...
- Linux下的C Socket编程 -- 简介与client端的处理
Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...
- python网络编程-socket编程
一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...
- Socket编程实践(2) Socket API 与 简单例程
在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...
- Socket编程实践(1) 基本概念
1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...
- [转]C语言SOCKET编程指南
1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...
随机推荐
- Spring入门案例
一.Spring基本介绍 1.什么是Spring Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制 ...
- bzoj2843极地旅行社
bzoj2843极地旅行社 题意: 一些点,每个点有一个权值.有三种操作:点与点连边,单点修改权值,求两点之间路径上点的权值和(需要判输入是否合法) 题解: 以前一直想不通为什么神犇们的模板中LCT在 ...
- Python数据分析实战:使用pyecharts进行数据可视化
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:刘早起 开始使用 基本套路就是先创建一个你需要的空图层,然后使用.s ...
- OSCP Learning Notes - Enumeration(4)
DNS Enumeration 1. Host Tool host is a simple utility for performing DNS lookups. It is normally use ...
- P3379 最近公共祖先(LCA) 洛谷
题意简单明了(这就是个模板). 就是让我们找2个节点的公共祖先而已,但我们要讲的做法不是生硬的爆搜,而且直接搜好像过不去…… 这次就讲我往后拖了n多天才开始学了倍增LCA. 嗯,这个题,如果2个节点的 ...
- .clearfix 清除浮动,@import
我们知道,在网页的DIV+CSS布局中,很多时候要用到浮动. 既然有浮动,那就有清除浮动. 清除浮动有很多种方式,而在实际项目中,比较常用的是这一种. .clearfix:after { conten ...
- 部署一套完整的Kubernetes高可用集群(二进制,v1.18版)
一.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadm Kubeadm是一个K8s部署工具,提供kubeadm ...
- 7.20试机测 T3 阶乘之和 暴力AC题解
7.20试机测 T3 阶乘之和 暴力AC题解 题外话:此乃本蒟蒻发表的第一篇题解,大家多多关照,支持一下,谢谢 题面 3.阶乘之和(sum.pas/in/out) 问题描述: 给定一个非负整数 n, ...
- 手把手教你基于C#开发WinCC语音报警插件「附源代码」
写在前面 众所周知,WinCC本身是可以利用C脚本或者VBS脚本来做语音报警,但是这种方式的本质是调用已存在的音频文件,想要实现实时播报报警信息是不行的,灵活性还不够,本文主要介绍基于C#/.NET开 ...
- ElasticSearch(二)Kibana、版本控制
Kibana简介: Kibana可视化界面 Kibana是一个开源的分析和可视化平台,设计用于和Elasticsearch一起工作. 你用Kibana来搜索,查看,并和存储在Elasticsearch ...