linux网络编程
一、socket
1.常见函数详解
1.创建socket:
int socket(int domain, int type, int protocol);
domain: AF_INET 这是大多数用来产生 socket 的协议,使用 TCP 或 UDP 来传输,用 IPv4 的地址
AF_INET6 与上面类似,不过是来用 IPv6 的地址
AF_UNIX 本地协议
type:SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的 socket 类型,这个socket 是使用 TCP 来进行传输。
SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用 UDP 来进行它的连接
protocol: 传 0 表示使用默认协议。 protcol是0表示,根据type的值选择合适的协议。
当type是sock_stream时。protoc是tcp
当type是sock_dgram时.protcol是udp
成功:返回指向新创建的 socket 的文件描述符,失败:返回-1,设置 errno
2.绑定socket的端口和ip
Int bind(int sockfd,const struct sockaddr*addr,socklen_t addrlen);
sockfd:套接字的fd(文件描述符),socket()函数的返回值
addr:结构体 ip+port(端口)
struct sockaddr_in{ (涉及强制转换sockaddr_in ->sockaddr 参考)
short int sin_family; //地址族
unsigned short int sin_port; //端口号
struct in_addr sin_addr; //IP地址
}
struct in_addr {
__be32 s_addr;
};
3.设置sockefd的同时最大的链接数
int listen(int sockfd,int backlog);
backlog:请求链接客户端队列的最大存放数目
返回值:判断成功失败
4.等待某个sockfd的客户端响应
int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen)
addr:传出参数,跟我建立连接的客户端的结构体(内含客户端ip+端口)
返回值:连接客户端的套接字。
当有一个client响应listenfd时,accept会返回一个connfd与客户端通信。
5.client与server通信
int connect(int sockfd,struct sockaddr*serv_addr,int addrlen)
sockfd:传入参数,文件描述符绑定连接成功的服务器套接字便于在客户端对服务器进行IO操作
serv_addr:绑定我要链接服务器的结构体(需要初始化绑上ip和断口),表明目的
2.服务器代码
思路:利用socket创建一个listenfd,bind其服务器ip和端口,listen设置其最大通信数,accept阻塞监听listen,当有client响应时,accept返回一个connfd与client通信。listenfd始终作为accept的换入参数,监听client。每当有client与其通行就设置新的connfd语气通信。相当于connfd是绑定client端ip和端口的fd,server只需要读写connfd就可以与其通信。
单进程服务器
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SER_PORT 8000
int main(void)
{
int sockfd,connfd;
int len;
char wbuf[1024];
char rbuf[1024];
struct sockaddr_in serveraddr,clientaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SER_PORT);
bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
listen(sockfd,128);
int size = sizeof(clientaddr);
connfd = accept(sockfd,(struct sockaddr *)&clientaddr,&size);
char ipstr[128];
printf("client ip%s ,port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),
ntohs(clientaddr.sin_port));
while(1)
{
memset(wbuf,0,sizeof(wbuf));
memset(rbuf,0,sizeof(wbuf));
int len = read(connfd,rbuf,sizeof(rbuf));
if(len==0)
{
printf("client is close....\n");
}
printf("receive from client:%s",rbuf);
printf("send to client:");
fgets(wbuf,sizeof(wbuf),stdin);
write(connfd,wbuf,strlen(wbuf));
}
close(connfd);
close(sockfd);
return 0;
}
多进程服务器
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include <unistd.h>
#include "wrap.h"
#define MAXLINE 8192
#define SERV_PORT 8000
void do_sigchild(int num)
{
while (waitpid(0, NULL, WNOHANG) > 0)
;
}
int main(void)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, n;
pid_t pid;
struct sigaction newact;
newact.sa_handler = do_sigchild;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGCHLD, &newact, NULL);
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(listenfd, 20);
printf("Accepting connections ...\n");
while (1) {
cliaddr_len = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
printf("-------------------------%d\n", connfd);
pid = fork();
if (pid == 0) {
Close(listenfd);
while (1) {
n = Read(connfd, buf, MAXLINE);
if (n == 0) {
printf("the other side has been closed.\n");
break;
}
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for (i = 0; i < n; i++)
buf[i] = toupper(buf[i]);
Write(STDOUT_FILENO, buf, n);
Write(connfd, buf, n);
}
Close(connfd);
return 0;
} else if (pid > 0) {
Close(connfd);
} else
perr_exit("fork");
}
return 0;
}
3.客户端代码
代码思路:创建connfd并调用connect与server的ip和端口通信,值得注意的是,connfd是可以不使用bind与client的ip和端口绑定,在调用connect的时候,会自动绑定。
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SER_PORT 8000
int main(void)
{
int sockfd;
struct sockaddr_in serveraddr;
int len;
char wbuf[1024],rbuf[1024];
sockfd = socket(AF_INET,SOCK_STREAM,0);
char ipstr[]="127.0.0.1";
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SER_PORT);
inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);
connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
while(1)
{
memset(wbuf,0,sizeof(wbuf));
memset(rbuf,0,sizeof(rbuf));
printf("send to server:");
fgets(wbuf,sizeof(wbuf),stdin);
write(sockfd,wbuf,strlen(wbuf));
len=read(sockfd,rbuf,sizeof(rbuf));
if(len==0)
{
printf("server is close....\n");
}
printf("receive from server:%s",rbuf);
}
close(sockfd);
return 0;
}
二、select
1.IO复用的概念
在socket模型中,我们阻塞监听listenfd,使进程阻塞在accept中。IO复用是指,非阻塞循环监听server中的connfd和listenfd,当有fd准备好,进程运行到非阻塞循环监听函数时,会运行响应的程序。 多路复用IO就是用一条线程,同时监听多个IO请求,并且在有IO请求产生的时候返回。注意,虽然我们的IO多路复用也会阻塞,但是这里的阻塞是应用层面的,也就是说在多路复用的方法上进行阻塞,而不是在操作系统层面去阻塞。
2.常见函数详解
1.int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds: 被监听的文件描述符总数,它会比文件描述符表集合中的文件描述符表的最大值大1,因为文件描述符从0开始计数
readfds:需要监听的可读事件的文件描述符集合(如果有动静则该集合只会保留产生可读事件)看下面实例
writefds:需要监听的可写事件的文件描述符集合
exceptfds:需要监听的异常事件的文件描述符集合
timeout:即告诉内核select等待多长时间之后就放弃等待。一般设为NULL 表示无动静就阻塞等待
2.set相关函数,set是一个1024的数组,可以记录每个fd。
FD_ZERO(fd_set* set)
理解为初始化文件描述符几何 (既把所有位都设置为0)
FD_SET(fd,fd_set* set)
理解为把文件描述符加入集合(本质为对应fd位设置为1)
FD_CLR(fd,fd_set* set)
理解为把文件描述符从集合取出(本质为对应fd位设置为0)
FD_ISSET(fd,fd_set* set)
理解为检测改文件描述符是否在集合(本质为对应的fd位是否设置为1)
3.服务器代码
代码思路:创建listenfd并将其放到fd集合set中,调用select阻塞监听集合set,当select返回时,判断set中是否还存在listenfd,如果有则说明有新的client与server通信,调用accept返回client的ip和端口并得到一个新的connfd,将其放到set中。继续阻塞监听select的set,若返回的set有listenfd则调用accept,将新的client的connfd加入到集合中,若是返回的set有connfd则与对应的client通信。
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SET_PORT 8000
int main(int argc, char *argv[])
{
int sockfd,connfd;
struct sockaddr_in serveraddr;
int i;
int on = 1;
sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) ;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SET_PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr *)&serveraddr,sizeof(serveraddr));
listen(sockfd,128) ;
int maxfd = sockfd;
int client[FD_SETSIZE];
fd_set rset;
fd_set allset;
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(sockfd,&allset);
int nready;
for(i = 0; i < FD_SETSIZE; i++)
client[i] = -1;
while(1)
{
rset = allset;
nready = select(maxfd+1,&rset,NULL,NULL,NULL);
if(FD_ISSET(sockfd,&rset))
{
struct sockaddr_in clientaddr;
memset(&clientaddr,0,sizeof(clientaddr));
int len = sizeof(clientaddr);
connfd = accept(sockfd, (struct sockaddr*)&clientaddr,&len);
char ipstr[128];
printf("client ip%s ,port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),
ntohs(clientaddr.sin_port));
for(i = 0; i < FD_SETSIZE; i++)
{ if(client[i] < 0)
{
client[i] = connfd;
break;
}
}
FD_SET(connfd,&allset);
if(connfd > maxfd)
maxfd = connfd;
if(--nready <= 0)
continue;
}
else
{
for(i = 0; i < FD_SETSIZE; i++)
{
if(FD_ISSET(client[i],&rset))
{
connfd = client[i];
char buf[1024] = {0};
int nread ;
nread = read(connfd, buf, sizeof(buf));
if(nread == 0)
{
printf("client is close..\n");
FD_CLR(connfd, &allset);
client[i] = -1;
close(connfd);
}
else
{
write(connfd,buf,nread);
memset(buf,0,1024);
}
if(--nready <= 0)
break;
}
}
}
}
return 0;
}
三.epool
1.epool和select的区别
epoll不存在集合的覆盖 epoll_create会返回一个fd,指向空间包含全部的事件(结构体) epoll把要监听的每一个fd都包装成一个事件,并把这个事件记入epollfd 让epollfd来监听 select产生动静是吧fd放入集合 但是epoll通过epoll_wait 把产生动静的fd所包装好的事件放入结构体数组 select需要备份,需要重新创建数组放fd循环比对,epoll直接通过包装好的事件(结构体)就能获得fd,效率也更快(差别主要体现在这) 两者的区别是的select适合用户客服端不多的情况,而epoll没有客户端的上限
2.常用的函数
#include <sys/epoll.h>
int epoll_create(int size);
作用:创建一个epoll句柄,告诉他需要监听的数目(也可以理解成申请一片空间,用于存放监听的套接字)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
作用:控制某个epoll监控的文件描述符上的事件:注册,修改、删除(也就是增添 删除 修改一个事件)
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
作用:监听红黑树上的事件,将产生动静的事件放在event这个数组内,
int epoll_create(int size);
参数一:通知内核监听size个fd,只是个建议值并与硬件有关系。(从 Linux 内核 2.6.8 版本起,size 这个参数就被忽略了,只要求 size 大于 0 即可)
返回值:返回epoll句柄(fd)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数一:int epfd:epoll_create()的返回值
参数二:int op: 表示动作,用三个宏来表示
EPOLL_CTL_ADD(注册新的fd到epfd)
EPOLL_CTL_MOD(修改已经注册的fd监听事件)
EPOLL_CTL_DEL(从epfd删除一个fd)
参数三:int fd 操作对象(socket)
参数四:struct epoll_evevt* evevt; 告诉内核需要监听的事件
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
参数一:int epfd:epoll_create()函数返回值
参数二:struct epoll_events* events用于回传代处理事件的数组(也就是存放产生动静的事件)
参数三:int maxevents 同时最多产生多少事件,告诉内核events有多大,该值必须大于0
参数四:int timeout表示 -1相当于阻塞,0相当于非阻塞,超时时间(单位:毫秒)
返回值:成功返回产生动静事件的个数
struct epoll_event {
__uint32_t events; 宏定义读和写EPOLLIN读EPOLLOUT,边缘触发EPOLLET
epoll_data_t data; 联合体
};
联合体如下:
typedef union epoll_data {
void *ptr;//此指针可以指向一个带有函数的结构体,当有对应的事件发生时,可以将次指针指向的结构体内的函数手动进行调用。这就是反应堆模型。
int fd; //一般通过此fd判断是否是对应的listenfd或connfd进行判断是否有新的client链接和哪个旧的client在发送数据
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
Level Triggered (LT) 水平触发只要有数据都会触发
3.服务区代码
代码思路:epool_create创建红黑树,返回epollfd并指向红黑树。设置listenfd的event结构体,调用epool_ctl将listenfd上树,epool_wait监听上树的fd,epool_wait有响应时,通过epool_wait的传出参数struct epoll_event * events检查fd,如果events中listenfd则表示有新的client与server通信,这时调用accept得到connfd和client的ip和端口,并将新的connfd上树,再监听红黑树,当有旧的client通信则利用connfd与其通信。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define MAXLINE 80
#define SERV_PORT 8000
#define OPEN_MAX 1024
int main(int argc,char *argv[])
{
int maxi,sockfd,connfd;
int nready,efd;
struct sockaddr_in clientaddr, serveraddr;
struct epoll_event event, events[OPEN_MAX];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&serveraddr, sizeof(serveraddr));
bzero(&clientaddr, sizeof(clientaddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERV_PORT);
int on =1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) ;
bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr));
listen(sockfd, OPEN_MAX);
efd=epoll_create(OPEN_MAX);
event.events=EPOLLIN|EPOLLET;
event.data.fd=sockfd;
epoll_ctl(efd,EPOLL_CTL_ADD,sockfd,&event);
while(1)
{
nready=epoll_wait(efd,events,OPEN_MAX,-1);
for(int i=0;i<nready;i++)
{
if(!(events[i].events & EPOLLIN))
continue;
if (events[i].data.fd==sockfd)
{
int len=sizeof(clientaddr);
char ipstr[128];
connfd=accept(sockfd,(struct sockaddr *)&clientaddr,&len);
printf("client ip%s ,port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),
ntohs(clientaddr.sin_port));
event.events = EPOLLIN|EPOLLET;
event.data.fd = connfd;
epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);
}
else
{
connfd=events[i].data.fd;
char buf [1024];
memset(buf,0,1024);
int nread;
nread=read(connfd,buf,sizeof(buf));
if(nread==0)
{
printf("client is close..\n");
epoll_ctl(efd, EPOLL_CTL_DEL, connfd, NULL);
close(connfd);
}
else
{
printf("%s",buf);
}
}
}
}
}
四、udp通信
1.实现方法及其代码
创建socket时选择SOCK_DGRAM,则选择了用udp的协议,udp一次读写,没有稳定的链接,一般直接用recvfrom和sendto进行读写。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
sockfd: 套接字
buf:缓冲区地址
len:缓冲区大小
flags: 0
src_addr:(struct sockaddr *)&addr 传出。 对端地址结构
addrlen:传入传出。
返回值: 成功接收数据字节数。 失败:-1 errn。 0: 对端关闭。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd: 套接字
buf:存储数据的缓冲区
len:数据长度
flags: 0
src_addr:(struct sockaddr *)&addr 传入。目标地址结构
addrlen:地址结构长度。
返回值:成功写出数据字节数。 失败 -1, errno
server端
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#define SERV_PORT 8000
int main(void)
{
struct sockaddr_in serv_addr, clie_addr;
socklen_t clie_addr_len;
int sockfd;
char buf[BUFSIZ];
char str[INET_ADDRSTRLEN];
int i, n;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
printf("Accepting connections ...\n");
while (1) {
clie_addr_len = sizeof(clie_addr);
n = recvfrom(sockfd, buf, BUFSIZ,0, (struct sockaddr *)&clie_addr, &clie_addr_len);
if (n == -1)
perror("recvfrom error");
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
ntohs(clie_addr.sin_port));
for (i = 0; i < n; i++)
buf[i] = toupper(buf[i]);
n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
if (n == -1)
perror("sendto error");
}
close(sockfd);
return 0;
}
client端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
int sockfd, n;
char buf[BUFSIZ];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while (fgets(buf, BUFSIZ, stdin) != NULL) {
n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (n == -1)
perror("sendto error");
n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0);
if (n == -1)
perror("recvfrom error");
write(STDOUT_FILENO, buf, n);
}
close(sockfd);
return 0;
}
五、本地套接字
1.实现要点
Socket(AF_UNIX, SOCK_STREAM, 0)选择AF_UNIX,则选择了本地通信。
在网络通信中,使用sockaddr_in去设置ip和端口,并在bind和accept等函数中将其临时强转成sockaddr,这是历史原因造成的。但在本地通信中,设置的是sockaddr_un结构体,其108字节的空间并未全部使用,所以得用offsetof进行计算其长度,而不能想sockaddr_in中使用sizeof(判断某种类型的空间大小)计算。 len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);
struct sockaddr_in { __kernel_sa_family_t sin_family; /* Address family */ 地址结构类型
__be16 sin_port; /* Port number */ 端口号
struct in_addr sin_addr; /* Internet address */ IP 地址
};
struct sockaddr_un {
__kernel_sa_family_t sun_family; /* AF_UNIX */ 地址结构类型
char sun_path[UNIX_PATH_MAX]; /* pathname */ socket 文件名(含路径)
};
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stddef.h>
#include "wrap.h"
#define SERV_ADDR "serv.socket"
int main(void)
{
int lfd, cfd, len, size, i;
struct sockaddr_un servaddr, cliaddr;
char buf[4096];
lfd = Socket(AF_UNIX, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, SERV_ADDR);
len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);
unlink(SERV_ADDR);
Bind(lfd, (struct sockaddr *)&servaddr, len);
Listen(lfd, 20);
printf("Accept ...\n");
while (1) {
len = sizeof(cliaddr);
cfd = Accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t *)&len);
len -= offsetof(struct sockaddr_un, sun_path);
cliaddr.sun_path[len] = '\0';
printf("client bind filename %s\n", cliaddr.sun_path);
while ((size = read(cfd, buf, sizeof(buf))) > 0) {
for (i = 0; i < size; i++)
buf[i] = toupper(buf[i]);
write(cfd, buf, size);
}
close(cfd);
}
close(lfd);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
int sockfd, n;
char buf[BUFSIZ];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while (fgets(buf, BUFSIZ, stdin) != NULL) {
n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (n == -1)
perror("sendto error");
n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0);
if (n == -1)
perror("recvfrom error");
write(STDOUT_FILENO, buf, n);
}
close(sockfd);
return 0;
}
|