Network Socket Programming

Introduction to NP

Protocol Implementation

◼ TCP/IP

◼ IPX/SPX

Hiding the complexities (隐藏复杂性,用户不用自己操作协议)

◼ Sockets

◼ RPC : Remote Procedure Call(基于socket)

课上environments

◼ TCP/IP nodes on Ethernet

◼ LINUX as the Operating System

◼ C language for most sample programs and assignments

image-20230228100523099

网络编程只涉及五层以上,底层kernel support被封装隐藏给用户, 将这些程序发布在以太网中的TCP/IP节点。

这些app会调用系统内核中的接口等 接口可以理解为库函数

image-20230228100637018

Driver是与硬件相连

kernel是性能提升的部分

C Compiler in Linux

% gcc test1.c -o test

gcc –o test test.c,将test.c编译并连接成可执行的二进制文件test

gcc -g test.c -o test

-g表示可以调试

test1.c : program to be compiled

-o : specify the name for running program给可执行文件起名字

./test

运行test

debugger in Linux

gdb [options] [executable-file [core file or process-id]]

◼ file : load the program for debugging

◼ kill : stop the program for debugging

◼ list : list the source code of the program for debugging

​ 查看源程序代码,默认显示10行,按回车键继续看余下的

◼ break : set a break point in the source program 加断点

◼ run : run the program to be debugged

​ 运行程序直到遇到 结束或者遇到断点等待下一个命令

◼ next : execute a single line of the program, but not go into it

◼ step : execute a single line of the program, but go into it

◼ quit : quit the gdb to shell 退出gdb

◼ print : display the value of a variable

◼ make : make a run-able program without quiting gdb

◼ c : Continue running your program (e.g. at a breakpoint)

◼ bt (backtrace) : display the program stack

Basic Concepts

Process :进程是操作系统正在执行的程序的实例。

System Call Linux/Unix内核提供了有限数量的直接入口点(通常在60到200之间),活动进程可以通过这些入口点从内核获取服务。

File Descriptor A file descriptor is a small integer used to identify a file that has been opened for I/O operation. Linux 中一切都可以看作文件,包括普通文件、链接文件、Socket 以及设备驱动等,对其进行相关操作时,都可能会创建对应的文件描述符。文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,用于指代被打开的文件,对文件所有 I/O 操作相关的系统调用都需要通过文件描述符。

Signal A signal is a notification to a process that an event has occurred

如 CTRL C停止运行

process

One of the most basic abstractions in Unix (the other one is File)

process ≠ program

  • Program:a file containing instructions to be executed, static

  • Process: an instance of a program in execution, live entity

**One program can have multiple processes 、invoke multiple programs **并发

image-20230228103215402

  • Text: program code

  • Data: global variables

  • Heap: dynamic allocated memory, **malloc()**动态分配内存,放在堆中

  • Stack: temporary data (local variable, function parameters, return addresses)

PID (Process ID)

标识进程:The PID is an integer, typically in the range 0 through 32,767. 每个进程都有一个唯一的PID

每一个进程有父进程

PPID (Parent PID): Every process has a parent process ID.父进程ID

Special process

◼ PID = 1: init process

◼ PID = 0: special kernel process (e.g., idle/swapper process)

◼ PID = 2: special kernel process (e.g., page daemon process)

ps –ef

see every process on the system(本身也是一个process)

image-20230228104338070

kill PID 杀掉进程/CTRL c

fork(): to create a child process (父进程的拷贝)

getpid(): to obtain the PID of a process(当前进程的PID)

getppid(): to obtain the PPID(Parent Process ID) of a process(当前进程的父进程PID)

exec(): often used after fork() to load another process(改变子进程)

execl(), execv(), execle(), execve(), execlp(), execvp()

exit(): to terminate a process and release all the resources

fork()

#include <sys/types.h>
//是Unix/Linux系统的基本系统数据类型的头文件,含有size_t,time_t,pid_t等类型。
#include <unistd.h>
//是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件的名称。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
	pid_t t;        
    //typedef int pid_t linux里的int
	t=fork();//返回子进程进程号,子进程里t为0
   //fork()之后的代码会在父子进程都跑一遍
	printf("fork returned %d\n",t);
	exit(0);
}

创建一个进程,所创建的进程复制父进程的代码段/数据段/BSS段/堆/栈等所有用户空间信息

fork()特性

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

  1. 在父进程中,fork返回新创建子进程的进程ID;

  2. 在子进程中,fork返回0;

  3. 如果出现错误,fork返回一个负值;

    可以通过fork返回的值来判断当前进程是子进程还是父进程

$ gcc fork1.c –o fork1
$ ./fork1
fork returned 0 #子进程结果
fork returned 22770 #父进程结果

fork 调用生成的新进程与其父进程谁先执行不一定,哪个进程先执行要看系统的进程调度策略

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main (void) {
	pid_t t;
	printf("Original program, pid=%d\n", getpid());
	t = fork();
	if (t == 0) {
        #子进程执行
		printf("In child process, pid=%d, ppid=%d\n",getpid(), getppid());
	} else {
			printf("In parent, pid=%d, fork returned=%d\n",getpid(), t);
        #父进程自己id+子进程ID
	}
}

结果

Original program, pid=987
In child process, pid=988, ppid=987
In parent, pid=987, fork returned=988 #子进程进程号

exec()

用法:image-20230321192853166

Sample program of exec() – exec1.c

#include <unistd.h>
#include <stdio.h>
int main (void) {
	char *arg[] = { "/bin/ls", 0 };//字符串数组(路径)0字符结尾(null结尾) 先在另一个窗口运行ls process
    //char *arg[] = { "/bin/ls","-l", 0 };带参数的写法
	/* fork, and exec within child process */
	if (fork() == 0) {//子进程
		printf("In child process:\n");
		execv(arg[0], arg);//从此开始子进程执行ls,第二参数指针:读取参数
		printf("I will never be called\n");//在子进程不会执行
	}
	printf("Execution continues in parent process\n");
}
[shiyan@localhost examples-for-ia]$ ./exec1
In child process:
Execution continues in parent process
[shiyan@localhost examples-for-ia]$ exec1 exec1.c #ls
fork1 fork1.c fork2 fork2.c

exec: 将新程序代码加载(拷贝)到子进程的内存空间,替换掉原有的与父进程一模一样的代码和数据,让子进程空间运行全新的程序。

file descriptor

File descriptors are assigned by the kernel when the following system calls are successful

文件描述符(整数)在打开或创建文件时返回,并在以后读取或写入文件时用作参数

image-20230228111820559

对文件操作<unistd.h>

对字符流操作<stdio.h>

open

int open (char *pathname, int oflag, int mode); 

pathname:Name of the file

oflag:打开文件的方式:O_RDONLY O_WRONLY O_RDWR

mode:when creating a file to indicate the access authority

return:File descriptor or -1

close

int close (int filedes);

filedes:File descriptor

return:0 or -1

read

Be used to reading the data from an opened file

int read (int filedes, char *buff, unsigned int nbytes);

filedes:为要读取的文件的File descriptor

buff: 指向存放该函数返回内容的内存的指针

nbytes: 读的字节数

return: 0读完了,-1失败 其他表示写入的字符数

write

Be used to writing data into an opened file

int write (int filedes, char *buff, unsigned int nbytes);

fd:是文件描述符(输出到command line,就是1)

buf:通常是一个字符串,需要写入的字符串

lseek 改变文件偏移量

long lseek (int filedes, long offset, int whence);

offset:文件偏移量(offset为负值表示往前偏移,正值则表示往后偏移)

whence(偏移方式 起始点): 

  1. SEEK_SET表示从文件开头位置往后移动offset个字节,且offset只能为正数,如果offset为负数是没有意义的。

  2. SEEK_CUR表示从当前文件的位置往前或往后移动offset个字节,如果offset为正数则表示往后移动,为负数则表示往前移动。
  3. SEEK_END表示从文件末尾的位置往前或往后移动offset个字节,如果offset为正数则表示往后移动为负数则表示往前移动。

dup

Be used to duplicate a file descriptor 复制一个和filedesc指向同一个文件的文件描述符

int dup (int filedes); 参数旧文件,返回新文件
int dup2 (int filedes, int filedes2); 参数旧文件 新文件

fcntl

Be used to change the properties of the file that is already open

int fcntl (int filedes, int cmd, int arg);

image-20230228113256089

File Permissions

  • r-read, w-write, x-execute 三个代表三位(每一位都对应一种操作:read, write and execute)Three bits can be used to define the permission for one kind of user

  • binary number 111 means the user has permission to read, write and execute the file

    100 means the user only can read the file

  • Three kinds of users(需要定义三种用户的权限):

    u-user, g–group, O-other

    Three numbers for user, group, other

  • For example, 644 (octal) means

    ◼ user: 110 (binary), read&write

    ◼ group: 100 (binary), read

    ◼ other:100 (binary), read

一般前面加个0:0777

Sample program of lseek() – lseek1.c
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

char buf1[]="abcdefghij";
char buf2[]="ABCDEFGHIJ";
#define FILE_MODE 0644 

int main(void)
{
int fd;
if ((fd=creat("file.hole",FILE_MODE))<0) //(文件名,权限)
{ 	printf("creat error\n");
	exit(1);}
 if (write(fd,buf1,10)!=10)
{ 	printf("buf1 write error\n");
	exit(1);}
    /*offset now = 10 */
if (lseek(fd,40,SEEK_SET)==-1)
{ 	printf("lseek error\n");
	exit(1);}
	/*offset now = 40 */
if (write(fd,buf2,10)!=10)
{ 	printf("buf2 write error\n");
	exit(1);}
    /*offset now = 50 */
	exit(0);
} 
  • od command 看文件的内容

    ◼ be used to display the content of the file

    ◼ -c: display in character format

    0644对于下红框rw r r

    image-20230228114454019

\0就是没有写入的内存

当使用lseek时,文件的偏移量可以大于文件的长度。因此,下一个写入操作将扩展文件,并在文件内部创建一个洞

Sample program of read() and write() – readwrite1.c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
	char quit='.';
	char buf[10];
	int fd;
	if((fd = open("out.out",O_RDWR | O_CREAT,0))==-1)
        //O_RDWR 文件可读可写
        //O_CREAT调用create创建
			printf("Error in opening\n");
	while(buf[0]!=quit)
	{
			read(0,buf,1);//'0'代表从键盘读
			write(fd,buf,1);
			write(1,buf,1);//‘1’代表输出到命令行
	}
	close(fd);
}

chmod 777 name.txt代表把所有权限打开

signal

Signals are some time called “Software interrupts”

信号可以从一个进程发送到另一个进程,也可以从内核发送到一个进程

头文件 <signal.h>

The names of the signals begin with SIG

◼ SIGALRM: alarm clock timeout

◼ SIGINT: Interrupt character (Ctrl-C) is typed

image-20230228161053431

image-20230228161708272

Client-server model

image-20230228163853250

Most of network applications are based on the client-server model:

◼ A server process and one or more client processes

◼ Server manages some resources.

◼ Server provides service by manipulating resources for clients.

image-20230228164218084

image-20230228164322633

线上是应用程序,线下是操作系统

Connections are end-to-end, full-duplex (2- way communication), and reliable.

标识进程:端口号和ip

image-20230228164458890

Servers

Servers are long-running processes (daemons).

◼ Typically created at boot-time by the init process (pid=1)

◼ Run continuously until the machine is turned off

image-20230228164707631

Useful Unix Commands

netstat

打印有关Linux网络子系统的信息,例如网络连接、路由表、接口统计信息等

◼ netstat

​ Displays a list of open sockets.

◼ netstat -i

​ Display the information about the network interfaces

◼ netstat -ni

​ Display the information about the network interfaces using numeric addresses

◼ netstat -r

​ Display the kernel routing tables

◼ netstat -nr

​ Display the kernel routing tables using numeric addresses

image-20230228165420828

image-20230228165434425

eth0:以太网接口

lo:回环地址指向本机

image-20230228165552274

- ifconfig

配置网口,通常用于打印网口的配置信息(windows: ipconfig)

image-20230228165637065

ping

Functions: Sends a packet to the host specified by destination and prints out the roundtrip time ( Using ICMP messages)

image-20230228170031947

Structures about IP address and DNS

IP Addresses

Hosts are mapped to a set of 32-bit IP addresses.

  • 202.112.96.163 点分十进制

The set of IP addresses is mapped to a set of identifiers called Internet domain names

32-bit IP addresses are stored in an IP Address structure

用结构体定义

/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};



uint32:
/*Defined in <stdint.h>*/
typedef unsigned int uint32_t;

Two ways to store multi-byte integers

大端和小端

image-20230314100420469

little 先存低位(低位低地址,高位存高地址)

big 先存高位(高位低地址,低位高地址)

发端和收端存储方式不同:数据颠倒:0102->0201

Host byte order vs. network byte order

Host byte order(主机字节序) is machine-dependent (定义big or little)

in <bits/endian.h> 中定义

Network byte order(网络字节序) is machine-independent (big-endian)强制的

发数据前先把主机字节序转换为网络字节序,防止不一致

转换的函数

htonl(L对应Long int:四个字节,对应转换ip长度): host byte order → network byte order for long int

htons(s对应short int: 2个字节,对应转换端口号): host byte order → network byte order for short int

ntohl: network byte order → host byte order for long int

ntohs: network byte order → host byte order for short int

判断本机是little还是big

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
	{
		union {
				short s;
				char c[sizeof(short)];//一个字符是一个字节
		}un;
    //union:所有成员(s和c)共享存储空间 means only one of them can be used at a time
		un.s=0x0102;
		if (sizeof(short)==2){ 
			if (un.c[0]==1 &&un.c[1]==2)
				printf("big-endian\n");
			else if (un.c[0]==2 &&un.c[1]==1)
				printf("littlt-endian\n");
			else
				printf("Unknow\n");
		}else
			printf("sizeof(short)=%d\n",sizeof(short));
			exit(0);
}

image-20230314102722868

short: 2byte

char: 1byte

Domain Name System

DNS:host entry structures(服务器返回的结构体)

定义在 <netdb.h>

/* Description of data base entry for a single host. */
struct hostent
{
	char *h_name; //字符串/* Official name of host. */
	char **h_aliases;//字符串数组  /* Alias(别名) list. */
	int h_addrtype; /* Host address type. V4 or V6 */
	int h_length; /* Length of address. */
	char **h_addr_list;//地址列表(一个域名对应很多个ip) /* List of addresses from name server.*/
#define h_addr h_addr_list[0] /* The first address in 
the address list. */
};

image-20230314104248380

字符串\0结尾,字符串数组最后一位位null

image-20230314104459129

调用函数

  • gethostbyname: query key is a DNS domain name

    *gethostbyname (const char hostname)

#include <netdb.h>
struct hostent * gethostbyname (const char *hostname);

​ 返回结构体的指针

  • gethostbyaddr: query key is an IP address.
#include <netdb.h>
struct hostent * gethostbyaddr (const char *addr, int len, int family );//地址,长度,协议族(AF_INET for IPv4)

Socket Interface

What is a socket?

◼ To the kernel, a socket is an endpoint of communication.

◼ To an application, a socket is a file descriptor that lets the application read/write from/to the network.

All Unix I/O devices, including networks, are modeled as files

◼ Clients and servers communicate with each other by reading from and writing to socket descriptors

image-20230314105255754

0 1 2保留,每个stocket创建给一个描述符

Internet-specific socket address (bits/socket.h)

struct sockaddr_in {
	unsigned short sin_family; /* 协议族 address family (always AF_INET) */
	unsigned short sin_port; /* 端口port num in network byte order(必须为网络字节序) */
	struct in_addr sin_addr; /* IP地址iP addr in network byte order */
	unsigned char sin_zero[8]; /* pad to sizeof(struct sockaddr) */
};

一共占16字节

Address family: 域是指通信进程存在的区域。

image-20230314105658816

Generic socket address (<sys/socket.h>)

struct sockaddr {
	unsigned short sa_family; /* protocol family */
	char sa_data[14]; /* protocol-specific address, up to 14 bytes. */
};

16字节

image-20230314105858837

PF开头

多个stock通信的一些函数(**connect(), bind() and accept()**)Gneric socket

Must cast Internet-specific socket address (sockaddr_in *) to generic socket address (sockaddr *) for connect, bind, and accept

struct sockaddr_in serv;
/* fill in serv{}*/
bind (sockfd, (struct sockaddr *)&serv , sizeof(serv));//struct sockaddr *是指针类型,所以也要用&获取serv的指针
对比

image-20230314110612950

Sockets Interface

Stream Socket

Service: reliable (i.e. sequenced, non-duplicated, non-corrupted) bidirectional delivery of byte-stream data

int s = socket (PF_INET, SOCK_STREAM, 0);

s是stocket描述符

Datagram Socket

Service: unreliable, unsequenced datagram

int s = socket (PF_INET, SOCK_DGRAM, 0);

Raw Sockets Service

原始stocket

设计底层时,通过这个可以改底层服务

int s = socket (PF_INET, SOCK_RAW, protocol);

Reliably-delivered Message Socket

Similar to datagram socket but ensure the arrival of the datagrams(UDP加一点可靠性)

int s = socket (PF_NS, SOCK_RDM, 0);

Sequenced Packet Stream Socket

Similar to stream socket but using fixed-size datagrams

int s = socket (PF_NS, SOCK_SEQPACKET, 0);

Major System Calls

A network application works as follows:

◼ An endpoint (telephone) for communication is created on both ends.

◼ An address (phone no) is assigned to both ends to distinguish them from the rest of the network.

◼ One of the endpoint (receiver) waits for the communication to start.

◼ The other endpoint (caller) initiates a connection.

◼ Once the call has been accepted, a connection is made and data is exchanged (talk).

◼ Once data has been exchanged the endpoints are closed (hang up).

image-20230314111743184

Socket Operation

image-20230314111917906

connect() listen() accept()只有tcp有

close() and shutdown():

  • close():四次挥手,双向关闭

  • shutdown():只关发送不关接收

Byte Order Conversion

image-20230314112222711

Address Formats Conversion

  • inet_aton()

    IP address in numbers-and-dots notation (ASCII string) → IP address structure in network byte order 把字符串地址转为结构体

  • inet_addr()

same function with inet_aton()

  • inet_ntoa()

    IP address structure in network byte order → IP address in numbers-and-dots notation (ASCII string)

image-20230314113157508

不要求

Socket Option

image-20230314113232615

Name and Address OperationA

image-20230314113317487

Process of Socket Operation:TCP Operations

image-20230314113415735

紫色对应三次握手

image-20230314113609695

socket()

create a new socket

that can be used for network communication

image-20230314114320877

bind()

An application calls bind() to specify the local endpoint address (a local IP address and protocol port number) for a socket(服务器的地址和端口)

对于TCP/IP,端点地址使用sockaddr_in结构体。

但是必须结构转换才能用这个函数:Must cast Internet-specific socket address (struct sockaddr_in *) to generic socket address (struct sockaddr *) for bind

image-20230314114500548

listen()

TCP server使用:place a socket in passive mode and make it ready to accept incoming connections

It only applies to socket used with TCP

image-20230314114620736

accept()

服务器调用accept()来提取下一个传入请求

Accept()为每个新的连接请求创建一个新的套接字,并将新套接字的描述符返回给它的调用者

6b6e6f516f1a636cb062e0bd4f47923

image-20230314115114195

返回一个通信的stocket 的fd

connect()

a client calls connect() to establish an active connection to a remote server

Must cast Internet-specific socket address (struct sockaddr_in *) to generic socket address (struct sockaddr *) for connect

image-20230314115347571

send()

应用程序传递数据应该发送到的套接字的描述符、要发送的数据的地址和数据的长度

image-20230314115431518

recv()

image-20230314115606787

UDP只有两个:sendto() & recvfrom()

前面send() recv()因为已经建立好连接了,所以参数中不用有目标的地址

UDP无连接:

​ sendto() requires the caller to specify a destination

​ recvfrom() uses an argument to specify where to record the sender’s address

image-20230314115736518

close()

一旦客户端或服务器完成对套接字的使用,就会调用close来释放它image-20230314115904963

inet_aton() & inet_addr()

第二个地址用来存转换后的

将一个字符串表示的点分十进制IP地址IP转换为网络字节序(这俩一个功能)

image-20230314115942758

直接返回

inet_ntoa()

将32位整数(网络字节顺序的IP地址)映射为点分十进制格式的ASCII字符串

image-20230314120153969

Sample Programs

TCP

image-20230314120403961

TCP的close只能 close掉通信的stocket

UDP

image-20230314120445239

UDP-based echo service

  • echo服务只是简单地将它接收到的任何数据发送回原始源

  • 一个非常有用的调试和测量工具

  • 基于UDP的Echo Service:定义为基于UDP的数据报应用。服务器监听UDP数据报UDP端口7。当接收到一个数据报时,其中的数据将以应答数据报的形式发回。

image-20230314120921628

EchoClient

头文件:
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), sendto() and 
recvfrom() */
#include <arpa/inet.h> /* for sockaddr_in and inet_addr() */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
Initial part of UDP EchoClient
#define ECHOMAX 255 /* Longest string to echo */
int main(int argc, char *argv[]) //argc参数个数,argv[]具体内容
{
int sock; /* Socket descriptor */
struct sockaddr_in echoServAddr; /* Echo server address */
struct sockaddr_in fromAddr; /* Source address of echo */
unsigned short echoServPort; /* Echo server port */ 
unsigned int fromSize; /* In-out of address size 
for recvfrom() */
char *servIP; /* IP address of server */
char *echoString; /* String to send to echo server */
char echoBuffer[ECHOMAX+1]; /* Buffer for receiving 
echoed string(最后一个是/0) */
int echoStringLen; /* Length of string to echo (发送的)*/
int respStringLen; /* Length of received response */

argc: 整数,传给main()的命令行参数个数

argv: 字符串数组。argv是一个指针数组,元素个数是argc,存放的是指向每一个参数的指针

image-20230321100217339
if ((argc < 3) || (argc > 4)) /* Test for correct number of
arguments 参数数为3、4才执行*/
{
printf("Usage: %s <Server IP> <Echo Word> [<Echo Port>]\n", 
argv[0]);
exit(1);
}
servIP = argv[1]; /* First arg: server IP address (dotted quad) */
echoString = argv[2]; /* Second arg: string to echo */
if ((echoStringLen = strlen(echoString)) > ECHOMAX) /* Check input 
length */
printf("Echo word too long.\n"); 
if (argc == 4)
echoServPort = atoi(argv[3]); /* ASCII to integer Use given port, if any */
else//如果没自定义端口,就给7;
echoServPort = 7; /* 7 is the well-known port for echo service */
I/O part of UDP EchoClient
/* Create a datagram/UDP socket */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)//成功返回描述符,失败小于0
printf("socket() failed.\n");
/* Construct the server address structure */
memset(&echoServAddr, 0, sizeof(echoServAddr));/*Zero out structure*/初始化
echoServAddr.sin_family = AF_INET; /* Internet addr family */
echoServAddr.sin_addr.s_addr = inet_addr(servIP);/*Server IP address*/
echoServAddr.sin_port = htons(echoServPort); /* Server port */
/* Send the string to the server */
if ((sendto(sock, echoString, echoStringLen, 0, 
(struct sockaddr *) &echoServAddr, sizeof(echoServAddr))) 
!= echoStringLen)
printf("sendto() sent a different number of bytes than expected.\n");
/* Recv a response */
fromSize = sizeof(fromAddr);
if ((respStringLen = recvfrom(sock, echoBuffer, ECHOMAX, 0,
(struct sockaddr *) &fromAddr, &fromSize)) != echoStringLen)
printf("recvfrom() failed\n");
if (echoServAddr.sin_addr.s_addr != fromAddr.sin_addr.s_addr)
{
    //判断是不是从echoserver来的
printf("Error: received a packet from unknown source.\n");
exit(1);
}
/* null-terminate the received data */
echoBuffer[respStringLen] = '\0';
printf("Received: %s\n", echoBuffer);/*Print the echoed message*/
close(sock);
exit(0);
}

EchoServer

Initial part of UDP EchoServer
#define ECHOMAX 255 /* Longest string to echo */
int main(int argc, char *argv[])
{
int sock; /* Socket */
struct sockaddr_in echoServAddr; /* Local address */
struct sockaddr_in echoClntAddr; /* Client address */
unsigned int cliAddrLen; /* Length of client address */
char echoBuffer[ECHOMAX]; /* Buffer for echo string */
unsigned short echoServPort; /* Server port */
int recvMsgSize; /* Size of received message */
Argument check part of UDP EchoServer
if (argc != 2)
{
printf("Usage: %s <UDP SERVER PORT>\n", argv[0]);
exit(1);
}
Socket part of UDP EchoServer
echoServPort = atoi(argv[1]); /* First arg: local port */
/* Create socket for sending/receiving datagrams */
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
printf("socket() failed.\n");
/* Construct local address structure */
memset(&echoServAddr, 0, sizeof(echoServAddr));
echoServAddr.sin_family = AF_INET;
echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);//0.0.0.0
echoServAddr.sin_port =htons(echoServPort);
/* Bind to the local address */
if ((bind(sock, (struct sockaddr *) &echoServAddr, //强制转换
sizeof(echoServAddr))) < 0)
printf("bind() failed.\n");
Main loop of UDP EchoServer
for (;;) /* Run forever */
{
/* Set the size of the in-out parameter */
cliAddrLen = sizeof(echoClntAddr);
/* Block until receive message from a client */
if ((recvMsgSize = recvfrom(sock, echoBuffer, ECHOMAX, 
0,(struct sockaddr *) &echoClntAddr, &cliAddrLen)) < 0)
printf("recvfrom() failed.\n");
printf("Handling client %s\n",inet_ntoa(echoClntAddr.sin_addr));
/* Send received datagram back to the client */
if ((sendto(sock, echoBuffer, recvMsgSize, 0,
	(struct sockaddr *) &echoClntAddr, 
	sizeof(echoClntAddr))) != recvMsgSize)
	printf("sendto() sent a different number of bytes 
			than expected.\n");
}
}

image-20230321103225333

image-20230321103237237

$是普通用户权限

#是root权限

使用默认端口

image-20230321103346931

使用自定义端口image-20230321103439203