简介

本文主要介绍的socket编程的实现相关的内容;

理论

函数

socket

用来创建socket描述符,它唯一标识一个socket

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

客户端和服务器都要进行的操作

bind

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

通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

服务器端的操作

网络字节序与主机字节序

主机字节序:就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:

  1. Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
  2. Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。

注意:

在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。

listen/connect

int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen是服务器的操作,用来监听客户端

connect是客户端的操作,用来连接服务端,发出请求连接,此时服务端通过listen来接收到客户端的请求

accept

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

accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

send/recv

因为之前客户端和服务器已经建立好了连接,可以通过网络IO进行发送接收数据的操作了,此时为网络中不同进程的通信;

  • read()/write()
  • recv()/send()
  • readv()/writev()
  • recvmsg()/sendmsg()
  • recvfrom()/sendto()

close

int close(int fd);

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

客户端和服务器端都要进行的操作;

其他函数

网址:http://c.biancheng.net/cpp/html/370.html

总结

总的来说,客户端和服务端分别要做的工作如下:

  • 客户端: socket connect send/recv close
  • 服务端: socket bind listen accept send/recv close

握手连接

三次握手建立连接

从下面的图可以理解客户端和服务端之间需要操作哪些函数。

从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

四次握手释放连接

客户端首先发送一个关闭请求Fin M,服务器则会返回一个确认收到客户端请求的回应报文,等再过一段时间服务器也会发送一个主动关闭的请求,因为在这段时间中,有可能服务器在发送数据给客户端,因此需要等待服务器发送数据给客户端完后才会主动给客户端发送关闭请求,同样的,客户端发送回应报文,告诉服务端收到了,并且关闭,但是此时,服务端并没有马上关闭,因为有可能因为网络延迟的原因,服务端没有收到回应报文,所以通常会进入到TIME_WAIT状态,一般2ML的时间,如果收到了,服务端才会进入到CLOSE状态;

实现

客户端

//
// main.cpp
// Client
//
// Created by staff on 16/11/1.
// Copyright © 2016年 George1994. All rights reserved.
// #include <iostream> #include <stdio.h>
#include <iostream>
#include <sys/socket.h>
#include <fcntl.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
using namespace std;
#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1 int main()
{
// 创建套节字
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
printf(" Failed socket() \n");
return 0;
} // 也可以在这里调用bind函数绑定一个本地地址
// 否则系统将会自动安排 // 填写远程地址信息
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(4567);
// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址
// 如果你的计算机没有联网,直接使用127.0.0.1即可
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf(" Failed connect() \n");
return 0;
} char buff[256];
char szText[256] ; while(true)
{
// 向服务器端发送数据
gets(szText) ;
szText[255] = '\0';
::send(s, szText, strlen(szText), 0) ; //从服务器端接收数据
// size_t nRecv = ::recv(s, buff, 256, 0);
// if(nRecv > 0)
// {
// buff[nRecv] = '\0';
// printf("接收到数据:%s\n", buff);
// }
} // 关闭套节字
::close(s);
return 0;
}

服务端

//
// main.cpp
// Server
//
// Created by staff on 16/11/1.
// Copyright © 2016年 George1994. All rights reserved.
// #include <iostream> #include <stdio.h>
#include <iostream>
#include <sys/socket.h>
#include <fcntl.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
using namespace std;
#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1 int main()
{
// 创建套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//用来指定套接字使用的地址格式,通常使用AF_INET,即IPv4
//指定套接字的类型,若是SOCK_DGRAM,则用的是udp不可靠传输
//配合type参数使用,指定使用的协议类型(当指定套接字类型后,可以设置为0,因为默认为UDP或TCP)
if(sListen == INVALID_SOCKET)
{
printf("Failed socket() \n");
return 0;
} // 填充sockaddr_in结构 ,是个结构体
/* struct sockaddr_in { short sin_family; //地址族(指定地址格式) ,设为AF_INET
u_short sin_port; //端口号
struct in_addr sin_addr; //IP地址
char sin_zero[8]; //空子节,设为空
} */ sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(4567); //1024 ~ 49151:普通用户注册的端口号
sin.sin_addr.s_addr = INADDR_ANY; // 绑定这个套节字到一个本地地址
if(::bind(sListen, (sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Failed bind() \n");
return 0;
} // 进入监听模式
//2指的是,监听队列中允许保持的尚未处理的最大连接数
if(::listen(sListen, 2) == SOCKET_ERROR)
{
printf("Failed listen() \n");
return 0;
} // 循环接受客户的连接请求
sockaddr_in remoteAddr;
socklen_t nAddrLen = sizeof(struct sockaddr_in);
SOCKET sClient = 0;
char szText[] = " TCP Server Demo! \r\n";
while(sClient==0)
{
// 接受一个新连接
//((SOCKADDR*)&remoteAddr)一个指向sockaddr_in结构的指针,用于获取对方地址
sClient = ::accept(sListen, (sockaddr *)&remoteAddr, &nAddrLen);
if(sClient == INVALID_SOCKET)
{
printf("Failed accept()");
} printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
continue ;
} while(true)
{
// // 向客户端发送数据
// gets(szText) ;
// ::send(sClient, szText, strlen(szText), 0); // 从客户端接收数据
char buff[256] ;
size_t nRecv = ::recv(sClient, buff, 256, 0);
if(nRecv > 0)
{
buff[nRecv] = '\0';
printf(" 接收到数据:%s\n", buff);
}
} // 关闭同客户端的连接
::close(sClient); // 关闭监听套节字
::close(sListen); return 0;
}

最后

借鉴自:

socket原理

socket

Socket理解的更多相关文章

  1. Socket 理解

    TCP/IP要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协 ...

  2. python socket理解

    socket 所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象.一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制.从所处的地位来讲 ...

  3. python之socket 网络编程

    提到网络通信不得不复习下osi七层模型: 七层模型,亦称OSI(Open System Interconnection)参考模型,是参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互 ...

  4. Python之路【第七篇】:初识Socket

    What is Socket 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制, ...

  5. socket+网路编程

    1.网络编程: 通过某种计算机语言来实现不同设备间的资源共享和信息传递.计算机网络的创造可能比计算机本身意义更重大!!!(否则,你只能玩单机版游戏 OSI模型 OSI模型定义了不同计算机互联的标准,是 ...

  6. 项目总结——深入浅出socket网络编程

    前言: 为什么会有如题的概念呢,我想对于没有主动听说过socket网络编程的人来说读到题目可能就已经蒙头了,为了很好的让大家进入场景,首先说一下一个需要用到这点东西的业务需求. 首先大家应该明确的是s ...

  7. Socket编程(摘抄)

    http://www.blogjava.net 例子代码就在我的博客中,包括六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具工程,和此小工具的所有运行时库.资源和执行程序.代码 ...

  8. Socket编程指南及示例程序

    例子代码就在我的博客中,包括六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具工程,和此小工具的所有运行时库.资源和执行程序.代码的压缩包位置是http://www.blogja ...

  9. windows socket 网络编程

    样例代码就在我的博客中,包含六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具project,和此小工具的全部执行时库.资源和执行程序.代码的压缩包位置是http://www.b ...

随机推荐

  1. iOS中二维码的生成与使用(入门篇)

    这里简单总结一下关于二维码的扫描与生成,用的是原生的AVFoundation框架,其实这个框架目前功能还是够用的,不过这里推荐一个二维码扫描的第三方(face++),网址就不贴了,直接度娘就OK,里面 ...

  2. mysql 加载文本数据

    可以配置导入哪几列,每个字段用什么隔开,每行用什么隔开,也可以单独设置某个字段的值. 详细看代码: LOAD DATA INFILE 'D:/aa.txt' INTO TABLE aa FIELDS ...

  3. devm_kzalloc and kmalloc

    Move resources allocated using unmanaged interface to managed devm interface So today let's talk abo ...

  4. Spring框架:Spring容器具体解释

    Spring容器 Spring容器能够帮助你管理所有的Bean对象.专业术语称之为IoC控制反转.在传统的程序中.对象的生成都是由开发人员完毕的.而在控制反转中,对象的生成所有都交给框架完毕.这种优点 ...

  5. [ES7] Descorator: evaluated & call order

    When multiple decorators apply to a single declaration, their evaluation is similar to function comp ...

  6. There is an error while getting planid. No Free partitions available

    问题概述 Oracle Advanced Supply Chain Planning最初的设置职责的时候有点问题,不知是不是要打什么补丁或其它配置什么东东,, 这个提示,,但我查到的分区是还有可用分区 ...

  7. WCF - 实例与会话

    实例上下文 实例上下文是对服务实例的封装 是WCF管理服务实例生命周期的依托  一个WCF服务通过ServiceHost进行寄宿 开启服务后当接收到请求 则会判断当前是否存在实例上下文 如果存在 则通 ...

  8. 使用C#WebClient类访问(上传/下载/删除/列出文件目录)由IIS搭建的http文件服务器

    前言 为什么要写这边博文呢?其实,就是使用C#WebClient类访问由IIS搭建的http文件服务器的问题花了我足足两天的时间,因此,有必要写下自己所学到的,同时,也能让广大的博友学习学习一下. 本 ...

  9. Java 反射学习笔记

    要学反射,先要了解Class这个类,Class是所有Java类的一个总称,Class的实例中存储的是一个类的字节码,获取Class的实例有三种方式: System.class new Date().g ...

  10. php yield

    php中关于 yield 关键字的介绍[点击查看] <?php function gen_one_to_three() { for ($i = 1; $i <= 3; $i++) { // ...