
【Web-socket】服务器程序
socket编程
linux服务端代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<signal.h>
#include<fcntl.h>
//手动设置连接台数
#define CONNMAX 1000
char *WorkPath;
int listenfd, clients[CONNMAX];
void error(char *x);
void startServer(char *x);
void respond(int);
int main(int argc, char* argv[])
{
struct sockaddr_in clientaddr;
socklen_t addrlen;
char c, PORT[6];
//设置当前路径
WorkPath = getenv("PWD");
//默认端口
strcpy(PORT,"10000");
// 当前socket标识符下标
int slot=0;
//从命令行接收参数
while ((c = getopt (argc, argv, "p:r:")) != -1)
switch (c)
{
case 'r':
WorkPath = malloc(strlen(optarg));
strcpy(WorkPath,optarg);
break;
case 'p':
strcpy(PORT,optarg);
break;
case '?':
fprintf(stderr,"参数错误\n");
exit(1);
default:
exit(1);
}
printf("服务器开放端口 %s 使用 %s 作为当前路径\n",PORT,WorkPath);
// 设置所有的套接字标识符的默认值为-1,代表这个套接字标识号没有没有使用
int i;
for (i=0; i<CONNMAX; i++)
clients[i]=-1;
//先开启服务,执行流程中的1,2,3
startServer(PORT);
// 持续接收请求
while (1)
{
//包含addr所指向结构大小的数值,函数返回时包含对等地址(一般为服务器地址)的实际数值
addrlen = sizeof(clientaddr);
//4、接收来自客户端的连接请求 accept
//accept函数等待来自客户端的连接请求到达"监听描述符listenfd"----客户端会调用connect函数发送客户端的"套接字地址"【ip:port】
//accpet将客户端的套接字地址填写到clientaddr这个结构中,并返回“已连接描述符connfd”
clients[slot] = accept (listenfd, (struct sockaddr *) &clientaddr, &addrlen);
//出错返回-1
if (clients[slot]<0)
error ("accept()函数出错,接收失败");
else{
//accept返回“已连接描述符后”,创建子进程,让子程序完成response。父进程继续监听
if ( fork()==0 ){
//子进程应该关掉监听描述符【因为继续父进程全部变量】,只保留已连接描述符
close(listenfd);
//对连接作出响应
respond(slot);
exit(0);
}
}
//-1表示当前位置无描述符,这步用来寻找数组中下一个可用的已连接描述符的位置
while (clients[slot]!=-1)
slot = (slot+1)%CONNMAX;
//父进程应该关掉已连接描述符
close(clients[slot]);
}
return 0;
}
//服务端进程做的“监听准备”
void startServer(char *port)
{
//hits结构可用于提供要产生的套接字类型的信息
struct addrinfo hints, *res, *p;
// 将hints结构清空
memset (&hints, 0, sizeof(hints));
//使用的协议簇AF_INET=ipv4/AF_INET6=ipv6
hints.ai_family = AF_INET;
//协议类型,设置为流类型
hints.ai_socktype = SOCK_STREAM;
//设置标志为AI_PASSIVE,告诉函数,要产生的套接字,会被服务端当作“监听套接字”。否则默认返回"主动套接字"
hints.ai_flags = AI_PASSIVE;
//将NULL表示得到的套接字地址结构中,ip字段是通配符地址。告诉内核让这个服务器接受发送到此主机的全部请求
//返回结果是个链表,链表包含的是很多套接字地址,存入res中
if (getaddrinfo( NULL, port, &hints, &res) != 0)
{
perror ("getaddrinfo() 出错");
exit(1);
}
// 1、创建 套接字描述符
// 2、将“套接字描述符”与“套接字地址”绑定
for (p = res; p!=NULL; p=p->ai_next)
{
//若成功则返回 套接字描述符,若出错则返回-1
listenfd = socket (p->ai_family, p->ai_socktype, 0);
if (listenfd == -1)
continue;
//将“套接字描述符”与“套接字地址【ip:port】”绑定
//调用bind的函数之后,通过套接字描述符来读取或发送到套接字地址
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
break;
//绑定失败,关掉当前描述符并尝试下一个
close(listenfd);
}
if (p==NULL)
{
perror ("socket() 或 bind()");
exit(1);
}
//释放这个链表
freeaddrinfo(res);
// 3、转换成监听套接字
// 客户端发起的请求是主动实体,默认情况下,内核会认为socket函数创建的描述符是“主动套接字”,默认存在于客户端。但服务端,需要使用listen函数,
//来告诉内核,这个描述符是给服务端用的。
//listen函数将 这个描述符 从主动套接字,变成“监听套接字”。“监听套接字”可以接受客户端的连接请求。
if ( listen (listenfd, 1000000) != 0 )
{
perror("listen()函数转换出错");
exit(1);
}
}
//服务端的响应
void respond(int n)
{
//mesg缓冲区用来存放recv函数接收到的数据
char mesg[99999], *reqline[3], data_to_send[1024], path[99999];
//rcvd用来接收copy的字节数
int rcMegLen, file, bytes_read;
memset( (void*)mesg, (int)'\0', 99999 );
//5、从socket中读取字符
//第一个参数指定接收端套接字描述符
//第二个参数缓冲区用来存放recv函数接收到的数据;
//第三个参数指明缓冲区的长度;
//第四个参数一般置0。
rcMegLen=recv(clients[n], mesg, 99999, 0);
//如果recv在copy时出错,那么它返回SOCKET_ERROR
if (rcMegLen<0)
fprintf(stderr,("无法接收消息\n"));
//如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
else if (rcMegLen==0)
fprintf(stderr,"与客户端的连接意外中断.\n");
else // 接收到消息
{
//打印客户端请求信息
printf("%s\n", mesg);
//按换行进行切分消息
reqline[0] = strtok (mesg, " \t\n");
if ( strncmp(reqline[0], "GET\0", 4)==0 )
{
reqline[1] = strtok (NULL, " \t");
reqline[2] = strtok (NULL, " \t\n");
if (strncmp( reqline[2], "HTTP/1.0", 8)!=0 && strncmp( reqline[2], "HTTP/1.1", 8)!=0 )
{
write(clients[n], "HTTP/1.1 400 Bad Request\n", 25);
}
else
{
//如果url后面不写任何路径,默认为index.html
if ( strncmp(reqline[1], "/\0", 2)==0 )
reqline[1] = "/index.html";
strcpy(path, WorkPath);
strcpy(&path[strlen(WorkPath)], reqline[1]);
printf("向套接字描述符%d号发送了文件: %s\n",clients[n], path);
printf("========================================================\n");
//如果文件存在,向客户端发送这个文件,以只读方式打开文件
if ( (file=open(path, O_RDONLY))!=-1 )
{
// 5.5、向socket写入信息
//第一个参数指定发送端套接字描述符
//第二个参数指明一个存放应用程序要发送数据的缓冲区
//第三个参数指明实际要发送的数据的字节数
//第四个参数一般置0
send(clients[n], "HTTP/1.1 200 OK\n\n", 17, 0);
//向套接字发送数据
while ( (bytes_read=read(file, data_to_send, 1024))>0 )
write (clients[n], data_to_send, bytes_read);
}
// 否则提示此文件不存在
else write(clients[n], "HTTP/1.1 404 Not Found\n", 23);
}
}
}
//来禁止套接字上的输入/输出,使用SHUT_RDWR则将同时无法读取和发送数据
shutdown (clients[n], SHUT_RDWR);
//等到客商退出后才会close
//6、关闭socket
close(clients[n]);
//可以重新使用
clients[n]=-1;
}默认返回的index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>恭喜</title>
<style>
body{
background-color: antiquewhite;
background: url(./bgimage.jpg)no-repeat;
background-size: 100%;
}
.container {
width: 60%;
margin: 10% auto 0;
background-color: #f0f0f0;
padding: 2% 5%;
border-radius: 10px;
text-align:center;
}
ul {
padding-left: 20px;
}
ul li {
line-height: 2.3
}
a {
color: #20a53a
}
</style>
</head>
<body>
<div class="container">
<h1>访问成功</h1>
<h3>这是默认index.html</h3>
<ul>
</ul>
</div>
</body>
</html>- 文章作者:
- Lthero博客
- 版权声明:
- 本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议。转载请注明来自 Lthero博客!




