目录

网络初识

1、网络协议初识

2、网络协议的分层:

OSI分层模型--->网络理论模型:

TCP/IOP五层模型--->工业中采用的网络模型

为什么要有网络分层?

3、网络数据的封装与复用

4、IP地址和MAC的地址

IP地址

MAC地址

套接字编程

1、预备知识

1、认识端口--->port

2、网络数据的五元组信息

3、网络字节序

4、主机字节序与网络字节序的互相转化

5、TCP协议与UDP协议的特性和区别

2、UDP_socket编程

1、流程

2、接口:

3、代码实现(注意其中的一些细节问题)

3、TCP_socket编程

1、流程

2、接口:

3、代码


网络初识

1、网络协议初识

协议:约定沟通双方传递信息的格式

网络完成的事:将数据从主机A的a进程传递到主机B的b进程。

网络协议:约定网络主机在传输数据时候的格式。

①网络数据 = 应用层数据 + 协议部分

  • 在网络中的数据不仅仅只有数据本身,还需要有协议的内容,协议的内容帮助要传输的数据正确的在网络当中传输到对方的主机上。

②基于操作系统和库函数写出来的程序,都是应用层程序,产生的数据称之为应用层数据。

③协议部分,在Linux操作系统中也是采用描述的手法,描述的本质就是一个结构体。换句话说,协议部分的内容就是结构体数据。

2、网络协议的分层:

OSI分层模型--->网络理论模型:

TCP/IOP五层模型--->工业中采用的网络模型

从顶层到底层分别为:应用层、传输层、网络层、数据链路层、物理层

为什么要有网络分层?

1、从软件方面考虑:

将网络的功能解耦开来,有负责应用层数据、有负责端与端之间的传输,有负责路由等等。

2、从实现层面讲:

分层当中各个协议完成各自的协议功能即可,只需要将不同层之间互相通信的接口设计匹配就行。至于每一层的内部实现什么功能,如何实现,这些都可以单独考虑,也就便于实现。

3、网络数据的封装与复用

结论:

1、应用层数据经过网络传输的时候,需要经过网络协议栈的封装,到达对端之后,需要经过网络协议栈的分用。

2、网络协议栈封装的时候,是增加了协议的内容。目的是为了在网络中能够正确传输。

4、IP地址和MAC的地址

IP地址

分类:IPv4和IPv6两个版本的IP地址

本质:IPv4版本的IP地址,本质上是一个无符号的32位整数,范围是[0, 2^32 - 1]

作用:在网络中标识一台主机

表现形式:通常使用“点分十进制”的字符串表示IP地址,例如192.168.0.1;用点分割的每一个数字表示一个字节,范围是0 - 255。

注意:

  • 一个IP地址只能被一台机器占用
  • 一台机器可以占有多个IP地址

MAC地址

本质:长度为48比特位即为6字节,一般使用16进制数据加上冒号的方式来表示。(例如:08:89:24:fc:19:02)

作用:

  • 标识具体的某一块物理网卡设备,网卡设备在出厂的时候,都会打上全球独一无二的MAC地址
  • MAC地址用来标识数据链路层中相连的节点

使用ifconfig命令查看自己的Linux机器的网卡信息:

套接字编程

1、预备知识

1、认识端口--->port

本质:端口号是一个2字节16位的无符号整数,范围是[0,65535]

作用:端口号是用来标记一个进程,告诉操作系统,当前的数据要交给哪一个进程来处理

注意:

  • 一个端口只能被一个进程占用
  • 一个进程可以占用多个端口
  • [0,1023]范围内的端口已经被一些知名的协议所使用,我们在编写代码的时候不要使用该范围内的数据作为端口号。

2、网络数据的五元组信息

名称 作用
源IP 标识网络数据是从那一台主机发出的

目的IP

标识数据要去往哪一台主句
源端口 标识网络数据是从“源端口”对应的这台主机的哪个进程产生的
目的端口 通过目的IP找到目的主机之后,需要利用目的端口找到对应的进程
协议 标识双方传输数据时使用的协议

3、网络字节序

字节序又称端序或者位序。指的是多字节数据在内存中存放的顺序。

我们接触的字节序分为两类:小端和大端

主机字节序:指的是机器本身的字节序。

网络字节序:规定网络传输数据的时候采用大端字节序进行传输

4、主机字节序与网络字节序的互相转化

既然网络字节序是大端字节序,现在假设有AB两台主机,他们之间需要通过网络进行通信,我们分析A向B发送消息这一过程。

A向B发送数据,通过网络传输时一定要转换为网络字节序,否则传输的数据可能会出错(这取决于主机A是大端还是小端机器)

B从网络中接收A发送的数据时,也需要将数据从网络字节序转换为B主机的主机字节序

操作系统提供了转化的接口:

主机字节序--->网络字节序:

ip:uint32_t

uint32_t htol(uint32_t hostlong)

port:unit16_t

unit16_t htos(uint16_t hostshort)

网络字节序--->主机字节序:

ip:unit32_t

unit32_t ntohl(unit32_t netlong)

port:uint16_t

uint16_t ntohs(unit16_t netshort)

5、TCP协议与UDP协议的特性和区别

UDP:无连接,不可靠,面向数据报

无连接:UDP双方在发送数据之前,是不需要进行沟通的。只需要知道对方的IP和端口(不关心对方进程是否准备好通信),就可以通信。

不可靠:不保证UDP数据是可靠、有序的到达对方。

面向数据报:UDP在和应用层或者网络层递交数据的时候,都是整条数据进行交付的

TCP:面向连接、可靠传输、面向字节流

面向连接:TCP双方在发送数据之前先会建立连接

可靠传输:TCP保证传输的数据是可靠、有序的到达对端的

面向字节流:对于传输的数据没有明显的边界;对于接收方而言,可以按照任意的字节进行接收;好处:提升了传输效率。存在的问题:TCP粘包问题

2、UDP_socket编程

1、流程

服务端:

  1. 创建套接字
  2. 绑定地址信息
  3. 收发消息
  4. 使用完毕后关闭套接字

客户端:

  1. 创建套接字
  2. 不推荐绑定地址信息(不推荐在代码手动绑定地址信息),一个端口之能被一个进程占用,防止出现多个进程绑定同一个端口。
  3. 收发消息
  4. 使用完毕后关闭套接字

如图所示:

1、为什么要创建套接字?

将进程和网卡进行绑定,进程可以从网卡中接收消息,也可以通过网卡发送消息。

2、绑定地址信息具体干了什么?

绑定IP和端口。目的是为了在网络中表示一台主机和一个进程。这样一来,对于接收方而言,发送数据的人就知道接收方在哪台机器哪个进程了;对于发送方而言,能够标识网络数据是从哪台机器的哪个进程发送出去的。

2、接口:

1、创建套接字

domain(地址域):

选择一个具体的协议簇进行沟通。对于我们而言,UDP/TCP,可以认为在指定网络层 使用什么协议。具体:

AF_UNIX:本地域套接字,在同一台机器使用 文件进行通信,不用跨机器

AF_INET:IPV4版本的IP协议

AF_INET6:IPV6版本的IP协议

type(套接字的类型):

SOCK_DGRAM:用户数据报套接字-----对应UDP

SOCK_STREAM:流式套接字------对应TCP

protocol(协议):

0:标识按照套接字类型选择默认协议,SOCK_DGRAM-->UDP,SOCK_STREAM->TCP

IPPROTO_TCP:对应数字6,代表TCP协议

IPPROTO_UDP:对应数字17,代表UDP协议

返回值:返回套接字操作句柄,本质上就是一个文件描述符;大于等于0:创建成功;小于0:创建失败

2、绑定接口

sockfd:创建套接字时返回的套接字描述符

addr:绑定的地址信息(IP + port)

addrlen:绑定的地址信息的长度

注意:这里的struct sockaddr是一个通用的数据结构,结构如下:

 我们在组织参数的时候,传递的并不是上面这个通用的数据结构,而是struct sockaddr_in这个结构体变量,具体内容如下

struct sockaddr_in{//地址域sa_family_t sin_family;//端口号uint16_t sin_port;//32为IP地址struct in_addr sin_addr;//预留未使用char sin_zero[8];
}
struct in_addr{in_addr_t s_addr;
}

3、发送接口

sockfd:套接字描述符

buf:要发送的数据

len:要发送数据的长度

flags:0(阻塞发送)

dest_addr:地址信息结构,包含了目的IP,目的端口;表示要把数据发送到哪里去。

addrlen:地址信息的长度

返回值:

成功:返回正常发送的数据

失败:返回-1

4、接收接口:

sockfd:套接字描述符

buf:程序员准备的接收数据的缓冲区

len:最大能接收数据的大小,一般为缓冲区的大小

flags:0(阻塞接受)

src_addr:源IP + 源端口

addrlen:是一个出参,返回地址信息的长度

5、关闭接口

int close(int fd)

3、代码实现(注意其中的一些细节问题)

服务端:

 #include<stdio.h>2 #include<sys/socket.h>3 #include <netinet/in.h>4 #include <arpa/inet.h>5 #include<string.h>6 #include<unistd.h>7 int main(){8     /*9      * 1.创建套接字10      * 2.绑定地址信息11      * 3.接受信息12      * 4.发送消息13      * */14 15     int udp_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);16     if(udp_fd < 0){17         perror("socket");18         return 0;19     }20     //绑定地址信息21     struct sockaddr_in addr;22     addr.sin_family = AF_INET;23     addr.sin_port = htons(22222); //这里的端口号必须转化为网络字节序24      /*25      * in_addr_t inet_addr(const char *cp);26      *    1.将点分十进制的ip转化成为无符号的32位整数27      *    2.将无符号32位整数转换成为网络字节序28      * */29     addr.sin_addr.s_addr = inet_addr("0.0.0.0");30     int ret = bind(udp_fd, (struct sockaddr*)&addr, sizeof(addr));31     if(ret < 0){32         perror("bind");33         return 0;34     }35     //接受消息36     char buf[1024];37     struct sockaddr_in rcv_addr;38     socklen_t rcv_addr_len = sizeof(rcv_addr);39     ssize_t rcv_size = recvfrom(udp_fd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&rcv_addr, &rcv_addr_len);40     if(rcv_size < 0){41         return 0;42     }43     /*44      * inet_ntoa:                                                                                                                                                                                                                                                                                                     45      * 1、将unit32位整型数字转换为点分十进制的字符串46      * 2、将网络字节序转化为主机字节序47      * */48 49     printf("%s : %d client send value is: %s\n", inet_ntoa(rcv_addr.sin_addr), ntohs(rcv_addr.sin_port), buf);50     memset(buf, '\0', sizeof(buf));51     sprintf(buf, "hello , I am server");52     //发送消息53     ssize_t send_size = sendto(udp_fd, buf, strlen(buf), 0, (struct sockaddr*)&rcv_addr, sizeof(rcv_addr));54     if(send_size < 0){55         return 0;56     }57     close(udp_fd);58     return 0;59 }

客户端:

#include<stdio.h>2 #include<sys/socket.h>3 #include<arpa/inet.h>4 #include<netinet/in.h>5 #include<string.h>6 #include<unistd.h>7 int main()8 {9     /*10      *1、创建套接字11      *2、发送消息12      *3、接收消息13      * */14     int udp_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);15     if(udp_fd < 0)16     {17         perror("socket");18         return 0;19     }20     //发送消息21     struct sockaddr_in addr;22     addr.sin_family = AF_INET;                                                                                                                                                                                                                                           23     addr.sin_port = htons(22222);24     addr.sin_addr.s_addr = inet_addr("10.0.24.13");25 26     const char* str = "Hello, I am udp_client!";27     ssize_t send_size = sendto(udp_fd, str, strlen(str), 0 ,(struct sockaddr*)&addr, sizeof(addr));28     if(send_size < 0)29     {30         perror("sendto");31         return 0;32     }33     //接收消息34     char buf[1024] = {0};35     struct sockaddr_in serve_addr;36     socklen_t serve_addrlen = sizeof(serve_addr);37     ssize_t rcv_size = recvfrom(udp_fd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&serve_addr, &serve_addrlen);38     if(rcv_size < 0){39         return 0;40     }41     printf("%s : %d send value is : %s\n", inet_ntoa(serve_addr.sin_addr), ntohs(serve_addr.sin_port), buf);42     close(udp_fd);43     return 0;44  }

查看端口信息:

结果:

3、TCP_socket编程

1、流程

服务端:

创建套接字

绑定地址信息

监听

获取新连接

收发消息

关闭连接

客户端:

创建套接字

不推荐绑定地址信息

发起连接

收发数据

关闭连接

如图:

 监听的含义:

监听TCP客户端新的连接,同客户端建立TCP连接。(此时,TCP的建立在内核中就完成了)

获取新连接的含义:

获取新连接的套接字描述符,每一个TCP连接会产生一个新的套接字描述符。

发起连接:向服务端发起连接

2、接口:

1、监听

sockfd->套接字描述符:一般为服务端创建套接字时产生的套接字描述符

backlog(TCP并发连接数,也就是一瞬间可以建立多少连接):

  • Linux2.2版本之前:未完成连接请求的数量
  • Linux2.2之后:TCP并发连接数即已完成连接的大小。

未完成连接的队列:还处于建立连接的连接被放到这个队列中,可以理解为正在3次握手的连接在该队列中。

已完成连接的队列:连接已经建立,可以正常通信的连接放在这个队列中,可以理解为三次握手完毕的连接在该队列中。

可以通过修改/proc/sys/net/ipv4/tcp_max_syn_backlog当中的值,修改未完成连接队列的大小。

返回值:

成功返回0,失败返回-1。

2、获取新连接

sockfd:套接字描述符

addr:地址信息结构体,用来描述客户端地址信息

addrlen:地址信息长度

返回值:成功返回新连接的套接字,失败返回-1

注意:

该接口具有阻塞属性

  1. 如果已完成连接队列中没有已经建立的连接,则阻塞
  2. 如果有,获取新连接后返回。

注意:

  1. 返回的新连接的套接字,是为了和客户端进行通信的,只不过这个套接字没有监听功能,同时有客户端的地址信息。
  2. 多个客户端发起连接,在服务端会创建多个新连接的套接字。
  3. 服务端使用socket创建的套接字描述符,是一个侦听套接字,主要责任就是侦听是否有新的连接到来;服务端使用accept创建出来的套接字,被称为新连接套接字,主要责任就是同客户端通信。

3、发起连接

sockfd:套接字描述符,一般为客户端创建套接字时的返回值

addr:地址信息结构,描述服务端的地址信息(服务端的IP和port)

addrlen:四肢信息长度

返回值:成功返回0,失败返回-1

该函数不仅可以完成连接功能。如果客户没有进行绑定,同时也会可定客户端的地址信息。

4、接收数据

sockfd:套接字描述符,谁接收就传谁的描述符

buf:将接收的数据存放在buf指定的空间

len:期望接收的字节个数

flags:0(阻塞接收)

返回值:成功收到字节的数量;失败:0,对端关闭连接了;-1,接收错误。

注意:

返回值为0表示对端关闭连接了,如果此时的对端指的是客户端,则服务端需要将对端的新套接字描述符关闭。

5、发送数据

sockfd:套接字描述符(服务端的话,传递的是新创建的套接字描述符)

buf:发送buf指向的空间的内容

len:数据长度

flags:0(阻塞发送)

返回值:成功,返回发送的字节数量;失败,返回-1。

3、代码

要求:服务端可以和多个客户端之间能够正常通信。

服务端:

 #include<stdio.h>                                                                                                                                                                                                                                                                                                                                                                    2 #include<unistd.h>3 #include<sys/socket.h>4 #include<arpa/inet.h>5 #include<netinet/in.h>6 #include<string.h>7 int main(){8     /*9      * 1、创建服务端套接字(侦听套接字)10      * 2、绑定地址信息11      * 3、监听12      * 4、获取新连接13      * 5、收发消息14      * */15     int listen_fd = socket(AF_INET, SOCK_STREAM, 0);16     if(listen_fd < 0){17         perror("socket");18         return 0;19     }20     struct sockaddr_in addr;21     addr.sin_family = AF_INET;22     addr.sin_port = htons(22222);23     addr.sin_addr.s_addr = inet_addr("0.0.0.0");24 25     int ret = bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));26     if(ret < 0){27         perror("bind");28         return 0;29     }30     //监听31     ret = listen(listen_fd, 5);32     if(ret < 0){33         perror("listen");34         return 0;35     }36     //接收新连接37     struct sockaddr_in cli_addr;38     socklen_t cli_addr_len = sizeof(cli_addr);39     int new_sockfd = accept(listen_fd, (struct sockaddr*)&cli_addr, &cli_addr_len);40     if(new_sockfd < 0){41         perror("accept");42         return 0;43     }44     //收发消息45     while(1){46         char buf[1024] = {0};47         int recv_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);48         if(recv_size < 0){49             perror("recv");50             return 0;51         }else if(recv_size == 0){52             //客户端将连接关闭了53             printf("perr shutdown\n");54             close(new_sockfd);55             return 0;56         }57         printf("buf is [%s]\n", buf);58         memset(buf, '\0', sizeof(buf));59         //发送消息60         sprintf(buf, "[%s]:[%d] I am serve, i recv your msg", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));61         send(new_sockfd, buf, strlen(buf), 0);62     }63     close(listen_fd);64     return 0;65 }
~

客户端:

include<stdio.h>2 #include<unistd.h>3 #include<sys/socket.h>4 #include<string.h>5 #include<arpa/inet.h>6 #include<netinet/in.h>7 #include<iostream>8 9 using namespace std;10 int main(){11     /*12      * 1、创建套接字13      * 2、建立连接14      * 3、收发消息15      * */16     int sockfd = socket(AF_INET, SOCK_STREAM, 0);17     if(sockfd < 0){18         perror("socket");19         return 0;20     }21     //connect22     struct sockaddr_in addr;23     addr.sin_family = AF_INET;24     addr.sin_port = htons(22222);25     addr.sin_addr.s_addr = inet_addr("10.0.24.13");26 27     int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));28     if(ret < 0){29         perror("connect");30         return 0;31     }32     while(1){33         char buf[1024] = {0};34         cout << "please enter your msg#" << endl;35         fflush(stdout);36 37         cin >> buf;38         int send_size = send(sockfd, buf, strlen(buf), 0);39         if(send_size < 0){40             perror("send");41             continue;42         }43         //接收消息44         memset(buf, '\0', sizeof(buf));45         int recv_size = recv(sockfd, buf, sizeof(buf) - 1, 0);46         if(recv_size < 0){47             perror("send");48             return 0;49         }else if(recv_size == 0){50             //服务端将连接关闭51             cout << "server shutdown connect" << endl;52         }53         printf("%s\n", buf);54     }55     close(sockfd);                                                                                                                                                                                                                                                                                                                                                                                                                                                                  56     return 0;57 }
~

结果:

 查看当前网络连接状态:

分析原因:

accept和recv接口都具有阻塞属性,对于当前代码来说,服务端在接收到第一个客户端的消息后,循环上来继续阻塞在recv接口处等待接收消息,并不会执行accpet函数,也就不会从已完成连接队列中读取套接字描述符。

如果将accept接口放到循环内部,能否解决问题?

不可以。由于accept和recv都具有阻塞属性,会导致accept阻塞属性影响recv的接收,recv属性会影响accept获取新连接,并且accept获取到的新连接套接字B会覆盖上一次获取到的新连接套接字A。

综上所述,单线程的TCP代码就目前而言只能服务于单个客户端的情况。(后续可以通过多路转接IO模型实现与客户端一对多的情况)

我们可以让服务端的一个进程(线程)只负责与客户端建立连接,剩下的一批进程(线程)可以各自与一个客户端进行沟通。这样就可以达到目标。

②TCP + 多进程

首先,对于客户端的代码而言,不需要作出任何的改动!因为客户端只需要一直和服务端进行通信即可。

主要更改是在服务端,我们通过创建子进程的方式来实现职责的分离,也就是父进程值负责与客户端建立连接,而子进程负责与客户端进行收发消息。

注意:

  1. 子进程是拷贝父进程的PCB,因此需要父进程先与客户端建立连接,也即在父进程的PCB中的fd_array中有了该套接字的文件描述符之后再创建子进程。
  2. 子进程创建成功过,由于它只需要和客户端进行收发消息,因此只需要accpet返回的新套接字描述符即可,所以需要将拷贝自父进程的侦听套接字关闭。
  3. 客户端如果将连接关闭,则子进程需要将对应的套接字即文件描述符关闭,然后该进程需要退出。(退出时,一定要通知父进程来回收子进程的退出状态信息,否则子进程就会变成僵尸进程!但是我们不能采用wait | waitpid来回收。因为wait具有阻塞属性,而waitpid需要搭配循环来使用,均不符合预期。我们可以通过信号量的方式来处理,即改写SIGCHILD信号!)
      #include<stdio.h>                                                                                                                                                                                                                                                                                                                                                                  2 #include<string.h>3 #include<sys/socket.h>4 #include<arpa/inet.h>5 #include<netinet/in.h>6 #include<signal.h>7 #include<unistd.h>8 #include<sys/wait.h>9
W> 10 void signalDeal(int signum){11     wait(NULL);12 }13 int main(){14     signal(SIGCHLD, signalDeal);15     int listen_fd = socket(AF_INET, SOCK_STREAM, 0);16     if(listen_fd < 0){17         perror("socket");18         return 0;19     }20     struct sockaddr_in addr;21     addr.sin_family = AF_INET;22     addr.sin_port = htons(22222);23     addr.sin_addr.s_addr = inet_addr("0.0.0.0");24     int ret = bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));25     if(ret < 0){26         perror("bind");27         return 0;28     }29     ret = listen(listen_fd, 5);30     if(ret < 0){31         perror("listen");32         return 0;33     }34 35     //收发消息36     while(1){37         struct sockaddr_in cli_addr;38         socklen_t cli_addr_len = sizeof(cli_addr);39         int new_sockfd = accept(listen_fd, (struct sockaddr*)&cli_addr,&cli_addr_len);40         if(new_sockfd < 0){41             perror("accept");42             return 0;43         }44         pid_t pid = fork();45         if(pid < 0){46             close(new_sockfd);47             continue;48         }else if(pid == 0){49             //子进程负责与客户端沟通50             close(listen_fd);51             while(1){52                 char buf[1024] = {0};53                 int recv_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);54                 if(recv_size < 0){55                     perror("recv");56                     continue;57                 }else if(recv_size == 0){58                     //客户端将连接关闭59                     printf("perr shutdown!\n");60                     close(new_sockfd);61                     return 0;62                 }63                 printf("[%s]:[%d] say [%s]\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);64                 memset(buf, '\0', sizeof(buf));65                 sprintf(buf, "I am server, I recv your msg");66                 send(new_sockfd, buf, strlen(buf), 0);67             }68         }else{69             close(new_sockfd);70         }71     }72     return 0;73 }~

 ③tcp + 多线程

1 #include<stdio.h>2 #include<pthread.h>3 #include<sys/socket.h>4 #include<arpa/inet.h>5 #include<netinet/in.h>6 #include<string.h>7 #include<unistd.h>8 9 //将新连接描述符和客户端地址信息封装传给线程入口函数10 struct cli_info{11     cli_info()12     :_new_sockfd(-1){13         memset(&addr, '0', sizeof(addr));14     }15     int _new_sockfd;16     struct sockaddr_in addr;17 };18 19 void* tcp_deal_start(void* arg){20     pthread_detach(pthread_self());21     cli_info* ci = (cli_info*)arg;22     int new_sockfd = ci->_new_sockfd;23     struct sockaddr_in cli_addr = ci->addr;24     //收发消息25     while(1){26         char buf[1024] = {0};27         int recv_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);28         if(recv_size < 0){29             perror("recb");30             continue;31         }else if(recv_size == 0){32             //客户端关闭连接33             printf("perr shutdown\n");34             delete ci;35             close(new_sockfd);36             return 0;37         }38         printf("[%s]:[%d] say [%s]\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);39         memset(buf, '\0', sizeof(buf));40         sprintf(buf, "[%s] : [%d] I am serve, I recv your msg", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));41         send(new_sockfd, buf, strlen(buf), 0);42     }43 }44 int main(){45     int listen_fd = socket(AF_INET, SOCK_STREAM, 0);46     if(listen_fd < 0){47         perror("listen");48         return 0;49     }50     struct sockaddr_in addr;51     addr.sin_family = AF_INET;52     addr.sin_port = htons(22222);53     addr.sin_addr.s_addr = inet_addr("0.0.0.0");54     int ret = bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));55     if(ret < 0){56        perror("bind");57        return 0;58     }59     ret = listen(listen_fd, 5);60     if(ret < 0){61         perror("listen");62         return 0;63     }64 65     //收发消息66     while(1){67         struct sockaddr_in cli_addr;68         socklen_t cli_addr_len = sizeof(cli_addr);69         int new_sockfd = accept(listen_fd, (struct sockaddr*)&cli_addr, &cli_addr_len);70         if(new_sockfd < 0){71             continue;72         }73         pthread_t tid;74         cli_info* ci = new cli_info();75         ci->_new_sockfd = new_sockfd;76         memcpy(&ci->addr, &cli_addr, sizeof(cli_addr));77         int ret = pthread_create(&tid, NULL, tcp_deal_start, (void*)ci);78         if(ret < 0){79             close(new_sockfd);80             continue;81         }82     }83     return 0;84 } 

【网络】网络基础套接字编程详解相关推荐

  1. TCP套接字编程详解

    目录 为什么socket编程又叫套接字编程? TCP服务端 初始化套接字库--WSAStarup 创建套接字--socket 绑定到本机 --bind 开始监听 --listen 连接客户端请求--a ...

  2. Socket 套接字原理详解

    Socket 套接字原理详解 socket 编程介绍 Socket编程封装了常见的TCP.UDP操作,可以实现非常方便的网络编程. socket() 函数介绍 # socket.socket(fami ...

  3. 《Unix网络编程》卷一(简介TCP/IP、基础套接字编程)

    通常说函数返回某个错误值,实际上是函数返回值为-1,而全局变量errno被置为指定的常值(即称函数返回这个错误值). exit终止进程,Unix在一个进程终止时总是关闭该进程所有打开的描述符. TCP ...

  4. Socket(套接字)详解 画图+实例

    Socket概念 Socket本意为"插座",在Linux下,用于表示进程间网络通信的特殊文件类型,本质为内核借助缓冲区形成的伪文件. 既然是文件,那肯定就可以使用文件描述符引用套 ...

  5. C# 网络编程之套接字编程基础知识

    最近阅读了周存杰编写的<C#网络编程实例教程>并阅读了很多相关方面的资料,同时自己也做了一些套接字编程方面的C#程序,所以根据它的知识总结了最近的套接字编程的一些知识点,方便自己的理解与他 ...

  6. 计算机网络(二) | 网络编程基础、Socket套接字、UDP和TCP套接字编程

    目录 一.网络编程基础 1.1 为什么需要网络编程 1.2 什么是网络编程 1.3 网络编程中的基本概念 二.Socket套接字 2.1 概念 2.2 分类 2.3 Java数据报套接字通信模型 2. ...

  7. 【Linux】网络基础+UDP网络套接字编程

    只做自己喜欢做的事情,不被社会和时代裹挟着前进,是一件很奢侈的事. 文章目录 一. 网络基础 1.局域网和广域网 2.协议初识和网络协议分层(TCP/IP四层模型) 3.MAC地址和IP地址(子网掩码 ...

  8. 网络基础:套接字编程,UDP和TCP通信程序

    文章目录 字节序 套接字编程 客户端与服务端 ★ netstat命令 ★ UDP通信程序 通信流程: 接口: 流程外的重要接口: 服务端代码 客户端代码 TCP通信程序 通信流程: 接口: 代码实例: ...

  9. 网络编程学习笔记-套接字编程-socket

    本文为了说明例子,用中文作为变量写在了程序里面,一般编程最好不要那么写 前置概念详见 本文目录 概念 基本TCP套接字编程 通信循环 半链接池 链接循环 udp协议 基于TCP协议实现远程执行客户端请 ...

最新文章

  1. 改善C#程序的建议5:引用类型赋值为null与加速垃圾回收
  2. 2019 年 Spread.NET 产品路线图(Roadmap)
  3. 以太网端口类型和可插拔模块揭密
  4. backbone.js入门
  5. 眼图matlab仿真_MATLAB开发自动驾驶第二十课-自动驾驶场景设计器中的预建驾驶场景...
  6. Kaldi 语音识别基础教程
  7. 桌面计算机怎么覆盖文件,恢复被覆盖的文件_恢复被覆盖的桌面文件
  8. C# 客服端上传文件与服务器器端接收 (简单代码)
  9. qt 限制一段时间内对button只能点按一次_299元入手拓牛智能垃圾桶,用第一次想退货,第三天我上瘾了...
  10. python中mod运算符_Python—运算符模块,pythonoperator
  11. (9)二进制文件方式部署Kubernetes高可用集群----------部署master节点
  12. java jre 7_jre1.7官方下载|
  13. 冰点还原界面无法呼出如何解决
  14. 斯坦福大学java视频_iPhone应用开发视频教程-斯坦福大学公开课
  15. html 字体炫彩特效,CSS3:TEXT-SHADOW|BOX-SHADOW(炫彩字体)
  16. VR行业的发展现状和前景
  17. linux进阶-ssh命令的使用技巧全get
  18. 期货反向跟单靠谱吗?
  19. 联手新加坡南洋理工大学,阿里加速推进人工智能战略
  20. Radare2 学习笔记:从入门到精通 1. Radare2 简介,及安装

热门文章

  1. 数据挖掘常用的模型,具体有哪些?
  2. Python的重要优点和缺点是什么
  3. dB 的 由来与详解
  4. python字符串可变吗_python中不可变数据类型和可变数据类型
  5. 经典设计案例:丢猫千万别找设计师
  6. jmeter并发测试(jmeter并发测试步骤)
  7. psql切换schema_PostgreSQL 模式(SCHEMA)
  8. php处理高并发问题
  9. 【汇正财经】什么是证券监管?
  10. 最强聊天机器人ChatGPT面世,AI又来抢饭碗了?