首先我们来说用到的函数
socket
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
*socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;
*应用程序可以像读写文件一样用read/write在网络上收发数据;
*如果socket()调用出错则返回-1;
*对于IPv4,family参数指定为AF_INET(第一个参数);
*对于TCP协议,type参数指定为SOCK_STREAM,面向字节流的传输协议(第二个参数)
*protocol指定为0就可以了(第三个参数)
bind
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接;服务器需要调用bind绑定一个固定的网络地址和端口号
bind成功返回0。失败返回-1
bind的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号;
前面讲过,struct sockaddr*是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而他们的长度各不一样,所以需要第三个参数addrlen指定结构体的长度;
我们程序中堆myaddr参数是这样初始化的:
bzero(&servaddr,sizeof(servaddr);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
1:将整个结构体清零;
2:设置地址类型为AF_INET;
3:网络地址为INADDR_ANY,这个宏可以表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这个设置可以在所有的IP地址上监听,知道与某个客户端建立了连接时才确定下来到底用哪个IP地址;
4:端口号为SERV_PORT,我们定义为9999
listen
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd,int backlog);
listen()声明sockfd处于监听状态,并且最多允许有backlog个客户短处与连接等待状态,如果接受到更多的连接请求就忽略,这里设置不会太大(一般是5)
listen()成功返回0;失败返回-1;
accept
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
三次握手完成后,服务器调用accept接受连接;
如果服务器调用accept时还没有客户端连接请求,就阻塞等待知道有客户端连接上来
addr是一个传出参数,accept返回时传出客户端的地址和端口号
如果addr参数传NULL,则说明不关心客户端的地址;
addrlen参数是一个传入传出参数,传入的是调用者提供的,缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);
我们的服务器程序结构是这样的
while(1)
{
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
n = read(connfd,buf,MAXLINE);
...
close(connfd);
}
connect
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
客户端需要调用connect连接服务器;
connect和bind的参数形式一直,区别在于bind参数是自己的地址,而connect的参数是对方的地址
connect成功返回0;失败返回-1;
服务器与客户端
server.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
printf("create socket error,errno is : %d, errstring is :%s\n",errno,strerror(errno));
}
struct sockaddr_in server_socket;
struct sockaddr_in client_socket;
bzero(&server_socket,sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_port = htons(_PORT_);
server_socket.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock,(struct sockaddr *)&server_socket,sizeof(struct sockaddr_in)) < 0)
{
printf("bind error,error code is %d,error string is :%s\n",errno,strerror(errno));
close(sock);
return 1;
}
if(listen(sock,_BACKLOG_) < 0)
{
printf("listen error,error code is %d,error string is %s\n",errno,strerror(errno));
close(sock);
return 2;
}
printf("bind and listen success,wait accept...\n");
for(;;)
{
socklen_t len = 0;
int client_sock = accept(sock,(struct sockaddr *)&client_socket,&len);
if(client_sock < 0)
{
printf("accept error, error is %d,errstring is %s\n",errno,strerror(errno));
close(sock);
return 3;
}
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip,'\0',sizeof(buf_ip));
inet_ntop(AF_INET,&client_socket.sin_addr,buf_ip,sizeof(buf_ip));
printf("get connect,ip is %s,port is %d\n",buf_ip,ntohs(client_socket.sin_port));
while(1)
{
char buf[1024];
memset(buf,'\0',sizeof(buf));//跟前面的初始化对比
read(client_sock,buf,sizeof(buf));
printf("client :# %s\n",buf);
printf("server :$");
memset(buf,'\0',sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
write(client_sock,buf,strlen(buf)+1);//在这里strlen(buf)+1和sizeof(buf)的区别
printf("please wait...\n");
}
}
close(sock);
return 0;
}
client.c
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <strings.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_PORT 9999
#define SERVER_IP "192.168.127.136"
int main(int argc,char *argv[])
{
if(argc != 2)
{
printf("usage :client IP\n");
return 1;
}
char *str = argv[1];
char buf[1024];
memset(buf,'\0',sizeof(buf));
struct sockaddr_in server_sock;
int sock = socket(AF_INET,SOCK_STREAM,0);
bzero(&server_sock,sizeof(server_sock));
server_sock.sin_family = AF_INET;
inet_pton(AF_INET,SERVER_IP,&server_sock.sin_addr);
server_sock.sin_port = htons(SERVER_PORT);
int ret = connect(sock,(struct sockaddr*)&server_sock,sizeof(server_sock));
if(ret < 0)
{
printf("connect failed...,errno is %d,errstring is %s\n",errno,strerror(errno));
}
printf("connect is success...\n");
while(1)
{
printf("client :#");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
write(sock,buf,sizeof(buf));
if(strncasecmp(buf,"quit",4) == 0)
{
printf("quit\n");
break;
}
printf("please wait...\n");
read(sock,buf,sizeof(buf));
printf("server :$ %s\n",buf);
}
close(sock);
return 0;
}
下面看实现的结果
但是我们现在再启动一个客户端,但是发现第二个客户端和服务器不能正常通信,原因就是我们在accept一个请求之后就进入了循环,一直在尝试read没有调用到accept,导致不能接受新的请求,所以我们还需要改进