互联网应用笔记(二)
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权限