int socket(int family, int type, int protocol)
/*
功能:对应于普通文件的打开操作
普通文件的打开操作返回一个文件描述字,socket用于创建一个socket描述符(socket descriptor), 唯一标识一个socket。
返回值:成功返回套接字文件描述符,失败返回-1
参数:int family 套接字使用的地址结构类型
AF_INET ipv4 / AF_INET6 ipv6 / AF_UNIX 本地套接字
int type 套接字本身的类型
1 SOCK_STREAM:TCP 2 SOCK_DGRAM:UDP
int protocol 套接字协议(常写默认0)
*/
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen)
/*
绑定一个固定的网络地址和端口号(占用端口)
返回值:成功 0 失败 -1
参数:int sockfd 套接字文件描述符
const struct sockaddr *myaddr 传入参数,(自己的)地址结构首地址
struct sockaddr_in servaddr;
用法:
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY指定地址为0.0.0.0的地址,表示不确定地址,或所有地址、任意地址
servaddr.sin_port = htons(6666); //端口号
socklen_t addrlen(int 类型),套接字地址结构的长度
*/
int listen(int sockfd, int backlog)
/*
让套接字具有接受客户端连接的请求
返回值:成功 0 失败 -1
参数:int sockfd:套接字文件描述符
int backlog:允许服务器端处于发起和完成态的总数上限
发起---三次握手---完成--accept--实例--close--关闭
*/
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)
/*
处于连接状态的套接字实例化
返回值:成功返回一个文件描述符 失败-1
返回时传出客户端的地址和端口号
参数:int sockfd 传入传出参数,传入一个文件描述符(监听套接字),传出一个新文件描述符(连接套接字)
struct sockaddr *cliaddr 若为NULL,表示不关心客户端的地址
socklen_t *addrlen 传入传出参数,传入的是调用者提供的缓冲区cliaddr的长度,传出的是客户端地址结构的实际长度
*/
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)
/*
客户端向服务器发起连接
返回值:成功 0 失败 -1
参数:sockfd 客户端套接字描述符
servaddr 目的地地址结构首地址
addrlen 地址结构长度
*/
int close(int fd)
/*
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作要关闭相应的socket描述字;
就像操作完打开的文件要调用fclose关闭打开的文件。
*/
/*
server.c
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
if(listen(listenfd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1) {
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
}
close(listenfd);
}
/*
client.c
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int sockfd, n;
char recvline[4096], sendline[4096];
struct sockaddr_in servaddr;
if(argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("send msg to server: \n");
fgets(sendline, 4096, stdin);
if(send(sockfd, sendline, strlen(sendline), 0) < 0){
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
close(sockfd);
exit(0);
}
/*
此为服务器程序:运行在后台
客户端:浏览器 http://localhost:8080/
*/
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define PORT 8080 //服务器监听端口
int main()
{
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(server_socket, 5);
int client_socket = accept(server_socket, NULL, NULL);
char buf[1024];
read(client_socket, buf, 1024);
printf("%s",buf);
char status[] = "HTTP/1.0 200 OK\r\n";
char header[] = "Server: DWBServer\r\nContent-Type: text/html;charset=utf-8\r\n\r\n";
char body[] = "<html><head><title>C语言构建小型Web服务器</title></head><body><h2>欢迎</h2><p>Hello,World</p></body></html>";
write(client_socket, status, sizeof(status));
write(client_socket, header, sizeof(header));
write(client_socket, body, sizeof(body));
close(client_socket);
close(server_socket);
return 0;
}
/*
web.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SERV_IP "127.0.0.1"
#define SERV_PORT 8000
#define ROOT "./www"
#define REPLY_HEAD "HTTP/1.1 200 OK\r\nContent-Type:"
#define TEXT "text/html"
#define IMG "image/jpg"
#define REPLY_END "\r\n\r\n"
void err_sys(const char *str) {
perror(str);
exit(1);
}
int main(void)
{
int lfd, cfd;
struct sockaddr_in serv_addr;
char buf[1024];
int n;
lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd == -1)
err_sys("socket error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);
n = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(n == -1)
err_sys("bind error");
n = listen(lfd, 20);
if(n == -1)
err_sys("listen error");
while(1){
char path[1024];
char *p, *q, *filename, *type;
int fd;
cfd = accept(lfd, NULL, NULL);
if(cfd == -1)
err_sys("accept error");
n = read(cfd, buf, 1024);
buf[n] = '\0';
p = strstr(buf, " ");
q = strstr(p + 1, " ");
*q = '\0';
filename = p + 2;
sprintf(path, "%s/%s", ROOT, filename);
n = access(path, X_OK);
if(n == 0){ /* cgi */
pid_t pid;
char *argv[2];
write(cfd, REPLY_HEAD, strlen(REPLY_HEAD));
write(cfd, TEXT, strlen(TEXT));
write(cfd, REPLY_END, strlen(REPLY_END));
pid = fork();
if(pid == 0){
dup2(cfd, STDOUT_FILENO);
argv[0] = path, argv[1] = NULL;
execvp(path, argv);
err_sys("exec error");
}else{
close(cfd);
wait(NULL);
}
}else{
p = rindex(filename, '.');
if(strcmp(p + 1, "jpg") == 0)
type = IMG;
else
type = TEXT;
fd = open(path, O_RDONLY);
if(fd != -1) {
write(cfd, REPLY_HEAD, strlen(REPLY_HEAD));
write(cfd, type, strlen(type));
write(cfd, REPLY_END, strlen(REPLY_END));
while(n = read(fd, buf, 1024))
write(cfd, buf, n);
close(fd);
}
close(cfd);
}
}
return 0;
}
/*
client.c
fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define ROOT "./cli"
#define CONN 1
#define READ 2
#define DONE 3
struct myfile
{
char *filename;
int fd;
int cfd;
int status;
};
/* ./app file1 file2 file3 ... */
int main(int argc, char *argv[])
{
int sfd, fd;
struct sockaddr_in serv_addr;
int n, i, flags;
int res, max_fd, nread, nleft;
struct myfile *list;
fd_set wset, rset, wtmp, rtmp;
n = argc - 1;
list = (struct myfile *)malloc(sizeof(struct myfile) * n);
for(i = 0; i < n; i++){
list[i].filename = argv[i + 1];
list[i].fd = -1;
list[i].cfd = -1;
list[i].status = 0;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8000);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
FD_ZERO(&wset); FD_ZERO(&rset);
max_fd = -1;
for(i = 0; i < n; i++){
sfd = socket(AF_INET, SOCK_STREAM, 0);
list[i].cfd = sfd;
if(sfd > max_fd)
max_fd = sfd;
flags = fcntl(sfd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(sfd, F_SETFL, flags);
res = connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(res == -1){
if(errno != EINPROGRESS){
perror("connect error");
return -1;
}
list[i].status = CONN;
FD_SET(sfd, &rset);
FD_SET(sfd, &wset);
}else if(res == 0){
char path[1024];
list[i].status = READ;
flags = fcntl(sfd, F_GETFL);
flags &= ~O_NONBLOCK;
fcntl(sfd, F_SETFL, flags);
FD_SET(sfd, &rset);
write(sfd, list[i].filename, strlen(list[i].filename));
sprintf(path, "%s/%s", ROOT, list[i].filename);
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
list[i].fd = fd;
}
}
nleft = n;
while(nleft > 0){
wtmp = wset, rtmp = rset;
select(max_fd + 1, &rtmp, &wtmp, NULL, NULL);
for(i = 0; i < n; i++){
if(list[i].status == DONE)
continue;
if(list[i].status == CONN){
if(FD_ISSET(list[i].cfd, &wtmp)){
if(FD_ISSET(list[i].cfd, &rtmp)){
printf("connect error...\n");
return -1;
}else{
char path[1024];
list[i].status = READ;
flags = fcntl(list[i].cfd, F_GETFL);
flags &= ~O_NONBLOCK;
fcntl(list[i].cfd, F_SETFL, flags);
FD_CLR(list[i].cfd, &wset);
write(list[i].cfd, list[i].filename, strlen(list[i].filename));
sprintf(path, "%s/%s", ROOT, list[i].filename);
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
list[i].fd = fd;
}
}
}else if(list[i].status == READ){
char buf[1024];
if(FD_ISSET(list[i].cfd, &rtmp)){
nread = read(list[i].cfd, buf, 1024);
if(nread == 0){
close(list[i].cfd);
close(list[i].fd);
FD_CLR(list[i].cfd, &rset);
list[i].status = DONE;
printf("%s done...\n", list[i].filename);
nleft--;
}else
write(list[i].fd, buf, nread);
}
}
}
printf("one round is over...\n");
}
return 0;
}
/*
server.c
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define ROOT "./serv"
int main(void)
{
int lfd, cfd, i, n;
struct sockaddr_in serv_addr;
pid_t pid;
struct sigaction act;
lfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8000);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(lfd, 20);
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &act, NULL);
while(1) {
cfd = accept(lfd, NULL, NULL);
pid = fork();
if(pid == 0) {
char buf[1024], path[1024];
int fd;
close(lfd);
n = read(cfd, buf, 1024);
buf[n] = '\0';
sprintf(path, "%s/%s", ROOT, buf);
fd = open(path, O_RDONLY);
while(n = read(fd, buf, 1024))
write(cfd, buf, n);
close(cfd);
return 0;
}
close(cfd);
}
return 0;
}