【Linux】系统编程之网络编程(socket)

news2025/8/8 4:24:36

目录

    • 一、Socket编程
      • 1、TCP/UDP
      • (1)TCP/UDP区别
      • (2)TCP/UDP服务端和客户端的通信流程
      • 2、IP地址和端口号
        • (1)IP地址
        • (2)端口号
    • 二、字节序
      • 1、字节系相关概念
      • 2、有关字节序转换的函数
        • (1)主机字节序转换网络字节序函数
        • (2)网络字节序转换主机字节序函数
      • 3、地址转换函数
        • (1)点分十进制IP地址转换为二进制IP地址函数
          • ①函数inet_aton
          • ②函数inet_pton
        • (2)二进制IP地址转换为点分十进制IP地址函数
          • ①函数inet_ntoa
          • ②函数inet_ntop
    • 三、Socket套接字相关函数
      • 1、Socket 编程流程
        • (1)TCP通信流程
        • (2)UDP通信流程
      • 2、Socket 相关函数
        • (1)socket()函数(创建套建字)
        • (2)bind()函数(绑定套建字)
        • (3)listen()函数(监听被绑定的端口)
        • (4)accept()函数(接收连接请求)
        • (5)connect()函数(发送连接请求)
        • (6)send()、recv()函数(TCP发送、接收信息)
          • ①函数send()
          • ②函数recv()
        • (7)sendto()、recvfrom()函数(UDP发送、接收消息)
          • ①函数sendto()
          • ②函数recvfrom()
    • 四、示例

一、Socket编程

在UNIX、Linux系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。

为了表示和区分已经打开的文件,UNIX/Linux会为每个文件分配一个ID,这个文件就是一个整数,被称为文件描述符
例如:
通常用 0 来表示标准输入文件(stdin),它对应的硬件设备就是键盘;
通常用 1 来表示标准输出文件(stdout),它对应的硬件设备就是显示器。

网络连接也是一个文件,它也有文件描述符
我们可以通过 socket() 函数来创建一个网络连接,或者说打开一个网络文件,socket() 的返回值就是文件描述符(注意在windows下的socket返回的叫文件句柄,并不是叫文件描述符)。有了文件描述符,我们就可以使用普通的文件操作函数来传输数据了,例如:
read() 读取从远程计算机传来的数据;
write() 向远程计算机写入数据。

1、TCP/UDP

OSI七层网络模型中,各个层之间都有相对应的协议,这里主要关注的是传输层的协议:TCP/UDP协议

首先标准套接字分为TCP和UDP协议两种不同type的工作流程,TCP网络编程相对于UDP来说相对复杂,因为TCP是面向连接的服务,其中包括三次握手建立连接的过程,而UDP则是无连接的服务。

(1)TCP/UDP区别

(1)TCP面向连接(如打电话要先拨号建立连接),而UDP是无连接的,即发送数据之前不需要建立连接。
(2)TCP提供可靠的服务,也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;而UDP则是尽最大努力进行交付,即不保证可靠交付。
(3)TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;而UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(这样对实时应用很有用,如,IP电话,实时视频会议等)。
(4)每一条的TCP只能是点到点的,UDP支持一对一,一对多,多对一和多对多的交互通信。
(5)TCP首部开销20字节;UDP首部开销小,只有8个字节。
(6)TCP的逻辑通信的信道是全双工的可靠信道,而UDP则是不可靠信道。

(2)TCP/UDP服务端和客户端的通信流程

TCP和UDP的网络编程模式有两种,一种是服务器模式,另一种是客户端模式,因为TCP是面向连接的服务,所以在socket机制当中,TCP的服务器模式比UDP的服务器模式多了listen,accept函数,TCP客户端比UDP客户端多了connect函数。下面是TCP和UDP网络编程的两种模式流程图:
TCP:
在这里插入图片描述
UDP:
在这里插入图片描述

2、IP地址和端口号

(1)IP地址

IP地址是网络中主机(电脑)的标识,在网络中主机想要与其他机器通信就必须拥有一个自己的IP地址,IP地址为32位(IPV4)或者128位(IPV6),每一个数据包都必须携带目的地址IP和源IP地址,路由器依靠此信息为数据包选择最优路由(路线)。(IP地址就有点像是几栋楼中的楼的号码)
对于IPv4来说,IP地址是一个4字节32为的整数。通常使用“点分十进制”的字符串来表示IP地址,如192.168.144.2,用点分割的每一个数字表示一个字节,范围是0~255。

(2)端口号

用于区分一台主机中接收到的数据包应该交给那一个进程进行处理(注意TCP和UDP的端口号是相互独立的)。(如果IP地址是相当于一栋楼的楼号的话,那么端口号就相当于是这栋楼里面的房间的房号)

端口号的作用:
一台拥有IP地址的主机可以提供许多服务,比如Web服务,FTP服务,SMTP服务等。

这些服务完全是可以由一个IP地址来实现的,那么,主机是怎么样区分不同的网络服务的呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。

实际上通过"IP地址+端口号"来区分不同的服务的。
端口提供了一种访问通道,服务器一般都是通过知名的端口来识别的。例如,对于每个TCP/IP的实现来说,FTP服务的端口号是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69。

二、字节序

1、字节系相关概念

字节序:是指多字节数据的存储顺序,在设计计算机系统的时候,有两种处理内存中数据的方法:即大端格式、小端格式。

MSB(Most Significant Byte),最高有效字节,是一个数据中权值最大的那一个字节。
LSB(Least Significant Byte),最低有效字节,是一个数据中权值最小的那一个字节。

小端格式(Little-Endian):将低位字节(LSB)数据存储在低地址
大端格式(Big-Endian):将高位字节(MSB)数据存储在低地址
注意:网络字节序=大端字节序

例如:整型数 0x1A2B3C4D 它的MSB是 0x1A,LSB是 0x4D,在内存中存储的方式如下:
在这里插入图片描述

为何使用字节序?

  • 计算机电路先处理低位字节,效率比较高。因为计算就是从低位开始的,所以计算机内部很多都是小端字节序。
  • 格式规范是为人类编写的,大端字节序更符合人类习惯。

网络字节序的定义:将收到的第一个字节的数据当做高位来看待,这就要求发送端的发送的第一个字节应该是高位。而在发送端发送数据时,发送的第一个字节是该数字在内存中起始地址对应的字节。可见多字节数值在发送前,在内存中数值应该以大端法存放。

所以,网络协议指定了通讯字节序:大端。只有在多字节数据处理时才需要考虑字节序,运行在同一台计算机上的进程相互通信时,一般不用考虑字节序,异构计算机之间进行通讯时,需要将自己的字节序转换为网络字节序。

2、有关字节序转换的函数

  h代表host,n代表net,s代表short(2个字节),l代表long(4个字节),通过下面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取。

(1)主机字节序转换网络字节序函数

函数原型:

#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);//将16位主机字节序数据转换成网络字节序数据
uint32_t htonl(uint32_t hostlong);//将32位主机字节序数据转换成网络字节序数据

功能:
htons将16位主机字节序数据转换成网络字节序数据
htonl将32位主机字节序数据转换成网络字节序数据

参数介绍:
hostshort:需要转换的16位主机字节序数据,uint16_t:unsigned short int
hostlong:需要转换的32位主机字节序数据,uint32_t:32位无符号整型

返回值:
成功,返回网络字节序的值;失败,返回-1

(2)网络字节序转换主机字节序函数

函数原型:

#include <arpa/inet.h>
uint16_t ntohs(uint16_t netshort);//将16位网络字节序数据转换成主机字节序数据
uint32_t ntohl(uint32_t netlong);//将32位网络字节序数据转换成主机字节序数据

函数功能:
ntohs将16位网络字节序数据转换成主机字节序数据
ntohl将32位网络字节序数据转换成主机字节序数据

参数介绍:
netshort:需要转换的16位网络字节序数据,uint16_t:unsigned short int
netlong:需要转换的32位网络字节序数据,uint32_t:unsigned int

返回值:
成功,返回主机字节序的值;失败,返回-1

3、地址转换函数

点分十进制(Dotted Decimal Notation)全称为点分(点式)十进制表示法,是IPv4的IP地址标识方法。IPv4中用四个字节表示一个IP地址,每个字节按照十进制表示为0~255。
点分十进制就是用4组从0~255的数字,来表示一个IP地址。如192.168.1.1。

(1)点分十进制IP地址转换为二进制IP地址函数

①函数inet_aton

函数原型:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);

说明:
将点分十进制字符串转换成32位无符号整数,只适用于IPV4地址

参数介绍:
cp:将被转换的点分十进制IP地址
inp:保存被转换成二进制的IP地址

返回值:
如果地址合法,则返回非0值,反之返回0值

②函数inet_pton

函数原型:

#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);

说明:
该函数将字符串src转换为af地址类型协议簇的网络地址,并存储到dst中。
对于af参数,必须为AF_INETAF_INET6适用于IPV4和IPV6地址

参数介绍:
af:AF_INET或AF_INET6
src:将被转换的点分十进制的IP地址
dst:保存被转换的二进制IP地址

返回值:
转换成功则返回1,对于指定的地址类型协议簇,如果不是一个有效的网络地址,将转换失败,返回 0,如果指定的地址类型协议簇不合法,将返回-1,并且errno设置为EAFNOSUPPORT

(2)二进制IP地址转换为点分十进制IP地址函数

①函数inet_ntoa

函数原型:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);

说明:
inet_ntoa()用来将参数in所指的大端网络字节序二进制的数字转换成ipv4点分十进制字符串网络地址,然后将指向此网络地址字符串的指针返回。成功则返回字符串指针,失败则返回NULL。

参数:
in:将被转换的二进制IP地址(即32位无符号整数)

返回值:
成功:返回点分十进制的IP地址
失败:返回NULL

②函数inet_ntop

函数原型:

#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

说明:
将二进制的IP地址转换成点分十进制的字符串(即点分十进制的IP地址)
该函数将地址类型协议簇为af的网络地址src转换为字符串,并将其存储到dst中,其中dst不能是空指针。
调用者在参数size中指定可使用的缓冲字节数。
inet_ntop拓展自inet_ntoa来支持多种地址类型协议簇,inet_ntoa现在已经被弃用。

参数:
afAF_INETAF_INET6
src:二进制的IP地址
dst:保存被转换成点分十进制的IP地址
size:指定可使用的缓冲字节数

返回值:
inet_ntop执行成功,返回一个指向dst的非空指针,如果执行失败,将返回NULL,并且errno设置为相应的错误类型。

错误类型:

EAFNOSUPPORT :af并不是一个合法的地址类型协议簇
ENOSPC :要转换的字符串地址src其字节大小超过了给定的缓冲字节大小

三、Socket套接字相关函数

1、Socket 编程流程

socket编程有以下几种基本函数:
socket():用于创建套接字,同时指定协议和类型
bind():将保存在相应地址结构中的地址信息与套接字进行绑定。主要用于服务器端,客户端创建的套接字可以不绑定地址
listen():在服务器端建立套接字并绑定地址后,将套接字设置成监听模式(被动模式),准备接收客户端的连接请求
accept():等待并接收客户端的连接请求。建立好TCP连接后,该函数将返回一个新的已连接套接字
connect():客户端通过该函数向服务器端的监听套接字发送连接请求
send()和recv():通常用于TCP通讯中的发送和接收数据,也可用在UDP中
sendto()和recvfrom():通常用于UDP通讯中的发送和接收数据

(1)TCP通信流程

服务器:
1.创建套接字(socket)
2.将socket与IP地址和端口绑定(bind)
3.监听被绑定的端口(listen)
4.接收连接请求(accept)
5.从socket中读取客户端发送来的信息(read)
6.向socket中写入信息(write)
7.关闭socket(close)

客户端:
1.创建套接字(socket)
2.连接指定计算机的端口(connect)
3.向socket中写入信息(write)
4.从socket中读取服务端发送过来的消息(read)
5.关闭socket(close)

(2)UDP通信流程

服务器:
1.使用函数socket(),生成套接字文件描述符;
2.通过struct sockaddr_in 结构设置服务器地址和监听端口;
3.使用bind() 函数绑定监听端口,将套接字文件描述符和地址类型变量(struct sockaddr_in )进行绑定;
4.接收客户端的数据,使用recvfrom() 函数接收客户端的网络数据;
5.向客户端发送数据,使用sendto() 函数向服务器主机发送数据;
6.关闭套接字,使用close() 函数释放资源;

客户端:
1.使用socket(),生成套接字文件描述符;
2.通过struct sockaddr_in 结构设置服务器地址和监听端口;
3.向服务器发送数据,sendto() ;
4.接收服务器的数据,recvfrom() ;
5.关闭套接字,close() ;

2、Socket 相关函数

(1)socket()函数(创建套建字)

函数原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

说明:
用于创建套接字,同时指定协议和类型

参数:
domain:指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族);

AF_INET IPv4因特网域
AF_INET6 IPv6因特网域
AF_UNIX Unix域
AF_ROUTE 路由套接字
AF_KEY密钥套接字
AF_UNSPEC 未指定

type:参数指定socket的类型:

SOCK_STREAM: 流式套接字提供可靠的,面向连接的通信流,它使用TCP协议,从而保证了数据传输的正确性和顺序性
SOCK_DGRAM:数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠,无差错的。它使用数据报协议UDP
SOCK_RAW:允许程序使用底层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

protocol:通常赋值为“0”

0选择type类型对应的默认协议
IPPROTO_TCP TCP传输协议
IPPROTO_UDP UDP传输协议
IPPROTO_SCTP SCTP传输协议
IPPROTO_TIPC TIPC传输协议

返回值:
成功返回非负套接字描述符,失败返回-1

(2)bind()函数(绑定套建字)

函数原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

说明:
用于绑定IP地址和端口号到socket

参数介绍:
sockfd:是一个socket描述符
addr:是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同。
addrlen:地址的长度,一般用sizeof(struct sockaddr_in)表示

补充:
sockaddr在头文件#include <sys/socket.h>中定义,
sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了,如下:

struct sockaddr {  
	sa_family_t 	sin_family;//地址族
	char 			sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               
}; 

sockaddr_in在头文件#include<netinet/in.h>#include <arpa/inet.h>中定义,
该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:

struct sockaddr_in {
	sa_family_t		sin_family;/*协议族*/
	in_port_t		sin_port;/*端口号*/
	struct in_addr 	sin_addr;/*IP地址结构体*/
	unsigned char 	sin_zero[8];/*填充没有实际意义只是为跟sockaddr结构在内存中对齐这样两者才能相互转换*/

由上面两个结构体可以看出我们在实际编程的时候通常是对第二个结构体进行使用,由于struct sockaddr数据结构类型不方便设置,所以通常会通过对struct sockaddr_in进行地质结构设置,然后进行强制类型转换成struct sockaddr类型的数据。

返回值:
若成功,返回0;若出错,返回-1

(3)listen()函数(监听被绑定的端口)

函数原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);

说明:
设置能处理的最大连接数,listen并未开始接受连线,只是设置了socket的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁进行连接,因此,它不会主动的要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接,主要就连个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数。

内核为任何一个给定监听套接字维护两个队列:
未完成连接队列,每个这样的SYN报文段对应其中一项:已由某个客户端发出并到达服务器,而服务器正在等待完成相应的TCP三次握手过程,这些套接字处于SYN_REVD状态
已完成连接队列,每个已完成TCP三次握手过程的客户端对应其中一项,这些套接字处于ESTABLISHED状态。

参数介绍:
sockfd:socket系统调用返回的服务端socket描述符
backlog:指定在请求队列中允许的最大的请求数,大多数系统默认为5

返回值:
若成功,返回0;若出错,返回-1

(4)accept()函数(接收连接请求)

函数原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

说明:
accept函数由TCP服务器调用,用于从已完成连接队列对头返回下一个已完成连接,如果已完成连接队列为空,那么进程被投入睡眠。

参数介绍:
sockfd:是socket系统调用返回的服务器端socket描述符
addr:用来返回已连接的对端(客户端)的协议地址
addrlen:客户端地址长度,注意需要取地址

返回值:
该函数的返回值是一个新的套接字的描述符,返回值是表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符,一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示TCP三次握手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。若出错,返回-1

(5)connect()函数(发送连接请求)

函数原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

说明:
该函数用于绑定之后的client端(客户端),与服务器建立连接

参数介绍:
sockfd:创建的socket描述符
addr:服务端的ip地址和端口号的地址结构指针
addrlen:地址的长度,通常被设置为sizeof(struct sockaddr)

返回值:
成功返回0,遇到错误时返回-1,并且errno中包含相应的错误码

(6)send()、recv()函数(TCP发送、接收信息)

①函数send()

函数原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

说明:
函数只能对处于连接状态的套接字进行使用,参数sockfd为已建立好连接的套接字描述符

参数介绍:
sockfd:为已建立好连接的套接字描述符即accept函数的返回值
buf:要发送的内容
len:发送内容的长度
flags:设置为MSG_DONTWAITMSG 时表示非阻塞,设置为0时功能和write()一样\

返回值:
成功返回实际发送的字节数,失败:返回 -1

②函数recv()

函数原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

说明:
接收套接字中的数据

参数介绍:
sockfd:在哪个套接字接
buf:存放要接收的数据的首地址
len:要接收的数据的字节
flags:设置为MSG_DONTWAITMSG 时表示非阻塞,设置为0时功能和read()一样
注意:read() 是一个阻塞函数,如果客户端没有声明断开,那么它就会认为客户端仍旧可能发送数据,所以就会一直阻塞而不是返回-1;

返回值:
成功返回实际发送的字节数,失败:返回 -1

(7)sendto()、recvfrom()函数(UDP发送、接收消息)

①函数sendto()

函数原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

说明:
UDP发送消息
参数介绍:
sockfd: socket描述符
buf:UDP数据报缓存区(包含待发送数据)
len: UDP数据报的长度
flags:调用方式标志位(一般设置为0),设置为MSG_DONTWAITMSG 时表示非阻塞
dest_addr:IP地址和端口号,指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换);之前介绍bind函数时已经介绍
addrlen:dest_addr所指结构体的长度

返回值:
成功则返回实际传送出去的字符数,失败返回-1,错误原因会存于errno 中

②函数recvfrom()

函数原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

说明:
UDP接收消息

参数介绍:
sockfd:socket描述符
buf:UDP数据报缓存区(包含所接收的数据)
len:缓冲区长度
flags:调用操作方式(一般设置为0),设置为MSG_DONTWAITMSG 时表示非阻塞
src_addr:IP地址和端口号,指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换)
addrlen:指针,指向src_addr结构体长度值。

返回值:
成功则返回实际接收到的字符数,失败返回-1,错误原因会存于errno 中

四、示例

代码:

//server.c
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
//#include <linux/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc,char **argv)
{
	int sockfd;
	int client_fd;
	int n_read;
	char readBuf[128];
	//char *msg = "I get your message";
	char writeBuf[128] = {0};
	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;

	if(argc != 3)
	{
		printf("the param is not good!\n");
		exit(1);
	}

	//void *memset(void *s, int c, size_t n);
	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	//1.int socket(int domain, int type, int protocol);
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		exit(1);
	}
	
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(atoi(argv[2]));//uint16_t htons(uint16_t hostshort);
	inet_aton(argv[1],&s_addr.sin_addr); //int inet_aton(const char *cp, struct in_addr *inp);

	//2.int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
	bind(sockfd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

	//3.int listen(int sockfd, int backlog);
	listen(sockfd,10);

	//4.int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	int c_len = sizeof(struct sockaddr_in);
	while(1){

		client_fd = accept(sockfd,(struct sockaddr *)&c_addr,&c_len);
		if(client_fd == -1){
			perror("accept");
			exit(1);
		}

		printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//char *inet_ntoa(struct in_addr in);
		
		if(fork() == 0){	
			if(fork() == 0){
				while(1){
					//6.write
					memset(writeBuf,0,sizeof(writeBuf));
					printf("input: ");
					gets(writeBuf);
					write(client_fd,writeBuf,strlen(writeBuf));
				}
			}
			
			while(1){
				//5.read
				memset(readBuf,0,sizeof(readBuf));
				n_read = read(client_fd,readBuf,128);
				if(n_read == -1){
					perror("read");
					exit(1);
				}else{
					printf("get %d message:%s\n",n_read,readBuf);
				}
			}
			break;	
		}
	}
	return 0;
}
//client.c
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
//#include <linux/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc,char **argv)
{
	int client_fd;
	int n_read;
	char readBuf[128];
	//char *msg = "message from client";
	char writeBuf[128] = {0};
	struct sockaddr_in c_addr;

	if(argc != 3)
	{
		printf("the param is not good!\n");
		exit(1);
	}

	//void *memset(void *s, int c, size_t n);
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	//1.int socket(int domain, int type, int protocol);
	client_fd = socket(AF_INET,SOCK_STREAM,0);
	if(client_fd == -1){
		perror("socket");
		exit(1);
	}
	
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(atoi(argv[2]));//uint16_t htons(uint16_t hostshort);
	inet_aton(argv[1],&c_addr.sin_addr); //int inet_aton(const char *cp, struct in_addr *inp);

	//2.int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
	if(connect(client_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
		perror("connect");
		exit(1);
	}
	
	printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//char *inet_ntoa(struct in_addr in);

	while(1){
		if(fork() == 0){
			while(1){
				memset(writeBuf,0,sizeof(writeBuf));
				printf("input: ");
				gets(writeBuf);
				//3.write/send
				write(client_fd,writeBuf,strlen(writeBuf));
			}
		}

		while(1){
			//4.read
			memset(readBuf,0,sizeof(readBuf));
			n_read = read(client_fd,readBuf,128);
			if(n_read == -1){
				perror("read");
				exit(1);
			}else{
				printf("get %d message from server:%s\n",n_read,readBuf);
			}
		}
	}
	return 0;
}

这里用到了memset函数,补充博客推荐:C语言中menset()函数
结果:
在这里插入图片描述
在这里插入图片描述
参考:Linux系统编程——网络编程

最后谢谢阅读,笔者乃小白,如有错误之处还请指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/34441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小小王总,如何变成任正非、化腾、强东这样的巨人!

原创&#xff1a;小姐姐味道&#xff08;微信公众号ID&#xff1a;xjjdog&#xff09;&#xff0c;欢迎分享&#xff0c;非公众号转载保留此声明。王总特别迷信外面的企业培训。当遇到问题时&#xff0c;他喜欢去取经。这个经不像唐僧取经一样&#xff0c;需要历经九九八十一难…

ImmunoChemistry艾美捷通用阻断ELISA阻断缓冲液说明书

ImmunoChemistry艾美捷通用阻断ELISA阻断缓冲液包含适用于大多数抗体捕获ELISA格式和肽或蛋白质抗原下调ELISA格式的哺乳动物蛋白质阻断剂。这种封闭缓冲液为干燥的抗原或抗体外壳蛋白提供了长期稳定的环境&#xff0c;并使测定过程中的非特异性结合相互作用最小化。 General B…

中石油测井-技术研发岗回顾

前提&#xff1a; 时间&#xff1a;2022年11月25日 结果&#xff1a;暂未可知 阶段&#xff1a;面试结束 等结果 整个过程中&#xff0c;注意查看官网 中石油招聘 投递之前关注一下基本要求&#xff08;学历-专业&#xff09; 招聘人数&#xff1a;应聘人数 &#xff08;通过能…

【富文本编辑器】Ueditor的demo——创建、修改——代码使用

文章目录富文本编辑器简述&#xff1a;使用&#xff1a;1.下载的demo&#xff1a;2.项目创建&#xff1a;3.修改代码&#xff1a;4.使用富文本编辑器&#xff1a;示例&#xff1a;富文本编辑器 Ueditor的资源官网&#xff1a;http://fex.baidu.com/ueditor/ 资源下载官网&…

【Linux】进程优先级(PRI,NI)和进程切换

1、进程优先级 无论是外设还是CPU&#xff0c;所能提供给操作系统的资源很少&#xff0c;而操作系统中的进程又非常多&#xff0c;安排谁先使用资源&#xff0c;就需要进程优先级的存在。   查看进程优先级 ps -al 查看当前运行进程的优先级信息。 ps -al | grep test 配合…

卫龙食品冲刺港股:上半年经调整利润超4亿 高瓴与红杉是股东

雷帝网 雷建平 11月24日卫龙食品日前更新招股书&#xff0c;准备在香港上市。这是卫龙食品通过聆讯后第二次更新招股书。卫龙食品的前身可追溯到20多年前。当年&#xff0c;卫龙食品创办人刘卫平下了火车&#xff0c;找到一个简陋的小旅馆休息&#xff0c;天亮后&#xff0c;就…

DirectX12 - Swap Chain(交换链)

这里是SunshineBooming&#xff0c;GPU公司一枚小小的Driver工程师&#xff0c;主要工作是写DirectX12 Driver&#xff0c;我会持续更新这个DX12 Spec系列&#xff0c;可能比较冷门&#xff0c;但是都是干货和工作中的心得体会&#xff0c;有任何GPU相关的问题都可以在评论区互…

宠物经济:千亿级的孤独生意,如何用智能化玩出新花样?

今年双十一&#xff0c;由铲屎官们主导的宠物经济成功实现逆增长&#xff0c;在众多行业中脱颖而出&#xff0c;成为消费疲软环境下的一匹强势黑马。 天猫、京东数据显示&#xff1a;双十一期间&#xff0c;天猫平台宠物烘焙粮销量同比增长超 700%&#xff0c;京东平台宠物高端…

js逆向tips-某思录登录

0. 写在最前 现在很多时候我们再网页上进行操作时都是用http协议进行一个交流&#xff0c;前端后也是如此&#xff0c;现在的开发模式大部分都是前后端分离的一个交互模式&#xff0c;那就意味着前端发送请求时也是和我们点击按钮一样去向后端发送一个请求&#xff0c;那这种请…

Pr 时间重映射卡点

哈喽&#xff0c;各位小伙伴&#xff01;今天我们来学习一下如何通过用Pr时间重映射做出卡点的效果~ 卡点音乐 一首适合卡点&#xff08;群青为例&#xff09;的音乐可以帮助我们更好的掌握视频的节奏&#xff0c;卡点可以采用手动卡点&#xff0c;方法可以通过在峰值最高处标…

3.66 OrCAD中不同的工程文件,怎么输出所需要工程文件的网表?OrCAD中怎么给元器件自定义属性?

笔者电子信息专业硕士毕业&#xff0c;获得过多次电子设计大赛、大学生智能车、数学建模国奖&#xff0c;现就职于南京某半导体芯片公司&#xff0c;从事硬件研发&#xff0c;电路设计研究。对于学电子的小伙伴&#xff0c;深知入门的不易&#xff0c;特开次博客交流分享经验&a…

vue如何通过VNode渲染节点

vue如何通过VNode渲染节点vue的源码包含三大核心实现一个Mini-Vue渲染系统的实现vue2和vue3写法上的区别vue的源码包含三大核心 Compiler模块&#xff1a;编译模板系统 Runtime模块&#xff1a;也可以称之为Renderer模块&#xff0c;真正的渲染的模块 Reactivity模块&#x…

合成孔径雷达地面运动目标检测技术研究——基于概率图(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

SpringCloud学习笔记(四)

文章目录SpringCloud学习笔记(四)1.说在前面2.OpenFeign 简介3.OpenFeign 快速入门3.1 本次调用的设计图3.2 启动一个 eureka-server 服务&#xff0c;这里不重复演示&#xff0c;参考 eureka3.3 先创建 01-order-service&#xff0c;选择依赖3.4 创建 02-user-consumer&#x…

Uni-app常用知识点总结

一、一句话总的形容一下uniapp与vue和微信小程序的异同点 简单来讲Uni-app就是用着vue的指令和小程序的组件和API 二、Uniapp中配置tabbar的方式 见之前的单独文章—— (3条消息) Uni-app中的tabBar的配置_终将抵达丶的博客-CSDN博客_uniapp设置tabbar图片大小https://blog.…

利用霍尔效应传感器和Arduino研究了一个简单的钟摆

A simple pendulum studied using Hall effect sensor and Arduino 利用霍尔效应传感器和Arduino研究了一个简单的钟摆&#xff1a;原文&#xff08;Hall effect sensor (scitation.org)&#xff09; ARTICLES YOU MAY BE INTERESTED IN Measurement of gravitational accele…

One UI 5 升级来了

从11月23日开始&#xff0c;三星多款手机海内外开始推送安卓13/One UI 5.0正式版&#xff0c;大家心心念念的One UI 5终于来了&#xff0c;接下来我们看下有关新版One UI 5相关的更新内容&#xff0c;具体如下&#xff1a; One UI 5 升级 (Android 13) One UI 5 为您带来更加强…

Eureka服务注册与发现

✨ Eureka服务注册与发现微服务的注册中心注册中心的基本介绍注册中心的主要作用注册中心基本原理常见的注册中心Eureka基本介绍服务治理服务注册Eureka 两大组件搭建EurekaEureka端服务注册中心创建新模块 cloud-eureka-server7001添加pom依赖yml配置启动类服务中心管理后台服…

Connection(数据库连接对象)

Connection&#xff08;数据库连接对象&#xff09; 简介&#xff1a;通过代码来讲解Connection的含义。 推荐学习路线&#xff1a;JDBC数据库的连接->Connection&#xff08;数据库连接对象&#xff09;->Statement->ResultSet->通过PreparedStatement预防SQL注入…

【云原生 | Kubernetes 系列】--Gitops持续交付 Argo Rollouts Analysis

1. Argo Rollouts 由一个控制器和一组CRD组成,可为K8s提供高级部署功能 - blue-green - canary - canary analysis 结合外部指标系统金丝雀 - experimentation 实验性的结果 - progressive delivery 渐进式交付,精准管控外部流量策略,不用关心后端部署机制支持Ingress Contro…