参考:socket是什么?套接字是什么?;Unix网络编程;socket文件描述符
文章中还有。。。。就不列出来了。
本文是对网上博客内容的一些摘抄与总结,谢谢各位的文章供我学习入门,侵删!

文章目录

  • 1.关于socket
  • 2.核心流程
    • 2.1.创建套接字socket()
    • 2.2.绑定地址bind()
      • 2.21.大小端字节序/网络、主机字节序
    • 2.4.系统监听 listen()
    • 2.5.主动连接connect()
    • 2.6.接受连接accept()
    • 2.7.写读write and read 函数
    • 2.8. 关闭连接close()
  • 3.关于服务器端两个socket的解释

1.关于socket

  socket 原意为“插座”,可理解为电器插入插座便通上电,在网络编程中,socket被翻译为套接字,可理解为计算机通过它可以连接上因特网。
  在Unix/linux中我们知道,万物皆为文件,具体地说,不同种类的类型都被抽象为文件,例如:普通文件,字符设备,块设备,套接字,进程等等。当一个文件被进程打开时,系统会为其创建一个文件描述符fd,这个fd是个整数,这时,文件的路径就成为了寻址系统,文件描述符则成为了字节流的接口。例如:用0表示标准输入文件,其对应的硬件设备为键盘;用1表示标准输出文件,其对应的硬件设备为显示器。
  UNIX/Linux 程序在执行任何形式的 I/O 操作时,都是在读取或者写入一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数,它的背后可能是一个硬盘上的普通文件、FIFO、管道、终端、键盘、显示器,甚至是一个网络连接。
  文件是应用程序与系统(包括特定硬件设备)之间的桥梁,而文件描述符就是应用程序使用这个“桥梁”的接口。在需要的时候,应用程序会向系统申请一个文件,然后将文件的描述符返回供程序使用。返回socket的文件通常被创建在/tmp或者/usr/tmp中。我们实际上不用关心这些文件,仅仅能够利用返回的socket描述符就可以了。
   相对于普通文件这类真实存在于文件系统中的文件,tcp socket、unix domain socket等这些存在于内存中的特殊文件在被进程打开的时候,也会创建文件描述符。所以"一切皆文件"更准确的描述应该是"一切皆文件描述符"

2.核心流程

2.1.创建套接字socket()

  如上所述,网络连接也是一个文件,它也有文件描述符,可以通过socket()函数创建一个网络连接,其返回值就是文件描述符,有了它,我们就可以用普通的文件操作函数来传输数据了,例如用 read() 读取从远程计算机传来的数据,用 write() 向远程计算机写入数据。
  用于创建一个新的socket,用于客户端和服务端。成功返回一个文件描述符,失败返回-1。(linux中不记得函数形式可用man socket 查看参数及头文件)

int socket (int domain, int type, int protocol);
  • domain:协议簇,可理解为协议的种类,比如网际协议tcp/ip等。
  • type:因为不同的协议提供了不同的数据传输方式,常见的有面向连接的流式传输模式(SOCK_STREAM),顺序,可靠,双向;数据报(SOCK_DGRAM),定长,不可靠。
  • protocol:这里才是具体的协议类型,如tcp,udp。
2.2.绑定地址bind()

  其主要作用是将由socket函数创建的文件描述符和一个本地地址结构体绑定起来。通俗点说,即给新买的手机插上电话卡。成功返回0,失败返回-1,其函数原型为:

int bind (int socketfd, const struct sockaddr *my_addr, socklen_t addrlen)

第一个参数为调用socket函数返回的文件描述符。
  其中,第二个参数即为我们想要绑定的地址,可看到其为一个结构体指针,指向sockaddr这结构体。这个sockaddr为通用的套接字地址,其类型定义为:

上述结构体在sa_data中包含了ip,port等信息,考虑到系统的兼容性,一般采用另外一个结构体(struct sockaddr_in)来代替,这个结构体描述了internet环境下的地址形式(可以认为,sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体):

注意到,上述结构体中嵌套了一个结构体

这点在具体程序实现中会碰到。
  因为我们在tcpip中所需的地址及端口信息都为internet环境下的,所以先需要sockaddr_in存放ip地址和端口信息,后面在调用bind函数时将一个sockaddr_in{}类型的对象强制转为sockaddr{}类型,再赋值给bind的第二个参数。这两个函数的区别,具体可参考:sockaddr和sockaddr_in详解
struct sockaddr与struct sockaddr_in的区别和联系

结构体成员分析
sin_family:协议簇
sin_port:16位端口号
sin_addr:32位地址信息,以网络字节序保存
sin_zero:是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。用memset函数写入0进行初始化即可。
void *memset(void *s,int c,size_t n)
总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。linux可用man memset查看参数及头文件。

下面说明以下在这个过程中用到的具体的函数
注意:port为unsigned short 16位,addr为 unsigned long 32位,据此选择对应的函数即可。

2.21.大小端字节序/网络、主机字节序

具体可参考文章:网络字节序和主机字节序,网络字节序和主机字节序
网络字节序和主机字节序详解!!!
  不同的机器有不同的字节序类型。考虑一个16位整数,由2个字节组成,内存中存储这个整数的顺序有两种,一种是将低序字节存储在起始地址,称为小端(little-endian)字节序;另一种方法是将高序字节存储在起始地址,称为大端(big-endian)字节序。
这两种字节序没有统一的标准,两种格式都有机器在使用,比如,Inter x86、ARM核采用的是小端模式,Power PC、MIPS UNIX和HP-PA UNIX采用大端模式。
  在数据传输过程中,一定有一个标准化的过程,例如安卓手机充电器接口。也就是说,主机a到主机b的通信,一定是服从:

网络字节序:是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用大端排序方式
主机字节序:特定主机内的内存的数据处理方式。
对应过来,就有:

第三个参数addrlen是指第二个参数在sockaddr{}类型下的实际长度,可用sizeof函数。

2.4.系统监听 listen()

一般在基于流式套接字的服务中会有这一步?,成功返回0,失败返回-1,函数原型为;

int listen(int listenfd, int backlog)

该函数的主要作用是将sockfd变为被动的连接的监听套接字
第一个参数:一般来说,socket函数可以创建一个套接字。默认情况,内核会认为socket函数创建的套接字是主动套接字(active socket),它存在于一个连接的客户端。而服务器调用listen函数告诉内核,该套接字是被服务器而不是客户端使用的,即listen函数将一个主动套接字转化为监听套接字(以 listenfd 表示)。监听套接字可以接受来自客户端的连接请求。关于主动套接字和被动套接字的区别:监听套接字与已连接套接字
第二个参数:backlog指明那些已经经过了TCP三次握手的处于established状态的连接项在被系统调度前的最大排队等候数。换句话说,典型的服务器程序可以同时服务于多个客户端,服务器端调用listen函数来声明listenfd处于监听状态,并且最多允许有backlog个tcp连接。

2.5.主动连接connect()

  该函数用于客户端主动向服务器发起连接,成功返回0,失败返回-1.函数原型为:

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

参数情况与前面类似,sockfd是由socket函数返回的套接字描述符,第二个、第三个参数分别是指向一个套接字地址结构的指针和该结构的大小。
对于TCP套接字,在调用该函数时会激发tcp三次握手的过程。

2.6.接受连接accept()

  其作用是返回一个新的套接字的文件描述符来和客户端通信,serv_addr保存了客户端的IP地址和端口号,而 listenfd 是服务器端的套接字。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。成功返回0,失败返回-1,函数原型为:

int accept(int listenfd, struct sockaddr *addr,int *addrlen)

当accept被调用时,服务器程序会一直阻塞,直到有一个客户端发起连接。accept成功时,返回最后的服务器端的文件描述符,失败返回-1。

2.7.写读write and read 函数

可通过man read 查看函数参数和头文件。其函数原型如下:

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

  收发数据的套接字内部有缓冲(buffer), 简言之就是字节数组. 通过套接字传输的数据将保存到该数组。因此, 我们 read、write其实是读取缓冲区的内容。
  这两个函数分别由fd指定的socket套接口发送(接收)count字节的数据,然后存在buf缓冲区里面。返回值为实际发送成功的字节数。

2.8. 关闭连接close()

调用该函数时对于TCP编程而言会触发一个FIN报文导致连接关闭。

3.关于服务器端两个socket的解释

可参考文章:为什么有监听socket和连接socket,为什么产生两个socket
从5中的描述上可以看出,accpet生成一个新的socket连接,返回该socket的文件描述符。对服务端来说,有两个socket,一个是用于监听的socket,还有一个就是客户端连接成功后,由accept函数创建的用于与客户端收发报文的socket。我觉得上面的文章总结的很到位:职责分工, 分层协作, 提高服务端性能
基于上面的描述,我们就可以实现一个基本的tcp的客户端与服务端通信的代码。(新手注意不要在if语句后面加分号。可用printf函数或者gdb一步一步调试代码)
服务端:

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include <netdb.h>
#include <string.h>int main()
{int listenfd;if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket");return -1;}// printf("socket creat success\n");struct sockaddr_in servaddr;  //通过 man 7 ip 查看,按住键盘下,查看隐藏内容;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");if(bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){perror("bind");return -1;}// printf("bind success\n");if(listen(listenfd,5) < 0){perror("listen");return -1;}// printf("listen success\n");
//定义对方的地址struct sockaddr_in peeraddr;socklen_t peerlen  = sizeof(peeraddr);int conn;if((conn=accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen))<0){perror("accept");return -1;}char recvbuf[1024];while(1){memset(recvbuf, 0, sizeof(recvbuf));int ret = read(conn, recvbuf, sizeof(recvbuf));//printf("%d",ret);fputs(recvbuf, stdout);write(conn, recvbuf, ret);}close(listenfd);close(conn);return 0;
}

客户端:

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include <netdb.h>
#include <string.h>int main()
{int sock;if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ){perror("socket");return -1;}struct sockaddr_in servaddr;  //通过 man 7 ip 查看,按住键盘下,查看隐藏内容;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(6666);// servaddr.sin_addr = hton1(INADDR_ANY);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");if(connect(sock,(struct sockaddr*)&servaddr, sizeof(servaddr))<0){perror("connect");return -1;}char sendbuf[1024] = {0};char recvbuf[1024] = {0};while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL){write(sock, sendbuf, strlen(sendbuf));read(sock, recvbuf, sizeof(recvbuf));fputs(recvbuf,stdout);}close(sock);return 0;
}

makefile文件

all: serve clientserve:serve.cgcc -o serve serve.c
client:client.cgcc -o client client.c

Tcp ip 网络编程入门(一)相关推荐

  1. 高等学校计算机科学与技术教材:tcp/ip网络编程技术基础,TCP/IP网络编程技术基础...

    TCP/IP网络编程技术基础 语音 编辑 锁定 讨论 上传视频 <TCP/IP网络编程技术基础>是2012年北京交通大学出版社出版的图书,作者是王雷. 书    名 TCP/IP网络编程技 ...

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

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

  3. TCP/IP网络编程(3)

    基于DUP的服务端与客户端 在TCP/IP网络编程(2)中,介绍了TCP/IP的四层模型,传输层分为TCP和UDP两种方式,通过TCP套接字完成数据交换已经进行了介绍,下面介绍通过UDP套接字完成数据 ...

  4. TCP/IP网络编程(1)

    1. 套接字 套接字是由操作系统提供的网络数据通信软件设备,即使对网络数据传输原理不了解,也能够使用套接字完成网络数据传输.为了与远程计算机进行数据传输,需要连接到英特网,套接字就是进行网络连接的工具 ...

  5. 《TCP/IP网络编程》第20章

    <TCP/IP网络编程>第20章 同步方法分类及CRITICAL_SECTION同步 用户模式(User mode)和内核模式(Kernal mode) 用户模式同步 内核模式同步 基于C ...

  6. TCP/IP网络编程之多进程服务端(一)

    TCP/IP网络编程之多进程服务端(一) 进程概念及应用 我们知道,监听套接字会有一个等待队列,里面存放着不同客户端的连接请求,如果有一百个客户端,每个客户端的请求处理是0.5s,第一个客户端当然不会 ...

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

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  8. TCP/IP网络编程之多进程服务端(二)

    TCP/IP网络编程之多进程服务端(二) 信号处理 本章接上一章TCP/IP网络编程之多进程服务端(一),在上一章中,我们介绍了进程的创建和销毁,以及如何销毁僵尸进程.前面我们讲过,waitpid是非 ...

  9. 网络编程+go+java,Go语言中的TCP/IP网络编程

    Go语言TCP/IP网络编程 乍一看,通过TCP/IP层连接两个进程会感觉可怕, 但是在Go语言中可能比你想象的要简单的多. TCP/IP层发送数据的应用场景 当然很多情况下,不是大多数情况下,使用更 ...

  10. tcp/ip网络编程--accept()函数返回的套接字

    tcp/ip网络编程–accept()函数返回的套接字 套接字:1)套接字是对网络中不同主机的应用进程之间进行双向通信的端点的抽象:一个套接字就是网络进程通信的一端.[1] 2)套接字是用来与另一个进 ...

最新文章

  1. 哪家互联网公司涨薪最厉害?居然不是阿里腾讯
  2. Xtrabackup备份到远程服务器
  3. mysql取出数据外键_mysql数据表有外键,应注意的几点(目前学习所获得的经验)...
  4. 机器人鸣人是哪一集_火影里的五个机器人,第一个比鸣人还厉害,机器丁次你都没见过...
  5. 文秘专业计算机基础考题,《计算机应用基础》课程无纸化试题库建设及应用分析...
  6. Effective C++ 条款21
  7. 使用Ping命令解析主机名解析出来的是IPv6
  8. python @修饰符_Python修饰符,返回替换了一个或多个参数的函数
  9. CSS3 Transitions, Transforms和Animation的使用
  10. GCD和NSThread延时执行对比
  11. 华为郭平:很愿意使用高通芯片制造手机
  12. JupyterHub与OpenLDAP集成
  13. EDA技术实用教程 | 复习三 | 不同类型的赋值语句
  14. CANoe 13 demo 下载和激活-转载
  15. 100m网速测试软件,网速测试哦(100兆宽带wifi最佳设置)
  16. MATLAB 符号运算
  17. 报表工具选型对比系列 - 多源关联性能
  18. 打开Idea,弹出Server‘s certificate is not trusted 解决方法
  19. mybatis-基本架构
  20. WinRAR v5.71 简体中文正式版

热门文章

  1. HDFS之存储优化纠删码原理、纠删码案例实操 、异构存储(冷热数据分离)
  2. 如何解决PDF图片字体未嵌入的问题
  3. 浅析HashMap底层原理
  4. 28-地理空间数据云下载
  5. 深入理解 Spring 事务原理
  6. js利用CLodop实现打印功能
  7. bcrypt加密工具
  8. python同构数_怎么用python判断一个数是否是同构数?
  9. 电子计算机与媒体阅读答案,电子计算机与多媒体课课练.docx
  10. 第五次作业:Excel制作英文课程表