socket套接字详解(TCP与UDP)

socket套接字详解(TCP与UDP)

内容导读

收集整理的这篇技术教程文章主要介绍了socket套接字详解(TCP与UDP),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含9981字,纯文字阅读大概需要15分钟

内容图文

学习LInux,网络编程套接字是基础,也是新手学习的难点,通过本篇文章,读者可以通过图解、作者的代码实现思路全面理解IP地址、端口号、TCP、UDP概念、socket API用法、模拟客户端/服务器通信等。

  • Mark:阅读blog + 代码实现耗时18分钟

文章重点:
  • IP地址、端口号……

  • socket API

  • 实现UDP客户端/服务器


  • 套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。

前面介绍过,本地的进程间通信(IPC)有很多种方式,常见的总结以下几点:

 1、管道(包括无名管道和命名管道); 2、消息队列; 3、信号量; 4、共享存储。 5、……( Socket和Streams支持不同主机上的两个进程IPC)。
认识网络层通信过程:

初识IP:

(IP就是:Internet协议IP)

在通信时,IP有源IP和目的IP之分,

对比寄快递:网络通信相当于收发快递,IP就是收件/发件人地址,仅仅知道地址还不行,还要知道派送人是谁?这就对比于网络中的端口号概念,端口号标识了一个进程,告诉操作系统,当前这个数据交给哪一个程序进行解析。


端口号:

端口号(port)是传输层协议的内容。

  • 端口号是一个2字节16位的整数;

  • 端口号用来标识一个进程,告诉操作系统,当前这个数据交给哪一个程序进行解析;

  • IP地址 + 端口号能标识网络上的某一台主机的某一个进程;

  • 一个端口号只能被一个进程占用。

端口号 & 进程:
  • 概念

进程有唯一的pid标识,端口号也能标识进程;

一个进程可以绑定多个端口号,一个端口号不能被多个进程绑定。

  • 源端口号 & 目的端口号

传输层协议(TCP/IP)的数据段中有两个端口号,分别叫做源端口号和目的端口号,就是在描述“数据是谁的?发给谁?”

TCP:

(TCP)传输控制协议,面向连接。是一种提供可靠数据传输的通用协议。

  • 传输层协议

  • 有连接

  • 可靠传输

  • 面向字节流

UDP:

(UDP)用户数据报协议,是一个面向无连接的协议。采用该协议不需要两个应用程序先建立连接。UDP协议不提供差错恢复,不能提供数据重传,因此该协议传输数据安全性差。

  • 传输层协议

  • 无连接

  • 不可靠传输

  • 面向数据报


网络字节序:

  • 如何定义网络数据流的地址?

其实很容易理解这个问题,就是C语言中比较讲究的大小端问题。

  • 发送机按内存地址从低到高顺序发送;

  • 接收主机按内存地址从低到高顺序保存;

  • TCP/IP规定:网络数据流应采用大端字节序,即地地址高字节

  • 不论主机是大端机还是小端机,都必须遵循TCP/IP规定;

  • 如果发送机是小端,就先将数据转成大端再发送。

socket API:
//创建socket文件描述符
(TCP/UDP,客户端+服务器)int socket(int domain, int type, int protocol);

参数1(domain): 选择创建的套接字所用的协议族;
AF_INET : IPv4协议;
AF_INET6: IPv6协议;
AF_LOCAL: Unix域协议;
AF_ROUTE:路由套接口;
AF_KEY :密钥套接口。
参数2(type):指定套接口类型,所选类型有:
SOCK_STREAM:字节流套接字;
SOCK_DGRAM : 数据报套接字;
SOCK_RAW : 原始套接口。
procotol: 使用的特定协议,一般使用默认协议(NULL)。

//绑定端口号
(TCP/IP,服务器)int bind(int socket, const struct sockaddr *address, socklen_t address_len);

参数1(socket) : 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
参数2(address):指向特定协议的地址指针。
参数3(address_len):上面地址结构的长度。
返回值:没有错误,bind()返回0,否则SOCKET_ERROR。

//开始监听socket
(TCP,服务器)int listen(int socket, int backlog);

参数1(sockfd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
参数2(backlog):所监听的端口队列大小。

//接受请求
(TCP,服务器)int accept(int socket, struct sockaddr* address, socklen_t* address_len);

参数1(socket) : 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
参数2(address):指向特定协议的地址指针。
参数3(addrlen):上面地址结构的长度。
返回值:没有错误,bind()返回0,否则SOCKET_ERROR。

//建立连接
(TCP,客户端)int connect(int sockfd, const struct struct sockaddr *addr, aocklen_t addrlen);
//关闭套接字int close(int fd);

参数(fd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4,IPv6,……

简单的TCP网络程序:
  • TCP客户—服务器程序的执行流程图:

服务器代码:

#include<iostream>#include<unistd.h>#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>using namespace std;#define SERVER_PORT
5050






 //端口号#define SERVER_IP

"192.168.3.254"

//服务器ip#define QUEUE_SIZE
 5








//所监听端口队列大小int main(int argc, char *argv[]){

//创建一个套接字,并检测是否创建成功

int sockSer;













sockSer = socket(AF_INET, SOCK_STREAM, 0);

if(sockSer == -1){



perror("socket");

}

//设置端口可以重用,可以多个客户端连接同一个端口,并检测是否设置成功

int yes = 1;

if(setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){



perror("setsockopt");

}

struct sockaddr_in addrSer,addrCli;



//创建一个记录地址信息的结构体

addrSer.sin_family = AF_INET;






//所使用AF_INET协议族

addrSer.sin_port = htons(SERVER_PORT);

 //设置地址结构体中的端口号

addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
 //设置其中的服务器ip

//将套接字地址与所创建的套接字号联系起来。并检测是否绑定成功

socklen_t addrlen = sizeof(struct sockaddr);

int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen);

if(res == -1)



perror("bind");

listen(sockSer, QUEUE_SIZE);


 //监听端口队列是否由连接请求,如果有就将该端口设置位可连接状态,等待服务器接收连接

printf("Server Wait Client Accept......n");

//如果监听到有连接请求接受连接请求。并检测是否连接成功,成功返回0,否则返回-1

int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen);

if(sockConn == -1)



perror("accept");

else

{



printf("Server Accept Client OK.n");



printf("Client IP:> %sn", inet_ntoa(addrCli.sin_addr));



printf("Client Port:> %dn",ntohs(addrCli.sin_port));

}

char sendbuf[256];



 //申请一个发送缓存区

char recvbuf[256];



 //申请一个接收缓存区

while(1)

{



printf("Ser:>");



scanf("%s",sendbuf);



if(strncmp(sendbuf,"quit",4) == 0)

//如果所要发送的数据为"quit",则直接退出。





break;



send(sockConn, sendbuf, strlen(sendbuf)+1, 0);
 //发送数据



recv(sockConn, recvbuf, 256, 0);

//接收客户端发送的数据



printf("Cli:> %sn",recvbuf);

}

close(sockSer);



 //关闭套接字

return 0;}

客户端代码:

#include<iostream>#include<unistd.h>#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>using namespace std;#define SERVER_PORT
5050#define SERVER_IP

"192.168.3.254"int main(int argc, char *argv[]){

//创建客户端套接字号,并检测是否创建成功

int sockCli;

sockCli = socket(AF_INET, SOCK_STREAM, 0);

if(sockCli == -1)



perror("socket");

//创建一个地址信息结构体,并对其内容进行设置

struct sockaddr_in addrSer;



 addrSer.sin_family = AF_INET;



 //使用AF_INET协议族

addrSer.sin_port = htons(SERVER_PORT);
//设置端口号

addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
 //设置服务器ip

bind(sockCli,(struct sockaddr*)&addrCli, sizeof(struct sockaddr));

//将套接字地址与所创建的套接字号联系起来

//创建一个与服务器的连接,并检测连接是否成功

socklen_t addrlen = sizeof(struct sockaddr);

int res = connect(sockCli,(struct sockaddr*)&addrSer, addrlen);

if(res == -1)



perror("connect");

else



printf("Client Connect Server OK.n");

char sendbuf[256];

 //申请一个发送数据缓存区

char recvbuf[256];

 //申请一个接收数据缓存区

while(1)

{



recv(sockCli, recvbuf, 256, 0);

//接收来自服务器的数据



printf("Ser:> %sn",recvbuf);



printf("Cli:>");



scanf("%s",sendbuf);



if(strncmp(sendbuf,"quit", 4) == 0)

//如果客户端发送的数据为"quit",则退出。





break;



send(sockCli, sendbuf, strlen(sendbuf)+1, 0);
 //发送数据

}

close(sockCli);


 //关闭套接字

return 0;}
简单的UDP网络程序:

  • 相对与TCP来说,UDP安全性差,面向无链接。所以UDP地实现少了连接与接收连接的操作。所以在收发数据时就不能再用send()和recvfrom()了,而是用sendto()和recvto()之名从哪收发数据。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

参数1(sockfd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)
参数2(buf):指向存有发送数据的缓冲区的指针
参数3(len):缓冲区长度。
**参数4(flags):**flags的值或为0,或为其他

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

参数1(sockfd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)
参数2(buf):指向存有接收数据的缓冲区的指针
参数3(len):缓冲区长度
**参数4(flags):**flags的值或为0,或为其他

服务器端代码:

#include<stdio.h>#include<unistd.h>#include<string.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/socket.h>int main(){

//创建一个套接字,并检测是否创建成功

int sockSer = socket(AF_INET, SOCK_DGRAM, 0);

if(sockSer == -1)



perror("socket");

struct sockaddr_in addrSer;
//创建一个记录地址信息的结构体

 addrSer.sin_family = AF_INET;

//使用AF_INET协议族

 addrSer.sin_port = htons(5050);

 //设置地址结构体中的端口号

addrSer.sin_addr.s_addr = inet_addr("192.168.3.169");
//设置通信ip

//将套接字地址与所创建的套接字号联系起来,并检测是否绑定成功

socklen_t addrlen = sizeof(struct sockaddr);

int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen);

if(res == -1)



perror("bind");

char sendbuf[256];

//申请一个发送数据缓存区

char recvbuf[256];

//申请一个接收数据缓存区

struct sockaddr_in addrCli;

while(1)

{



recvfrom(sockSer,recvbuf,256,0,(struct
sockaddr*)&addrCli, &addrlen);

 //从指定地址接收客户端数据



printf("Cli:>%sn",recvbuf);



printf("Ser:>");





scanf("%s",sendbuf);



sendto(sockSer,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&addrCli, addrlen);

//向客户端发送数据

}

return 0;}

客户端代码:

#include<stdio.h>#include<unistd.h>#include<string.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/socket.h>int main(){

//创建一个套接字,并检测是否创建成功

int sockCli = socket(AF_INET, SOCK_DGRAM, 0);

if(sockCli == -1){



perror("socket");

}

addrSer.sin_family = AF_INET;

//使用AF_INET协议族

 addrSer.sin_port = htons(5050);

 //设置地址结构体中的端口号

addrSer.sin_addr.s_addr = inet_addr("192.168.3.169");
//设置通信ip

socklen_t addrlen = sizeof(struct sockaddr);

char sendbuf[256];

//申请一个发送数据缓存区

char recvbuf[256];

//申请一个接收数据缓存区

while(1){



//向客户端发送数据



printf("Cli:>");



scanf("%s",sendbuf);



sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, addrlen);




 接收来自客户端的数据



recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &addrlen);



printf("Ser:>%sn", recvbuf);

}

return 0;}


【推荐课程:TCP/IP视频教程

以上就是socket套接字详解(TCP与UDP)的详细内容,更多请关注Gxl网其它相关文章!

内容总结

以上是为您收集整理的socket套接字详解(TCP与UDP)全部内容,希望文章能够帮你解决socket套接字详解(TCP与UDP)所遇到的程序开发问题。 如果觉得技术教程内容还不错,欢迎将网站推荐给程序员好友。

内容备注

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。


本文关键词:

联系我们

在线咨询:点击这里给我发消息

邮件:w420220301@qq.com