2017-2018-1 20155222实验三 实时系统
1.学习使用Linux命令wc
基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
客户端传一个文本文件给服务器
服务器返加文本文件中的单词数
上方提交代码
附件提交测试截图,至少要测试附件中的两个文件
2.使用多线程实现wc服务器并使用同步互斥机制保证计数正确
上方提交代码
下方提交测试
对比单线程版本的性能,并分析原因
linux socket编程常用函数
-
socket()
我们使用系统调用socket()来获得文件描述符:#include<sys/types.h> #include<sys/socket.h> int socket(int domain,int type,int protocol);
第一个参数domain设置为“AF_INET”。
第二个参数是套接口的类型:SOCK_STREAM或
SOCK_DGRAM。第三个参数设置为0。
系统调用socket()只返回一个套接口描述符,如果出错,则返回-1。 -
bind()
一旦你有了一个套接口以后,下一步就是把套接口绑定到本地计算机的某一个端口上。但如果你只想使用connect()则无此必要。
下面是系统调用bind()的使用方法:#include<sys/types.h> #include<sys/socket.h> intbind(int sockfd,struct sockaddr*my_addr,int addrlen);
第一个参数sockfd是由socket()调用返回的套接口文件描述符。
第二个参数my_addr是指向数据结构sockaddr的指针。数据结构sockaddr中包括了关于你的地址、端口和IP地址的信息。
第三个参数addrlen可以设置成sizeof(structsockaddr)。
下面是一个例子:#include<string.h> #include<sys/types.h> #include<sys/socket.h> #define MYPORT 3490 main() { int sockfd; struct sockaddr_inmy_addr; sockfd=socket(AF_INET,SOCK_STREAM,0); my_addr.sin_family=AF_INET; my_addr.sin_port=htons(MYPORT); my_addr.sin_addr.s_addr=inet_addr("132.241.5.10"); bzero(&(my_addr.sin_zero),8); bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));
如果出错,bind()也返回-1。
如果你使用connect()系统调用,那么你不必知道你使用的端口号。当你调用connect()时,它检查套接口是否已经绑定,如果没有,它将会分配一个空闲的端口。 -
connect()
系统调用connect()的用法如下:#include<sys/types.h> #include<sys/socket.h> int connect(int sockfd,struct sockaddr* serv_addr,int addrlen);
第一个参数还是套接口文件描述符,它是由系统调用socket()返回的。
第二个参数是serv_addr是指向数据结构sockaddr的指针,其中包括目的端口和IP地址。
第三个参数可以使用sizeof(structsockaddr)而获得。
下面是一个例子:#include<string.h> #include<sys/types.h> #include<sys/socket.h> #define DEST_IP "132.241.5.10" #define DEST_PORT 23 main() { intsockfd; structsockaddr_indest_addr; sockfd=socket(AF_INET,SOCK_STREAM,0); dest_addr.sin_family=AF_INET; dest_addr.sin_port=htons(DEST_PORT); dest_addr.sin_addr.s_addr=inet_addr(DEST_IP); bzero(&(dest_addr.sin_zero),8); connect(sockfd,(structsockaddr*)&dest_addr,sizeof(struct sockaddr));
同样,如果出错,connect()将会返回-1。
-
listen()
如果你希望不连接到远程的主机,也就是说你希望等待一个进入的连接请求,然后再处理它们。这样,你通过首先调用listen(),然后再调用accept()来实现。
系统调用listen()的形式如下:intl isten(int sockfd,int backlog);
第一个参数是系统调用socket()返回的套接口文件描述符。
第二个参数是进入队列中允许的连接的个数。进入的连接请求在使用系统调用accept()应答之前要在进入队列中等待。这个值是队列中最多可以拥有的请求的个数。大多数系统的缺省设置为20。你可以设置为5或者10。当出错时,listen()将会返回-1值。
当然,在使用系统调用listen()之前,我们需要调用bind()绑定到需要的端口,否则系统内核将会让我们监听一个随机的端口。所以,如果你希望监听一个端口,下面是应该使用的系统调用的顺序:
socket();
bind();
listen(); -
accept()
系统调用accept()比较起来有点复杂。在远程的主机可能试图使用connect()连接你使用
listen()正在监听的端口。但此连接将会在队列中等待,直到使用accept()处理它。调用accept()
之后,将会返回一个全新的套接口文件描述符来处理这个单个的连接。这样,对于同一个连接
来说,你就有了两个文件描述符。原先的一个文件描述符正在监听你指定的端口,新的文件描
述符可以用来调用send()和recv()。
调用的例子如下:#include<sys/socket.h> intaccept(intsockfd,void*addr,int*addrlen);
第一个参数是正在监听端口的套接口文件描述符。第二个参数addr是指向本地的数据结构
sockaddr_in的指针。调用connect()中的信息将存储在这里。通过它你可以了解哪个主机在哪个
端口呼叫你。第三个参数同样可以使用sizeof(structsockaddr_in)来获得。
如果出错,accept()也将返回-1。下面是一个简单的例子:#include<string.h> #include<sys/types.h> #include<sys/socket.h> #define MYPORT 3490 #define BACKLOG 10 main() { intsockfd,new_fd; structsockaddr_inmy_addr; structsockaddr_intheir_addr; intsin_size; sockfd=socket(AF_INET,SOCK_STREAM,0); my_addr.sin_family=AF_INET; my_addr.sin_port=htons(MYPORT); my_addr.sin_addr.s_addr=INADDR_ANY; bzero(&(my_addr.sin_zero),8); bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr)); listen(sockfd,BACKLOG); sin_size=sizeof(structsockaddr_in); new_fd=accept(sockfd,&their_addr,&sin_size);
下面,我们将可以使用新创建的套接口文件描述符new_fd来调用send()和recv()。
-
send() 和recv()
系统调用send()的用法如下:int send(int sockfd,const void* msg,int len,int flags);
第一个参数是你希望给发送数据的套接口文件描述符。它可以是你通过socket()系统调用返回的,也可以是通过accept()系统调用得到的。
第二个参数是指向你希望发送的数据的指针。
第三个参数是数据的字节长度。第四个参数标志设置为0。
下面是一个简单的例子:char*msg="Beejwashere!"; intlen,bytes_sent; .. len=strlen(msg); bytes_sent=send(sockfd,msg,len,0); ...
系统调用send()返回实际发送的字节数,这可能比你实际想要发送的字节数少。如果返回的字节数比要发送的字节数少,你在以后必须发送剩下的数据。当send()出错时,将返回-1。
系统调用recv()的使用方法和send()类似:
int recv(int sockfd,void* buf,int len,unsigned int flags);
第一个参数是要读取的套接口文件描述符。
第二个参数是保存读入信息的地址。
第三个参数是缓冲区的最大长度。第四个参数设置为0。
系统调用recv()返回实际读取到缓冲区的字节数,如果出错则返回-1。
这样使用上面的系统调用,你可以通过数据流套接口来发送和接受信息。 -
sendto() 和recvfrom()
因为数据报套接口并不连接到远程的主机上,所以在发送数据包之前,我们必须首先给出目的地址,请看:int sendto(int sockfd,const void* msg,int len,unsigned int flags,conststruct sockaddr*to,inttolen);
除了两个参数以外,其他的参数和系统调用send()时相同。
参数to是指向包含目的IP地址和端口号的数据结构sockaddr的指针。
参数tolen可以设置为sizeof(structsockaddr)。
系统调用sendto()返回实际发送的字节数,如果出错则返回-1。
系统调用recvfrom()的使用方法也和recv()的十分近似:int recvfrom(int sockfd,void* buf,int len,unsigned int flagsstruct sockaddr* from,int* fromlen);
参数from是指向本地计算机中包含源IP地址和端口号的数据结构sockaddr的指针。
参数fromlen设置为sizeof(struct sockaddr)。
系统调用recvfrom()返回接收到的字节数,如果出错则返回-1。 -
close() 和shutdown()
你可以使用close()调用关闭连接的套接口文件描述符:close(sockfd);
这样就不能再对此套接口做任何的读写操作了。
使用系统调用shutdown(),可有更多的控制权。它允许你在某一个方向切断通信,或者切断双方的通信:
int shutdown(int sockfd,int how);
第一个参数是你希望切断通信的套接口文件描述符。第二个参数how值如下:
0—Furtherreceivesaredisallowed
1—Furthersendsaredisallowed
2—Furthersendsandreceivesaredisallowed(likeclose())
shutdown()如果成功则返回0,如果失败则返回-1。
服务器:
#include "time.h"
#include<stdlib.h>
#include<pthread.h>
#include<sys/socket.h>
#include<sys/types.h> //pthread_t , pthread_attr_t and so on.
#include<stdio.h>
#include<netinet/in.h> //structure sockaddr_in
#include<arpa/inet.h> //Func : htonl; htons; ntohl; ntohs
#include<assert.h> //Func :assert
#include<string.h> //Func :memset
#include<unistd.h> //Func :close,write,read
#define SOCK_PORT 13222
#define BUFFER_LENGTH 1024
#define MAX_CONN_LIMIT 512 //MAX connection limit
struct sockaddr_in s_addr_client;
itoa(int n,char str[64]);
static void Data_handle(void * sock_fd); //Only can be seen in the file
int main()
{
int sockfd_server;
int sockfd;
int fd_temp;
struct sockaddr_in s_addr_in;
int client_length;
sockfd_server = socket(AF_INET,SOCK_STREAM,0); //ipv4,TCP
assert(sockfd_server != -1);
//before bind(), set the attr of structure sockaddr.
memset(&s_addr_in,0,sizeof(s_addr_in));
s_addr_in.sin_family = AF_INET;
s_addr_in.sin_addr.s_addr = htonl(INADDR_ANY); //trans addr from uint32_t host byte order to network byte order.
s_addr_in.sin_port = htons(SOCK_PORT); //trans port from uint16_t host byte order to network byte order.
fd_temp = bind(sockfd_server,(struct sockaddr *)(&s_addr_in),sizeof(s_addr_in));
if(fd_temp == -1)
{
fprintf(stderr,"bind error!
");
exit(1);
}
fd_temp = listen(sockfd_server,MAX_CONN_LIMIT);
if(fd_temp == -1)
{
fprintf(stderr,"listen error!
");
exit(1);
}
while(1)
{
printf("waiting for new connection...
");
pthread_t thread_id;
client_length = sizeof(s_addr_client);
//Block here. Until server accpets a new connection.
sockfd = accept(sockfd_server,(struct sockaddr *)(&s_addr_client),(socklen_t *)(&client_length));
if(sockfd == -1)
{
fprintf(stderr,"Accept error!
");
continue; //ignore current socket ,continue while loop.
}
printf("A new connection occurs!
");
pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd));
pthread_join(thread_id,NULL);
}
//Clear
int ret = shutdown(sockfd_server,SHUT_WR); //shut down the all or part of a full-duplex connection.
assert(ret != -1);
printf("Server shuts down
");
return 0;
}
static void Data_handle(void * sock_fd)
{
int fd = *((int *)sock_fd);
int i_recvBytes;
int i,count=0;
char buffer[1024];
char data_send[64]="hello";
//char * data_send = "Server has received your request!
";
//strcat(data_send,inet_ntoa(s_addr_client.sin_addr));
// puts(s_addr_client.sin_addr)
//strcat(data_send,"
20155222
");
while(1)
{
printf("waiting for request...
");
//Reset data.
//memset(data_recv,0,BUFFER_LENGTH);
// write(fd,data_send,strlen(data_send));
while((i_recvBytes = read(fd,buffer,1024))>0)
{
//count=0;
int flag = 0;
// printf("%d
",i_recvBytes);
// printf("%s
",buffer);
for(i=0;i<i_recvBytes;i++)
{
if(flag==0){
switch(buffer[i]){
case ' ':
count++;
break;
case '
':
count++;
break;
case '
':
count++;
break;
}
}
if(buffer[i]==' '||buffer[i]=='
'||buffer[i]=='
')flag=1;
else flag=0;
}
/*
for(i=0;i<i_recvBytes;i++)
if((buffer[i]==' '||buffer[i]==10)&&((buffer[i-1]>='A'&&buffer[i-1]<='Z')||(buffer[i-1]>='a'&&buffer[i-1]<='z')))
{
count++;
break;
}
i--;
for(;i<i_recvBytes;i++)
if((buffer[i]==' '||buffer[i]=='('||buffer[i]==' '||buffer[i]=='
')&&((buffer[i+1]>='A'&&buffer[i+1]<='Z')||(buffer[i+1]>='a'&&buffer[i+1]<='z')))
*/
//for(i=0;i<i_recvBytes;i++)
//if(buffer[i]==' '||buffer[i]=='
'||buffer[i]==' ')
// count++;
bzero(buffer,1024);
//printf("%d
",count);
}
//printf("fffaff
");
write(fd,data_send,strlen(data_send));
/*while((i_recvBytes = read(fd,buffer,1024))>0)
{
// puts(buffer);
for(i=0;i<i_recvBytes;i++)
if(buffer[i]!=' '&&buffer[i]!='
')
count++;
}
printf("a");*/
/*if(i_recvBytes == 0)
{
printf("Maybe the client has closed
");
break;
}
if(i_recvBytes == -1)
{
fprintf(stderr,"read error!
");
break;
}
if(strcmp(data_recv,"quit")==0)
{
printf("Quit command!
");
break; //Break the while loop.
}*/
// printf("read from client : %s
",data_recv);
//printf("%d",count);
bzero(data_send,64);
itoa(count,data_send);
puts(data_send);
if(write(fd,data_send,strlen(data_send)+1) == -1)
{
printf("send failure");
break;
}
time_t timep;
time (&timep);
break;
/*if(write(fd,ctime(&timep),strlen(ctime(&timep))) == -1)
{
break;
}*/
}
//Clear
printf("terminating current client_connection...
");
close(fd); //close a file descriptor.
pthread_exit(NULL); //terminate calling thread!
}
itoa(int n,char str[64])
{
int i=0,len;
char temp;
while(n>10)
{
str[i]=n%10+48;
// printf("%d,%c
",n,str[i]);
n/=10;
i++;
}
str[i]=n+48;
len=strlen(str);
for(i=0;i<len/2;i++)
{
temp=str[i];
str[i]=str[len-i-1];
str[len-i-1]=temp;
}
}
客户端:
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h> //pthread_t , pthread_attr_t and so on.
#include<stdio.h>
#include<netinet/in.h> //structure sockaddr_in
#include<arpa/inet.h> //Func : htonl; htons; ntohl; ntohs
#include<assert.h> //Func :assert
#include<string.h> //Func :memset
#include<unistd.h> //Func :close,write,read
#define SOCK_PORT 13225
#define BUFFER_LENGTH 1024
int main()
{
int sockfd;
int tempfd;
FILE *fp=NULL;
struct sockaddr_in s_addr_in;
char data_send[BUFFER_LENGTH]="aa";
char data_recv[BUFFER_LENGTH];
memset(data_send,0,BUFFER_LENGTH);
memset(data_recv,0,BUFFER_LENGTH);
sockfd = socket(AF_INET,SOCK_STREAM,0); //ipv4,TCP
if(sockfd == -1)
{
fprintf(stderr,"socket error!
");
exit(1);
}
//before func connect, set the attr of structure sockaddr.
memset(&s_addr_in,0,sizeof(s_addr_in));
s_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1"); //trans char * to in_addr_t
s_addr_in.sin_family = AF_INET;
s_addr_in.sin_port = htons(SOCK_PORT);
tempfd = connect(sockfd,(struct sockaddr *)(&s_addr_in),sizeof(s_addr_in));
if(tempfd == -1)
{
fprintf(stderr,"Connect error!
");
exit(1);
}
while(1)
{
if((fp=fopen("test2.txt","r"))==NULL)
{
printf("file no found");
break;
}
char buffer[1024]="";
printf("Please input something you wanna say(input "quit" to quit):
");
//scanf("%s",&data_send);
int ret;
//scanf("%[^
]",data_send); //or you can also use this
fseek(fp,0,SEEK_END);
int len = ftell(fp);
rewind(fp);
//printf("%ld
",len);
while(1)
{
// len-=1024;
//printf("%d
",len);
if(len<=0)
break;
ret=fgets(buffer,1024,fp);
// printf("%d
",ret);
// printf("%s",buffer);
// printf("bbb
");
tempfd = write(sockfd,buffer,strlen(buffer));
// printf("%d
",tempfd);
if(tempfd<=0)
{
printf("error
");
break;
}
len-=strlen(buffer);
bzero(buffer,1024);
}
//fread(buffer,1,len,fp);
//write(sockfd,buffer,strlen(buffer));
printf("finish send
");
if(shutdown(sockfd,1)==1)
printf("shutdown writting");
if(strcmp(data_send,"quit") == 0) //quit,write the quit request and shutdown client
{
break;
}
else
while(1){
tempfd = read(sockfd,data_recv,BUFFER_LENGTH);
if(tempfd<=0)
exit(0);
//assert(tempfd != -1);
printf("%s
",data_recv);
memset(data_send,0,BUFFER_LENGTH);
memset(data_recv,0,BUFFER_LENGTH);
}
}
int ret = shutdown(sockfd,SHUT_WR); //or you can use func close()--<unistd.h> to close the fd
assert(ret != -1);
return 0;
}