服务端套接字创建过程

第一步:调用socket函数创建套接字

//成功时返回文件表述符,失败时返回-1
int socket(int __domain, int __type, int __protocol)
  • domain:套接字使用的协议族(Protocol Family)信息;
  • type:套接字数据传输类型信息;
  • protocol:计算机间通信中使用的协议信息;

协议族(Protocol Family)

 Name         Purpose                                    Man page
AF_UNIX Local communication unix(7)
AF_LOCAL Synonym for AF_UNIX
AF_INET IPv4 Internet protocols ip(7)
AF_IPX IPX - Novell protocols
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_PACKET Low-level packet interface packet(7)

套接字类型(Type):是指套接字的传输方式

  • 面向连接的套接字(SOCK_STREAM)

    • 传输过程中数据不会消失
    • 按序传输数据
    • 传输的数据不存在数据边界(Boundary):调用了三次write传递了100字节,接受者仅一次read接收了全部
  • 面向消息的套接字(SOCK_DGRAM)
    • 强调快速传输而非传输顺序
    • 传输的数据可能丢失也可能损毁
    • 传输的数据有数据边界
    • 限制每次传输的数据大小

协议的最终选择

/* Standard well-defined IP protocols.  */
enum
{
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */
#define IPPROTO_ICMP IPPROTO_ICMP
IPPROTO_IGMP = 2, /* Internet Group Management Protocol. */
#define IPPROTO_IGMP IPPROTO_IGMP
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94). */
#define IPPROTO_IPIP IPPROTO_IPIP
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
#define IPPROTO_TCP IPPROTO_TCP
IPPROTO_EGP = 8, /* Exterior Gateway Protocol. */
#define IPPROTO_EGP IPPROTO_EGP
IPPROTO_PUP = 12, /* PUP protocol. */
#define IPPROTO_PUP IPPROTO_PUP
IPPROTO_UDP = 17, /* User Datagram Protocol. */
#define IPPROTO_UDP IPPROTO_UDP
IPPROTO_IDP = 22, /* XNS IDP protocol. */
#define IPPROTO_IDP IPPROTO_IDP
IPPROTO_TP = 29, /* SO Transport Protocol Class 4. */
#define IPPROTO_TP IPPROTO_TP
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol. */
#define IPPROTO_DCCP IPPROTO_DCCP
IPPROTO_IPV6 = 41, /* IPv6 header. */
#define IPPROTO_IPV6 IPPROTO_IPV6
IPPROTO_RSVP = 46, /* Reservation Protocol. */
#define IPPROTO_RSVP IPPROTO_RSVP
IPPROTO_GRE = 47, /* General Routing Encapsulation. */
#define IPPROTO_GRE IPPROTO_GRE
IPPROTO_ESP = 50, /* encapsulating security payload. */
#define IPPROTO_ESP IPPROTO_ESP
IPPROTO_AH = 51, /* authentication header. */
#define IPPROTO_AH IPPROTO_AH
IPPROTO_MTP = 92, /* Multicast Transport Protocol. */
#define IPPROTO_MTP IPPROTO_MTP
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET. */
#define IPPROTO_BEETPH IPPROTO_BEETPH
IPPROTO_ENCAP = 98, /* Encapsulation Header. */
#define IPPROTO_ENCAP IPPROTO_ENCAP
IPPROTO_PIM = 103, /* Protocol Independent Multicast. */
#define IPPROTO_PIM IPPROTO_PIM
IPPROTO_COMP = 108, /* Compression Header Protocol. */
#define IPPROTO_COMP IPPROTO_COMP
IPPROTO_SCTP = 132, /* Stream Control Transmission Protocol. */
#define IPPROTO_SCTP IPPROTO_SCTP
IPPROTO_UDPLITE = 136, /* UDP-Lite protocol. */
#define IPPROTO_UDPLITE IPPROTO_UDPLITE
IPPROTO_MPLS = 137, /* MPLS in IP. */
#define IPPROTO_MPLS IPPROTO_MPLS
IPPROTO_RAW = 255, /* Raw IP packets. */
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_MAX
};

第二步:调用bind函数分配IP地址和端口号

//成功时返回0,失败时返回-1
int bind(int __fd, const struct sockaddr *__addr, socklen_t __len)
  • __fd:要分配地址信息(IP地址和端口号)的套接字文件表述符。
  • __addr:存有地址信息的结构体变量地址值
  • __len:第二个结构体变量的长度

第三步:调用listen函数转为可接收请求状态

//成功时返回0,失败时返回-1
int listen(int __fd, int __n)
  • __fd:希望进入等待连接请求状态的套接字文件描述符,传递的描述符套接字参数成为服务器端套接字(监听套接字)。
  • __n:连接请求等待队列的长度。

第四步:调用accept函数受理连接请求

//成功时返回0,失败时返回-1
int accept(int __fd, struct sockaddr *__restrict__ __addr, socklen_t *__restrict__ __addr_len)
  • __fd:服务器套接字的文件描述符。
  • __addr:保存发起连接请求的客户端地址信息地变量地址值,调用函数后向传递来的地址变量参数填充客户端的地址信息。
  • __addr_len:第二个参数__addr结构体的长度,但是存有长度的变量地址。函数调用完后,该变量即被填入客户端地址长度。

客户端创建套接字过程

第一步:调用socket函数创建套接字

//成功时返回文件表述符,失败时返回-1
int socket(int __domain, int __type, int __protocol)

第二步:调用socket函数向服务器端发送连接请求

//成功时返回0,失败时返回-1
int connect(int __fd, const struct sockaddr *__addr, socklen_t __len)
  • __fd:客户端套接字文件表述符。
  • __addr:保存目标服务器端地址信息的变量地址值。
  • __len:以字节为单位传递已传递给第二个结构体参数__addr地址变量长度。

客户端调用connect函数后服务端接收连接请求(记录到等待队列)或发生断网等异常情况而中断连接请求才会返回(完成函数调用)。客户端的IP地址和端口号在调用connect函数时由内核自动分配,无需调用标记的bind函数进行分配。



总体流程就是:服务器端创建套接字后联系调用bind、listen函数进入等待状态,客户端通过调用connect函数发起连接请求,需要注意的是,客户端只能等到服务器端调用listen函数后才能调用connect函数。同时要清楚,客户端调用connect前,服务器端可能先调用了accept函数。当然,此时服务器端在调用accept函数时进入了阻塞状态,直到客户端调用connect函数为止。

TCP套接字中的I/O缓冲

  • I/O缓冲在每个TCP套接字中单独存在;
  • I/O缓冲在创建套接字时自动生成;
  • 即使关闭套接字也会继续传递输出缓冲中遗留的数据;
  • 关闭套接字将丢失输入缓冲中的数据;

编写

echosrv.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> int main(int argc, char** argv) {
// 1. 创建套接字
int listenfd;
//协议族 套接字类型 协议类型
if ((listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
} // 2. 分配套接字地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton("127.0.0.1", &servaddr.sin_addr); int on = 1;
// 确保time_wait状态下同一端口仍可使用
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
{
perror("setsockopt");
} // 3. 绑定套接字地址
if (bind(listenfd, (struct sockaddr*) &servaddr, sizeof servaddr) < 0) {
perror("bind");
}
// 4. 等待连接请求状态
if (listen(listenfd, SOMAXCONN) < 0) {
perror("listen");
}
// 5. 允许连接
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof peeraddr;
int connfd;
if ((connfd = accept(listenfd, (struct sockaddr *) &peeraddr, &peerlen)) < 0) {
perror("accept");
} printf("id = %s, ", inet_ntoa(peeraddr.sin_addr));
printf("port = %d\n", ntohs(peeraddr.sin_port)); // 6. 数据交换
char recvbuf[1024];
while (1)
{
memset(recvbuf, 0, sizeof recvbuf);
int ret = read(connfd, recvbuf, sizeof recvbuf);
if (ret == 0)
{
printf("client close\n");
break;
} else if (ret == -1)
{
perror("read");
}
fputs(recvbuf, stdout);
write(connfd, recvbuf, ret);
} // 7. 断开连接
close(connfd);
close(listenfd);
return 0;
}

echocli.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> int main()
{
// 1. 创建套接字
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
} // 2. 分配套接字地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
// servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton("127.0.0.1", &servaddr.sin_addr); // 3. 请求链接
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0) {
perror("connect");
} // 4. 数据交换
char recvbuf[1024] = {0};
char sendbuf[1024] = {0};
while (fgets(sendbuf, sizeof sendbuf, stdin) != NULL) // 键盘输入获取
{
// memset(recvbuf, 0, sizeof recvbuf);
// memset(sendbuf, 0, sizeof sendbuf);
write(sockfd, sendbuf, sizeof sendbuf); // 写入服务器
int ret = read(sockfd, recvbuf, sizeof recvbuf); // 服务器读取
if (ret == 0)
{
printf("server close\n");
break;
} else if (ret == -1)
{
perror("read");
}
fputs(recvbuf, stdout); // 服务器返回数据输出 // 清空
memset(recvbuf, 0, sizeof recvbuf);
memset(sendbuf, 0, sizeof sendbuf);
} // 5. 断开连接
close(sockfd); return 0;
}

实现基于TCP的服务端/客户端的更多相关文章

  1. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  2. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流(stream)的套接字.TCP是Transmissi ...

  3. TCP/IP网络编程之基于UDP的服务端/客户端

    理解UDP 在之前学习TCP的过程中,我们还了解了TCP/IP协议栈.在四层TCP/IP模型中,传输层分为TCP和UDP这两种.数据交换过程可以分为通过TCP套接字完成的TCP方式和通过UDP套接字完 ...

  4. TCP Socket服务端客户端(二)

    本文服务端客户端封装代码转自https://blog.csdn.net/zhujunxxxxx/article/details/44258719,并作了简单的修改. 1)服务端 此类主要处理服务端相关 ...

  5. 基于JAX-WS的Web Service服务端/客户端 ;JAX-WS + Spring 开发webservice

    一.基于JAX-WS的Web Service服务端/客户端 下面描述的是在main函数中使用JAX-WS的Web Service的方法,不是在web工程里访问,在web工程里访问,参加第二节. JAX ...

  6. JAVA WEBSERVICE服务端&客户端的配置及调用(基于JDK)

    前言:我之前是从事C#开发的,因公司项目目前转战JAVA&ANDROID开发,由于对JAVA的各种不了解,遇到的也是重重困难.目前在做WEBSERVICE提供数据支持,看了网上相关大片的资料也 ...

  7. 基于SignalR的服务端和客户端通讯处理

    SignalR是一个.NET Core/.NET Framework的实时通讯的框架,一般应用在ASP.NET上,当然也可以应用在Winform上实现服务端和客户端的消息通讯,本篇随笔主要基于Sign ...

  8. 手写内网穿透服务端客户端(NAT穿透)原理及实现

    Hello,I'm Shendi. 这天心血来潮,决定做一个内网穿透的软件. 用过花生壳等软件的就知道内网穿透是个啥,干嘛用的了. 我们如果有服务器(比如tomcat),实际上我们在电脑上开启了服务器 ...

  9. react服务端/客户端,同构代码心得

    FKP-REST是一套全栈javascript框架   react服务端/客户端,同构代码心得 作者:webkixi react服务端/客户端,同构代码心得 服务端,客户端同构一套代码,大前端的梦想, ...

  10. NTP时间同步 服务端 客户端 自动化安装配置

    NTP时间同步 服务端 客户端 自动化安装配置 原创内容 http://www.cnblogs.com/elvi/p/7657994.html #!/bin/sh #运行环境 centos6.cent ...

随机推荐

  1. 解决webservice接口调用报错:java.lang.ClassFormatError: Absent Code ... javax/mail/internet/MimeMultip

    今天使用java axis调用.net发布的webservice接口报了个错,排查半天,感觉代码逻辑没问题,最后发现是jar包冲突!!! 调用接口相关代码: String url="http ...

  2. SpringBoot整合aspectj实现面向切面编程(即AOP)

    前言 "面向切面编程",这样的名字并不是非常容易理解,且容易产生一些误导.但在实际业务中,AOP有着广泛的用途,比如日志记录,性能统计,安全控制,事务处理,异常处理等等. 举些栗子 ...

  3. Ubuntu常用工具和问题整理

    安装Ubuntu虚拟机时常会遇到的几个问题 1.安装时设置镜像 安装Ubuntu系统时设置国内镜像可以加快安装速度:http://mirrors.aliyun.com/ubuntu/ 参考:ubunt ...

  4. CXP2.0的相机是否可以使用CXP1.1的Grabber

    可以 答案是肯定的. 目前CXP共有2个发布版本: 2011年发布CXP1.1 2021年发布CXP2.1,向后兼容,新标准增加了同步功能.数据率放大了一倍. 只要是符合CXP标准.接插件匹配,那么C ...

  5. php的php-fpm

    FastCgi与PHP-fpm到底是个什么样的关系 昨晚有一位某知名在线教育的大佬问了我一个问题,你知道php-fpm和cgi之间的关系吗?作为了一个5年的phper了,这个还不是很简单的问题,然后我 ...

  6. Android Studio 有关 setOnClickListener() 方法的总结

    •前言 在 Android Studio 开发中,你会经常和这种代码打交道: 1 package com.example.activitytest; 2 public class FirstActiv ...

  7. notion database 必知必会

    notion database 必知必会 用过 mysql 的同学一定很容易上手 notion .在 notion 中,掌握好 database,基本上就掌握了 notion 最核心的概念. noti ...

  8. vscode 批量函数前加const 正则表达式替换

    需求 有200多个函数,我要在前面加const devService_importCertificate_result = function (args) { 方案 vscode 替换 打开正则的图标 ...

  9. 摆脱鼠标操作 - vscode - vim Ctrl + hjkl 我定位为 上下翻半页和行头行尾

    为什么 摆脱鼠标操作 - vscode - vim Ctrl + hjkl 我定位为 上下翻半页和行头行尾 翻页和当前行的开头结尾 我觉得都是高频操作 实现 settings.json 还附加了几个其 ...

  10. aardio 嵌入 其他应用程序

    aardio 嵌入 其他应用程序 需求 这个chrome壳不能进行拖拽和缩放,所以再套一个壳,可以再分屏的时候用 import win.ui; /*DSG{{*/ winform = win.form ...