最后更新:2019-10-25

一 基本概念

socket, 又称为"套接字"或者"插座". 是操作系统提供的一种进程间通信机制.目前大多用于不同网络设备之间的通信. socket 位于应用层与传输层之间, 通过传递给 socket 不同的参数, socket 最终选择不一样的协议(TCP/UDP等), 也就是说 socket 其实传输层协议簇的软件抽象.

图片来自于网络


1.1 流程概述

在网络应用中, 通信的两个进程之间主要用的是客户端/服务器 (C/S)模式, 即客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务. socket 最开始设计于 Unix, Unix/Linux 设计哲学是 "一切皆文件", 因此,我们可以像文件操作(open/read/write/close)一样来操作 socket.

下图展示展示 C/S 之间如何使用 socket

对于 TCP 服务端来说:

  • 创建 socket, 创建一个通信断点描述符, 但在进程中为未打开状态;
  • 绑定(bind) socket, 将 socket 绑定到某个地址以及端口上;
  • 监听(listen) socket, 用于表示该 socket 为一个被动链接(passive socket), 可以理解为 服务器的socket,需要客户端来主动连接了;
  • 接收客户端的请求(accept), 服务端通过该方法接收客户端的连接请求;
  • 读(read)/写(write)
  • 关闭 socket

对于客户端来说就比较的简单: 创建一个socket, 然后连接服务器(connect), 完成对应的数据操作(读写), 完成后关闭(close);

二 socket 相关接口

2.1 创建 socket()

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

socket 创建返回一个 socket 描述符, 跟文件描述符类似,后面的读写都需要依赖于这个描述符.

2.1.1 参数-协议族(domain)

协议族决定了使用的 socket 地址类型;

  • Unix 域 socket: AF_UNIXAF_LOCAL, 使用地址类型为: struct sockaddr_un
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* Pathname */
};
  • AF_INET, ipv4, 使用地址类型为: struct sockaddr_in
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
}; /* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
  • AF_INET6, ipv6, 使用地址类型为: sockaddr_in6
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
}; struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};
  • 其他类型(AF_X25 / AF_AX25 / AF_NETLINK 等),其他类型目前还没有涉及到,不做过多讨论,感兴趣可以访问手册进行研究;

2.1.2 参数-类型(type)

表示 socket 通信的类型

  • SOCK_STREAM: 可靠的面向流服务或流套接字 (TCP)
  • SOCK_DGRAM: 数据报文服务或者数据报文套接字 (UDP)
  • SOCK_SEQPACKET:可靠的连续数据包服务
  • SOCK_RAW: 网络层之上自行指定运输层协议头,即原始套接字

2.1.3 参数-协议类型(protocol)

指定实际使用的传输协议, 传 0 表示根据前面的 domain 和 type 选择协议;

2.2 其他函数略过

其他的函数没有什么特别需要注意的地方,直接参考下面例子以及手册就能看懂


三 示例程序-本地进程间通信

当给 socket() 的协议族传入(AF_UNIXAF_LOCAL)时,可表示Unix域socket, 用于本地进程间通信.

其中地址类型中的 sun_path是一个值得注意的地方,根据文档所描述,有三种类型

+ pathname: 文件类型,这种文件需要服务端与客户端对文件都有操作权限, 程序执行过程中,可以看到对应的文件.

+ unnamed

+ abstract: 抽象类型, 这种类型与 pathname 区别在于,给 sun_path 的第一个值为 \0;

3.1 客户端程序


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h> static const char *SERVERNAME = "@servername";
int main(int argc, char const *argv[])
{
int sockfd;
struct sockaddr_un serverAddr;
socklen_t len;
char buffer[1024]; sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0)
{
perror("init fail");
exit(1);
} memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sun_family = AF_UNIX;
strncpy(serverAddr.sun_path, SERVERNAME, sizeof(serverAddr.sun_path)); serverAddr.sun_path[0] = '\0';
len = offsetof(struct sockaddr_un, sun_path) + sizeof(SERVERNAME); if (connect(sockfd, (struct sockaddr *)&serverAddr, len) < 0)
{
perror("connect fail");
exit(1);
} while (fgets(buffer, sizeof(buffer), stdin) != NULL)
{
write(sockfd, buffer, strlen(buffer));
} close(sockfd); return 0;
}

3.2 服务端程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h> static const char *SERVERNAME = "@servername"; int main(int argc, char const *argv[])
{
int sockfd;
struct sockaddr_un serverAddr;
struct sockaddr_un clientAddr;
socklen_t clientLen;
socklen_t serverLen; char buffer[1024];
int connfd; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
perror("init fail");
exit(1);
} // 2. 设置对应地址,然后进行 connect
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sun_family = AF_UNIX;
strncpy(serverAddr.sun_path, SERVERNAME, sizeof(serverAddr.sun_path)); // abstract & calculate length
serverAddr.sun_path[0] = '\0';
serverLen = offsetof(struct sockaddr_un, sun_path) + sizeof(SERVERNAME); if (bind(sockfd, (struct sockaddr *)&serverAddr, serverLen) < 0)
{
perror("bind fail");
exit(1);
} if (listen(sockfd, 20) < 0)
{
perror("listen fail");
exit(1);
} while (1)
{
// 阻塞到有客户端链接
if ((connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientLen)) < 0)
{
printf("accept fail \r\n");
continue;
}
printf("accept success \r\n"); while(1)
{
memset(buffer, 0, sizeof(buffer)); int n = read(connfd, buffer, sizeof(buffer));
if (n < 0) {
perror("read error");
break;
} else if(n == 0) {
printf("EOF\n");
break;
} printf("received: %s", buffer);
}
close(connfd);
} close(sockfd); return 0;
}

四 实例程序-IPV4(6)网络通信

4.1 客户端程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h> int main(int argc, char const *argv[])
{
// ipv4
int sockfd;
struct sockaddr_in addr_in;
char buffer[1000]; if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("init socket fail");
exit(1);
} bzero(&addr_in, sizeof(addr_in));
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(9898);
addr_in.sin_addr.s_addr = inet_addr("103.101.153.8"); if ((connect(sockfd, (struct sockaddr *)&addr_in, sizeof(addr_in)))<0)
{
perror("connect fail");
exit(1);
} while (fgets(buffer, sizeof(buffer), stdin))
{
write(sockfd, buffer, strlen(buffer));
} close(sockfd); return 0;
}

4.2 服务端程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h> int main(int argc, char const *argv[])
{
int serfd, clifd;
struct sockaddr_in seraddr_in, cliaddr_in;
socklen_t clilen;
char buffer[1000]; if ((serfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("init socket fail");
exit(1);
} bzero(&seraddr_in, sizeof(seraddr_in));
seraddr_in.sin_family = AF_INET;
seraddr_in.sin_port = htons(9898);
seraddr_in.sin_addr.s_addr = inet_addr("103.101.153.8"); if (bind(serfd, (struct sockaddr *)&seraddr_in, sizeof(seraddr_in)) < 0)
{
perror("bind fail");
exit(1);
} if (listen(serfd, 20) < 0)
{
perror("listen fail");
exit(1);
} while (1)
{
if ((clifd = accept(serfd, (struct sockaddr *)&cliaddr_in, &clilen)) < 0)
{
perror("accept fail");
continue;
} while (1)
{
memset(buffer, 0, sizeof(buffer));
int len = read(clifd, buffer, sizeof(buffer)); if (len < 0 )
{
perror("read fail");
break;
} if (len == 0)
{
perror("EOF");
break;
} printf("receive: %s", buffer);
} close(clifd);
} close(serfd); return 0;
}

对比本地与网络通信,就是选择的协议族以及对应的地址不一样而已.

参考链接:

Socket编程-基础使用的更多相关文章

  1. socket编程基础-字节序/IP/PORT转换/域名

    socket编程基础 网络IP操作函数 字符串的IP和32位的IP转换 #include <sys/socket.h> #inlcude <netinet/in.h> #inc ...

  2. c#socket编程基础

    Microsoft.Net Framework为应用程序访问Internet提供了分层的.可扩展的以及受管辖的网络服务,其名字空间System.Net和System.Net.Sockets包含丰富的类 ...

  3. Java从零开始学四十五(Socket编程基础)

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

  4. java socket编程基础(转)

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

  5. Python学习笔记——基础篇【第七周】———FTP作业(面向对象编程进阶 & Socket编程基础)

    FTP作业 本节内容: 面向对象高级语法部分 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 参考:http://www.cnblogs.com/wupeiqi/ ...

  6. 【转】Java Socket编程基础及深入讲解

    原文:https://www.cnblogs.com/yiwangzhibujian/p/7107785.html#q2.3.3 Socket是Java网络编程的基础,了解还是有好处的, 这篇文章主要 ...

  7. 【Socket】Java Socket编程基础及深入讲解

    Socket是Java网络编程的基础,了解还是有好处的, 这篇文章主要讲解Socket的基础编程.Socket用在哪呢,主要用在进程间,网络间通信.本篇比较长,特别做了个目录: 一.Socket通信基 ...

  8. 你得学会并且学得会的Socket编程基础知识

    这一篇文章,我将图文并茂地介绍Socket编程的基础知识,我相信,如果你按照步骤做完实验,一定可以对Socket编程有更好地理解. 本文源代码,可以通过这里下载 http://files.cnblog ...

  9. Java Socket编程基础篇

    原文地址:Java Socket编程----通信是这样炼成的 Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Sock ...

  10. 【socket编程基础模板】

    网络编程的基础是基于socket编程.socket(TCP)编程基于固定编程模板 server端: socket(声明socket类型) bind(命令socket,绑定地址和端口) listen(创 ...

随机推荐

  1. Git 实习一个月恍然大悟合集

    从开始实习到现在大概有一个月了,这个月时间接触了很多新东西,其中就包括了git版本控制.分支管理等等.我在这段时间里,深深地感受到了git对公司项目代码管理和控制.团队合作带来的益处和其重要性.其实在 ...

  2. 简单的shell练习

    1.判断/etc/inittab文件是否大于100行,如果大于,则显示”/etc/inittab is a big file.”否者显示”/etc/inittab is a small file.”# ...

  3. Mac下的Web性能压力测试工具:ab(ApacheBench)

    Web开发,少不了的就是压力测试,它是评估一个产品是否合格上线的基本标准. ab是一种用于测试Apache超文本传输协议(HTTP)服务器的工具.apache自带ab工具,可以测试Apache.IIS ...

  4. 13 Python之第一类对象闭包和迭代器

      def fn():     print("我叫fn") fn() print(fn)## <function fn at 0x0000000001D12E18> f ...

  5. 16 Scrapy之分布式爬虫

    redis分布式部署 1.scrapy框架是否可以自己实现分布式? - 不可以.原因有二. 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls ...

  6. Vue项目里添加特殊字体或 某些字体乱码的问题

    问题:有个西藏的项目因使用藏文,而出现乱码 (一开始不乱的,不知道后台怎么处理...后来不知道为啥,前端乱码了) 解决:字体ttf 格式已下载,在项目中全局引入了 @font-face { font- ...

  7. Hyperledger Fabric 环境搭建(2)

    上一篇https://www.cnblogs.com/xdyixia/p/11738096.html 介绍了Hyperledger Fabric环境中各种软件安装和源码编译,这一篇介绍快速运行一个简单 ...

  8. Spring Boot 实际操作

    1.什么是springboot 2.springboot的很多默认编码方式都是utf-8,真是福利啊. 3.spring boot如何启动和访问和MocMvc测试 4.开发环境的调试热启动 5.app ...

  9. 00:Java简单了解

    浅谈Java之概述 Java是SUN(Stanford University Network),斯坦福大学网络公司)1995年推出的一门高级编程语言.Java是一种面向Internet的编程语言.随着 ...

  10. 2019 年百度之星·程序设计大赛 - 初赛一Game HDU 6669 (实现,贪心)

    Game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submissi ...