linux:网络:socket套接字
Posted mbf330
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux:网络:socket套接字相关的知识,希望对你有一定的参考价值。
文章目录
套接字socket
在正式接触网络编程套接字之前,需要先认识几个基本概念。
1.源IP地址和目的IP地址
2.端口号port
3.“端口号” 和 "进程ID"
4.源端口号和目的端口号
相关概念
1.源IP地址和目的IP地址
在IP数据包头部中, 有两个IP地址;分别叫做源IP地址, 和目的IP地址。
源IP地址:发送请求的IP地址。目的IP地址:被请求的主机IP地址。
比如:A机的IP地址为192.168.0.1,B机的IP地址为192.168.0.2
那么一个数据包从A机发送到B机,此通信过程中的A机192.168.0.1就是"源IP地址";B机192.168.0.2就是"目的IP地址".
2.端口号 port
端口号(port)是传输层协议的内容;是一个2字节16位的整数;用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;一个端口号只能被一个进程占用.
3.“端口号” 和 “进程ID”
端口号也是唯一表示一个进程。一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定;
PID是各进程的身份标识符,程序一运行系统就会自动分配给进程一个独一无二的PID。进程终止后,PID被系统回收,可能会被继续给新运行的程序。
4.源端口号和目的端口号
在一台主机上,一个进程对应一个端口。端口的作用就是用来唯一标识这个进程。源端口标识一台主机发起通信的那个进程,目的端口标识另一台主机接受通信的那个进程。有了端口号,接受到报文后才能够知道将报文发送到哪个进程。
传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要发给谁”;
5.套接字的本质
套接字本质是进程间通信。
ip:标定全公网内唯一一台主机。
端口号(port):主机上唯一一个进程。
ip+port:全网唯一一个进程,即为套接字(socket)。
6.TCP协议
此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识;
传输层协议
有连接
可靠传输
面向字节流
7.认识UDP协议
此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识;
传输层协议
无连接
不可靠传输
面向数据报
8.网络字节序
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
调用以下库函数做网络字节序和主机字节序的转换。
h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。
9.socket编程接口
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
10.sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及UNIX Domain Socket. 然而,各种网络协议的地址格式并不相同。
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址;
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6;这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容;
socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;
虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址;
in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数。
简单的UDP网络程序
1.服务器端(接收消息)
#pragma once
#include <iostream>
#include <string>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
using namespace std;\\
class udpServer
private:
//源ip地址,端口号,套接字
string ip;
int port;
int sock;
public:
//构造,初始化列表,给ip和port默认值
udpServer(string _ip="127.0.0.1",int _port=8080)
:ip(_ip)
,port(_port)
//初始化服务器
void initServer()
//创建套接字,IPv4协议,UDP套接字,0为默认方式
sock=socket(AF_INET,SOCK_DGRAM,0);
//sock默认值为3,
cout<<"sock:"<<sock<<endl;
struct sockaddr_in local;
//sockaddr_in保存通信协议类型,端口号以及ip地址
local.sin_family=AF_INET;
local.sin_port=htons(port);
//实际对in_addr中的s_addr进行赋值
local.sin_addr.s_addr=inet_addr(ip.c_str());
//绑定失败退出
//bind(绑定函数)将套接字与主机信息绑定,成功返回0,失败返回-1;
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
//标准错误输出
cerr<<"bind error!\\n"<<endl;
exit(1);
//echoserver
void start()
//要发送的信息
char msg[64];
for(;;)
msg[0]='\\0';
struct sockaddr_in end_point;
socklen_t len=sizeof(end_point);
//recvfrom(经过socket接收消息)
//从socket读数据msg;读写方式为0(阻塞)
//end_point代表“谁发的数据”
//len是输入也是输出,输入时:传入结构体大小,输出时:读到的结构体大小
ssize_t s=recvfrom(sock,msg,sizeof(msg)-1,\\
0,(struct sockaddr*)&end_point,&len);
if(s>0)
msg[s]='\\0';
cout<<"client# "<<msg<<endl;
string echo_string=msg;
echo_string+="[server echo!]";
//sendto:将从socket中读到的信息发送
//从socket中读取,发什么,发的长度,发送方式,往哪发,发的长度
sendto(sock,echo_string.c_str(),echo_string.size(),\\
0,(struct sockaddr*)&end_point,len);
~udpServer()
close(sock);
;
#include "udpServer.h"
int main()
udpServer us;
us.initServer();
us.start();
return 0;
2.用户端发送消息
#pragma once
#include <iostream>
#include <string>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
using namespace std;
class udpClient
private:
//源ip地址,端口号,
string ip;
int port;
int sock;
public:
//传源IP地址以及port
udpClient(string _ip="127.0.0.1",int _port=8080)
:ip(_ip)
,port(_port)
void initClient()
sock=socket(AF_INET,SOCK_DGRAM,0);
cout<<"sock:"<<sock<<endl;
//echoserver
void start()
string msg;
struct sockaddr_in peer;
peer.sin_family=AF_INET;
peer.sin_port=htons(port);
peer.sin_addr.s_addr=inet_addr(ip.c_str());
for(;;)
cout<<"please enter# "<<endl;
cin>>msg;
if(msg=="quit")
break;
sendto(sock,msg.c_str(),msg.size(),0,(struct sockaddr*)&peer,\\
sizeof(peer));
char echo[128];
struct sockaddr_in temp;
ssize_t s=recvfrom(sock,echo,sizeof(echo)-1,0,nullptr,nullptr);
if(s>0)
cout<<"server# "<<echo<<endl;
~udpClient()
close(sock);
;
#include"udpClient.h"
int main()
udpClient uc;
uc.initClient();
uc.start();
return 0;
3.运行截图
用户端发送消息
服务器端接收消息
以上是关于linux:网络:socket套接字的主要内容,如果未能解决你的问题,请参考以下文章
Linux Socket:如何在客户端程序中检测断开的网络?