利用socket套接字聊天
套接字地址结构体
struct sockaddr_in{
unsigned short sin_family;
unsigned short sin_port;//端口
struct in_addr sin_addr;//ip
unsigned char sin_zero[8];
};
另外一个通用的结构
struct sockaddr{
unsigned short sa_family;
char sa_data[14];
};
//存放ip的结构体
struct in_addr{
unsigned int a_addr;
};//将ip放在一个结构体中,是套接字接口早期不幸的产物
以上结构体都在netinet/in.h
无论何时我们都可以将sockaddr_in强制转换为sockaddr
typedef struct sockaddr SA;
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);
int connect(int sockfd,struct sockaddr *serv_addr,int addrlen);
int bind(int sockfd,struct sockaddr *my_addr,int addrlen);
int listen(int sockfd,int backlog);
int accept(int listenfd,struct sockaddr *addr,int *addrlen);
我们发现将socket和connect封装成一个open_clientfd函数是很方便的,客户端可以用它来和服务器建立连接
同样,将socket、bind、listen函数结合成一个open_listenfd的函数,服务端可以创建一个监听描述符。
--->
我将if((clientfd=socket(AF_INET,SOCK_STREAM,0))<0)写成了if(clientfd=socket(AF_INET,SOCK_STREAM,0)<0)导致了严重的错误
注意端口号是否被占用
<---
------------分割------------open.h
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>//use inet_aton();
#include <netinet/in.h>//use htons();
#include <sys/types.h>
#include <sys/socket.h>//use socket() connect()
typedef struct sockaddr SA;
int open_clientfd(char *ip,int port)
{
int clientfd;
struct sockaddr_in serveraddr;
bzero((char *)&serveraddr,sizeof(serveraddr));//设为0
if((clientfd=socket(AF_INET,SOCK_STREAM,0))<0)
return -1;
inet_aton(ip,&serveraddr.sin_addr);//ip
serveraddr.sin_port=htons(port);//port
serveraddr.sin_family=AF_INET;//family
if(connect(clientfd,(SA*)&serveraddr,sizeof(serveraddr))<0)
return -2;
return clientfd;
}
int open_listenfd(int port)
{
int listenfd;
struct sockaddr_in serveraddr;
bzero((char *)&serveraddr,sizeof(serveraddr));
if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
return -1;
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=INADDR_ANY;
serveraddr.sin_port=htons(port);
if(bind(listenfd,(SA*)&serveraddr,sizeof(serveraddr))<0)
return -2;
//bind(listenfd,(SA*)&serveraddr,sizeof(serveraddr));
printf("%d\n",listenfd);
if(listen(listenfd,1024)<0)return -3;
return listenfd;
}
---------------------------------open.h
第一版:客服端一直发送,服务端显示
客服端
----------------------------------echoclient.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "open.h"
#define max_long 1000
int main()
{
int clientfd;
clientfd=open_clientfd("127.0.0.1",1111);
printf("%d\n",clientfd);
while(1)
{
char buf[max_long];
bzero(&buf,max_long);
fgets(buf,max_long,stdin);
send(clientfd,buf,strlen(buf)-1,0);
}
close(clientfd);
return 0;
}
----------------------------------echoclient.c
服务端
----------------------------------echoserver.c
#include <stdio.h>
#include <unistd.h>
#include "open.h"
#include <string.h>
#define max_long 1000
void echo(int connfd);
int main()
{
int listenfd;
listenfd=open_listenfd(1111);
printf("%d\n",listenfd );
int connfd,addrlen;
struct sockaddr_in clientaddr;
while(1)
{
addrlen=sizeof(clientaddr);
connfd=accept(listenfd,(SA *)&clientaddr,&addrlen);
echo(connfd);
close(connfd);
}
return 0;
}
void echo(int connfd)
{
while(1)
{
char buf[max_long];
bzero(&buf,max_long);
recv(connfd,buf,max_long,0);
printf("%s\n",buf);
}
}
第二版 :可以通过进程的创建,一个用来接受,一个用来发送
但是存在两个问题:
1 当直接杀死某一端的时候,另一端会一直循环输出
2 没有妥善的出来子父进程之间的关系
客服端
----------------------------------echoclient.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "open.h"
#define max_long 1000
int main()
{
int clientfd;
char *ip="127.0.0.1";
clientfd=open_clientfd(ip,1111);
//printf("%d\n",clientfd);
if(clientfd>=0)
printf("已经连接上%s\n",ip);
pid_t pid;
pid=fork();
if(pid==0)//接受
{
while(1)
{
char buf[max_long];
bzero(&buf,max_long);
int n=recv(clientfd,buf,max_long,0);
if(buf[0]=='q')break;
printf("%s\n",buf);
}
}
else//发送
{
while(1)
{
char buf[max_long];
bzero(&buf,max_long);
fgets(buf,max_long,stdin);
send(clientfd,buf,strlen(buf)-1,0);
}
}
close(clientfd);
return 0;
}
----------------------------------echoclient.c
服务端
----------------------------------echoserver.c
#include <stdio.h>
#include <unistd.h>
#include "open.h"
#include <string.h>
#define max_long 1000
void echo(int connfd);
int main()
{
int listenfd;
listenfd=open_listenfd(1111);
//printf("%d\n",listenfd );
int connfd,addrlen;
struct sockaddr_in clientaddr;
while(1)
{
printf("等待别人连接:\n");
addrlen=sizeof(clientaddr);
connfd=accept(listenfd,(SA *)&clientaddr,&addrlen);
printf("%s已连接\n",inet_ntoa(clientaddr.sin_addr));
int pid;
pid=fork();
if(pid==0)//发送
{
while(1)
{
char buf[max_long];
bzero(&buf,max_long);
fgets(buf,max_long,stdin);
send(connfd,buf,strlen(buf)-1,0);
}
}
else//接受
{
echo(connfd);
}
close(connfd);
}
return 0;
}
void echo(int connfd)
{
while(1)
{
char buf[max_long];
bzero(&buf,max_long);
int n=recv(connfd,buf,max_long,0);
if(buf[0]=='q')
{break;}
printf("%s\n",buf);
}
}
这里是web服务器第一版
通过1118端口,简单的响应浏览器的访问
#include <stdio.h>
#include <unistd.h>
#include "open.h"
#include <string.h>
#define max_long 10000
typedef struct sockaddr SA;
int main()
{
int listenfd;
listenfd=open_listenfd(1118);
if(listenfd<0)return -1;
int connfd,addrlen;
struct sockaddr_in clientaddr;
while(1)
{
printf("waite connect...\n");
addrlen=sizeof(clientaddr);
connfd=accept(listenfd,(SA *)&clientaddr,&addrlen);
printf("connect success\n");
FILE *client=fdopen(connfd,"w");
char buf[max_long];
//发送给浏览器阅读的头信息
fprintf(client,"HTTP/1.1 200 OK\r\nServer:Testhttp server\r\nContent-length:1000\r\nContent-type:text/html\r\n\r\n");
//需要显示的内容
fprintf(client,"<html><title>test</title><body><p>this is a p<a href="">99999</a></p></body></html>");
//sleep(10);
fclose(client);
}
return 0;
}