Socket编程-①相关简介

本文详细介绍了Socket在本地进程间通信(IPC)以及网络进程通信中的应用,涵盖了TCP服务器端流程、TCP/UDP客户端操作,以及相关函数如socket(),bind(),listen(),accept(),send(),recv(),connect()的详解。
摘要由CSDN通过智能技术生成

( 2018年11月26日 )

简略知识点

本地进程间通信(IPC)方式

  • 消息传递(管道、FIFO、消息队列)
  • 同步(互斥量,条件变量,读写锁,文件和写记录锁,信号量)
  • 共享内存(匿名的、具名的)
  • 远程过程调用(Solaris门和Sun RPC)

网络进程中如何通信

网络层的ip地址可以唯一标识网络的进程,传输层的协议+端口可以唯一标识主机中的应用程序(进程)。

利用三元组(ip地址、协议、端口)就可以标识网络的进程,网络中的进程通信就可以利用这个标志与其他进程进行交互。

Socket基本操作

socket是“open - write/read - close”模式的一种实现,故socket也提供了这些操作对应的函数接口。

Socket简单通信

  • 功能:服务器端和客户端,服务器端监听端口发来的请求,收到后向客户端发送一个Hello World,客户机负责发送消息并打印收到的Hello World.
  • 服务器:建立socket,绑定socket和地址信息,开启监听,收到请求后发送数据。
  • 客户端:建立socket,连接服务器端,接收并打印服务器给的数据。

vs2015基于UDP协议的简单通信例程

  • UDP发送和接受数据分别使用sendto()和recvfrom()函数
  • 在vs2015中关于绑定本地Ip地址的inet_addr()函数在c++11中和以前的版本有所区别,新旧函数替换,或定义不检查。
  • UDT流程与UDP类似。

详解知识点

Socket简述

  • socket是网络编程的基本API,应用层编程API,提供了TCP/IP四层模型的第三层传输层的TCP、UDP协议的数据传输方式。第二层网络层有IP协议,不可靠协议,TCP在它基础上提供了可靠传输。UDP仍然提供不可靠传输。
  • 两个进程通信,可通过socket,不管两个进程在什么位置,只要它们主机都实现了TCP/IP协议栈。一个通信实体用网络地址(ip)+port标识。
  • 端口:是一种抽象软件结构(包括一些数据结构和I/O缓冲区)。应用程序同通过系统调用与某端口建立连接后,传输层传给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。端口用一个整数型标识符标识,端口号。端口使用一个16位数字表示,范围0-65535,1024以下端口号保留预定义服务。

Socket编程 - TCP服务器端流程

  • TCP传输模式是服务器先创建一个socket,然后把这个socket绑定到一个地址上。这时socket可以工作,然后向tcp/ip协议栈请求一个监听服务并创建一个服务队列来接受那些请求。
  • 第一次握手:建立连接时,客户端发送syn包(syn = j)到服务器,并进入SYN_SENT状态,等待服务器确认; SYN - 同步序列编号
  • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack = j+1),同时自己也发送一个SYN包(syn = k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack = k+1),此包发送完毕,客户端和服务器进入ESTANLESHED(TCP连接成功)状态,完成三次握手。

Socket相关函数详解 - [附录详解]

  • socket()、bind()、listen()、accept()、close() - 服务器
  • socket()、connect()、close() - 客户

TCP相关函数

  • read()、write()、read() - TCP服务器
  • write()、read() - TCP客户

附录

客户机/服务器模式,套接字类型、面向TCP/UDP的socket编程 - [目标获得详解]

  • 客户机/服务器模式 : 在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式。
  • 套接字的类型:流式套接字(SOCK_STREAM)提供面向连接、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收,实际上是基于TCP协议实现的。数据报式套接字(SOCK_DGRAM)提供无连接服务,数据包以独立句形式发送,不提供无错保证,数据可能丢失或重复。并且接收顺序混乱。实际上是基于UDP协议实现。原始套接字(SOCK_RAW)

面向TCP/UDP的socket编程:

TCP服务器端:

a):创建套接字(socket)
b):将套接字绑定到一个本地地址和端口上(bind)
c):将套接字设为监听模式,准备接受客户请求(listen)
d):等待客户请求到来,当请求到来后,接受连接请求,返回一个新的对应于此次的连接的套接字(accept)
e):用返回的套接字和客户端进行通信(send/recv)
f):返回,等待另一客户请求
g):关闭套接字

TCP客户端

a):创建套接字(socket)
b):向服务器发出连接请求(connect)
c):和服务器进行通信(send/recv)
d):关闭套接字

UDP服务器端(服务器端即先启动的一端为接收端)

a):创建套接字
b):将套接字绑定到一个本地址和端口上
c):等待接受数据(recvfrom)
d):关闭套接字

UDP客户端(发送数据的一端为发送到,也称客户端)

a):创建套接字
b):向服务器发送数据(sendto)
c):关闭套接字

Socket相关函数详解 - [目标获得详解]

  • WSAStartup是为了向操作系统说明,调用哪个库文件,让该库文件与当前的应用程序绑定,从而就可以调用该版本的socket的各种函数了。
  • 头文件 header:  Winsock2.h
    库library: Ws2_32.lib
  • 参数:
    wVersionRequested是Windows Sockets API提供的调用方可使用的最高版本号。高位字节指出副版本(修正)号,低位字节指明主版本号。
    lpWSAData 是指向WSADATA数据结构的指针,用来接收Windows Sockets实现的细节。
    WSADATA数据类型:这个结构被用来存储 被WSAStartup函数调用后返回的 Windows Sockets数据。它包含Winsock.dll执行的数据。
  • 功能:主要就是进行相应的socket库绑定。
  • 使用:当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。
  • 例:假如一个程序要使用2.1版本的Socket,那么程序代码如下
    wVersionRequested = MAKEWORD( 2, 1 );
    err = WSAStartup( wVersionRequested, &wsaData );

socket()函数 - int socket(int domain, int type, int protocol)

  • 函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1。

     #include<sys/types.h>
     #include<sys/socket.h>
    
  • 参数:
    函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族。通信协议族在文件sys/socket.h中定义。
    在这里插入图片描述

    函数socket()的参数type用于设置套接字通信的类型,主要有SOCKET_STREAM(流式套接字)、SOCK——DGRAM(数据包套接字)等。
    在这里插入图片描述
    在这里插入图片描述
    函数socket()的第3个参数protocol用于制定某个协议的特定类型,即type类型中的某个类型。通常某协议中只有一种特定类型,这样protocol参数仅能设置为0;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。
    函数socket()并不总是执行成功,有可能会出现错误,错误的产生有多种原因,可以通过errno获得:
    在这里插入图片描述

  • 例:建立一个流式套接字

int sock = socket(AF_INET, SOCK_STREAM, 0);

bind函数 - int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen)

  • 命名socket。socket名称包含"协议, ip地址, 端口号"这三个要素, 而命名就是通过调用bind函数把socket与这三个要素绑定一起来。
  • 第一个参数是待绑定的套接字,第二个参数是标识绑定在哪个“地方”,第三个参数是这个“地方”的占地大小。返回值表示绑定操作是否成功,0表示成功, -1表示不成功。
  • 例:调用
iRet = bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
// 注意强制转换

sockaddr和sockaddr_in的区别

 #include <netinet/in.h>
  • sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别:
    程序员不应操作sockaddr,sockaddr是给操作系统用的
    程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。
  • 一般的用法为:
    程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数
    网络编程中一段典型的代码为:
int sockfd;
struct sockaddr_in servaddr;
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
/* 填充struct sockaddr_in */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
/* 强制转换成struct sockaddr */
connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

in_addr_t inet_addr(const char* strptr);

返回:若字符串有效则将字符串转换为32位二进制网络字节序的IPV4地址,否则为INADDR_NONE
struct in_addr{
in_addr_t s_addr;
}
所处头文件: #include <arpa/inet.h>

  • 例:
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//本地回路地址

listen函数:int listen ( SOCKET s, int backlog )

  • listen在套接字函数中表示让一个套接字处于监听到来的连接请求的状态
  • 头文件:#include <sys/types.h> #include <sys/socket.h>
  • 功能:listen函数使用主动连接套接字变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
    listen函数一般在调用bind之后-调用accept之前调用。
  • 参数:sockfd 一个已绑定未被连接的套接字描述符
    backlog 连接请求队列(queue of pending connections)的最大长度。用SOMAXCONN则由系统确定。
  • 例:isten(sockSrv, 5);

accept函数:SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR*addrlen )

  • 函数的第一个参数用来标识服务端套接字(也就是listen函数中设置为监听状态的套接字),第二个参数是用来保存客户端套接字对应的“地方”(包括客户端IP和端口信息等), 第三个参数是“地方”的占地大小。返回值对应客户端套接字标识。
  • 例:
 SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len);

send函数:int send ( SOCKET s, const char FAR * buf, int len, int flags)

  • send()是一个计算机函数,功能是向一个已经连接的socket发送数据,如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR。
  • 向一个已连接的套接口发送数据。
    #include <winsock.h>
    int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags);
    s:一个用于标识已连接套接口的描述字。
    buf:包含待发送数据的缓冲区。
    len:缓冲区中数据的长度。
    flags:调用执行方式。
    linux下函数:
    intsend(SOCKETs, constcharFAR*buf, intlen, intflags);
  • 例:
send(sockConn, "test", 20, 0);//用返回的套接字和客户端进行通信。

recv函数:int recv (SOCKET s, char FAR* buf, int len, int flags )

  • 实例:
char sendBuf[256];
char recvBuf[256];
recv(sockConn, recvBuf, strlen(recvBuf), 0);
Sleep(1000);
if (strlen(recvBuf) > 0)
{
cout << recvBuf << endl;
cout << "Input:";
cin >> sendBuf;
send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);//用返回的套接字和客户端进 
                                                // 行通信。
       }

connect函数:int connect (SOCKET s,const struct sockaddrFAR* name, int namelen );

  • 实例:
SOCKADDR_IN addrSrv;//设定服务器端的IP和端口
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//本地回路地址
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
//将其第三个参数设为0,让其 //自动选择协议。
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//与服务器建立连接。
Sleep(2000);
......
...
closesocket(sockClient);
...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值