互联网应用笔记(二)
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
网络编程只涉及五层以上,底层kernel support被封装隐藏给用户, 将这些程序发布在以太网中的TCP/IP节点。
这些app会调用系统内核中的接口等 接口可以理解为库函数
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 **并发
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)
kill PID 杀掉进程/CTRL c
Related system calls
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调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
在父进程中,fork返回新创建子进程的进程ID;
在子进程中,fork返回0;
如果出现错误,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()
用法:
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
文件描述符(整数)在打开或创建文件时返回,并在以后读取或写入文件时用作参数
对文件操作<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(偏移方式 起始点):
- 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);
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
\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
Client-server model
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.
线上是应用程序,线下是操作系统
Connections are end-to-end, full-duplex (2- way communication), and reliable.
标识进程:端口号和ip
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
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
eth0:以太网接口
lo:回环地址指向本机
- ifconfig
配置网口,通常用于打印网口的配置信息(windows: ipconfig)
ping
Functions: Sends a packet to the host specified by destination and prints out the roundtrip time ( Using ICMP messages)
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
- 202.112.96.163 is mapped to www.bupt.edu.cn
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
大端和小端
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);
}
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. */
};
字符串\0结尾,字符串数组最后一位位null
调用函数
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
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: 域是指通信进程存在的区域。
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字节
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的指针
对比
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).
Socket Operation
connect() listen() accept()只有tcp有
close() and shutdown():
close():四次挥手,双向关闭
shutdown():只关发送不关接收
Byte Order Conversion
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)
不要求
Socket Option
Name and Address OperationA
Process of Socket Operation:TCP Operations
紫色对应三次握手
socket()
create a new socket
that can be used for network communication
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
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
accept()
服务器调用accept()来提取下一个传入请求
Accept()为每个新的连接请求创建一个新的套接字,并将新套接字的描述符返回给它的调用者
返回一个通信的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
send()
应用程序传递数据应该发送到的套接字的描述符、要发送的数据的地址和数据的长度
recv()
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
close()
一旦客户端或服务器完成对套接字的使用,就会调用close来释放它
inet_aton() & inet_addr()
第二个地址用来存转换后的
将一个字符串表示的点分十进制IP地址IP转换为网络字节序(这俩一个功能)
直接返回
inet_ntoa()
将32位整数(网络字节顺序的IP地址)映射为点分十进制格式的ASCII字符串
Sample Programs
TCP
TCP的close只能 close掉通信的stocket
UDP
UDP-based echo service
echo服务只是简单地将它接收到的任何数据发送回原始源
一个非常有用的调试和测量工具
基于UDP的Echo Service:定义为基于UDP的数据报应用。服务器监听UDP数据报UDP端口7。当接收到一个数据报时,其中的数据将以应答数据报的形式发回。
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,存放的是指向每一个参数的指针
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");
}
}
$是普通用户权限
#是root权限