网络编程就是编写程序使得两个计算机之间可以交换数据。

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编程实例

socket编程实例

C++/C socket编程的更多相关文章

  1. Linux下的C Socket编程 -- server端的继续研究

    Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...

  2. java socket编程(li)

    一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输.在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以 ...

  3. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  4. Linux下的C Socket编程 -- server端的简单示例

    Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...

  5. Linux下的C Socket编程 -- 获取对方IP地址

    Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...

  6. Linux下的C Socket编程 -- 简介与client端的处理

    Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...

  7. python网络编程-socket编程

     一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...

  8. Socket编程实践(2) Socket API 与 简单例程

    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...

  9. Socket编程实践(1) 基本概念

    1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...

  10. [转]C语言SOCKET编程指南

    1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...

随机推荐

  1. oracle 12c数据库在Windows环境下的安装

    ​    因为菜鸟小白之前做着一些数据库审计产品的测试,接下来我会分享一些关于数据库安装和通过python的访问数据库的知识 安装 首先我们需要下载一个oracle 12c的安装程序,解压后右键点击“ ...

  2. Porter 进入 CNCF 云原生全景图,新版本即将发布!

    近日,KubeSphere 社区子项目面向物理机环境的负载均衡器 Porter 正式进入 CNCF Landscape.CNCF Landscape 在云原生实践过程中的每个环节帮助用户了解有哪些具体 ...

  3. tensorflow对鸢尾花进行分类——人工智能入门篇

    tensorflow之对鸢尾花进行分类 任务目标 对鸢尾花数据集分析 建立鸢尾花的模型 利用模型预测鸢尾花的类别 环境搭建 pycharm编辑器搭建python3.* 第三方库 tensorflow1 ...

  4. 从一次故障聊聊前端 UI 自动化测试

    背景 事件的起因在于老板最近的两次"故障",一次去年的,一次最近.共同原因都是脚手架在发布平台发布打包时出错,导致线上应用白屏不可用. 最神奇的是,事后多次 Code Review ...

  5. TestNg失败重跑—解决使用 dataProvider 参数化用例次数冲突问题

    问题背景 在使用 testng 执行 UI 自动化用例时,由于 UI自动化的不稳定性,我们在测试的时候,往往会加上失败重跑机制.在不使用 @DataProvider 提供用例参数化时,是不会有什么问题 ...

  6. Spring事务源码分析专题(一)JdbcTemplate使用及源码分析

    Spring中的数据访问,JdbcTemplate使用及源码分析 前言 本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成 对源码分析前,我希望先介绍一下Spring中数据访问的相关内容 ...

  7. 题解 洛谷 P6142 【[USACO20FEB]Delegation P】

    和赛道修建类似,先对\(k\)进行二分,将最值问题转化为判定问题. 在判定一个\(k\)是否合法时,贪心去考虑,一个节点下面的若干条链在合并时,一条链肯定和另一条使它合并后恰好满足长度限制的链合并最优 ...

  8. iOS打包测试ipa

    1. 连接iphone真机 2.选中真机, archive

  9. 让内层浮动的Div将外层Div撑开 -----清浮动

    清浮动的好处写多了都能体会到,解决高度塌陷, 一般情况下是要清除浮动的,不然会影响下面标签的排版. <div class="parent" style="width ...

  10. java反序列化——apache-shiro复现分析

    本文首发于“合天智汇”公众号 作者:Fortheone 看了好久的文章才开始分析调试java的cc链,这个链算是java反序列化漏洞里的基础了.分析调试的shiro也是直接使用了cc链.首先先了解一些 ...