一、socket客户端配置步骤
昨天以及完成了服务器的配置,已经可以让客户端连接,但是还无法达到数据交互,就差一个可以正式达到聊天效果的客户端。在正式编程之前,我们先来回顾一下客户端的编程步骤:
其中有一个连接服务的函数connect();并没有学过,来=看看一下这个函数:
1.连接函数connect()
连接服务器,只用于客户端:
函数原型: int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数说明:
1. sockfd 网络描述符,为socket() 返回值;
2. *addr IP地址和端口的配置结构体,配置需要连接的服务器的IP地址和端口;
3. addrlen 为 *addr 的大小;
返回值: 成功返回0;失败返回 -1;
小笔记:
昨天在配置客户端的时候,write和read 使用的描述符是 accept 返回的描述符,但是现在客户端不需要accept 函数,所以客户端的收发,write和read 所使用的描述符是socket() 返回的描述符。下面正式来编写客户端程序。
二、socket客户端配置
先起一个函数给客户端初始化函数吧:
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int socket_fd;
void Client_Init(char *ip_Addr,unsigned int port)//客户端初始化
{
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof( struct sockaddr_in));
ip_Addr=(char *)malloc(sizeof(char));
socket_fd=socket(AF_INET,SOCK_STREAM,0);//创建sokcet
//2.bind()
s_addr.sin_family=AF_INET;//配置协议为IPv4
s_addr.sin_port=htons(port);//配置端口
inet_aton(ip_Addr,&s_addr.sin_addr);//配置IP地址
if(connect(socket_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in))==-1){
perror("connect");
exit(-1);
}else {
printf("connect server success!\n");
}
free(ip_Addr);//释放内存
}
有了这个函数,方便我们连接服务器IP地址和端口号,后面运行时,带参数输入,就免去因为服务器的IP的改变而改来改去的麻烦。其实到了这里,只要在主函数里调用就可以连接服务器了,我们调用一下:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int socket_fd;
void Client_Init(char *ip_Addr,unsigned int port)
{
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof( struct sockaddr_in));
ip_Addr=(char *)malloc(sizeof(char));
socket_fd=socket(AF_INET,SOCK_STREAM,0);
//2.bind()
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(port);
inet_aton(ip_Addr,&s_addr.sin_addr);
if(connect(socket_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in))==-1){
perror("connect");
exit(-1);
}else {
printf("connect server success!\n");
}
}
int main(int argc,char **argv)
{
if(argc!=3){
printf("input error!");
exit(-1);
}
Client_Init(argv[1],atoi(argv[2]));
//write/read
return 0;
}
连接成功后,会打印connect server success!,为了更加直观一些,我们先改一下服务器的代码:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int status=10;
int socket_fd;
struct sockaddr_in a_addr;
void Server_Init(char *ip_Addr,unsigned int port)
{
int bind_ret;
struct sockaddr_in s_addr;
ip_Addr=(char *)malloc(sizeof(char ));
memset(&s_addr,0,sizeof( struct sockaddr_in));
//1.socket
socket_fd=socket(AF_INET,SOCK_STREAM,0);
//2.bind()
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(port);
inet_aton(ip_Addr,&s_addr.sin_addr);
bind_ret=bind(socket_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
free(ip_Addr);
//3.listen()
listen(socket_fd,10);
}
int main(int argc,char **argv)
{
int addrlen;
int accept_num;
if(argc!=3){
printf("input error!\n");
exit(-1);
}
Server_Init(argv[1],atoi(argv[2]));
//4.accept()
addrlen=sizeof(struct sockaddr_in);
memset(&a_addr,0,sizeof( struct sockaddr_in));
printf("wait Client...\n");
accept_num=accept(socket_fd,(struct sockaddr *)&a_addr,&addrlen);
if(accept_num==-1){
perror("accept");
}
printf("Connect client ip=%s\n",inet_ntoa(a_addr.sin_addr));
while(1);
return 0;
}
服务器的IP地址和端口也过参数传入,来运行看看:
三、实现双方聊天
说一下思路,先从服务器开始吧:
服务在listen 之后会卡在accept 处,等待客户端的连接,那么我们就可以在while循环里不断地检测客户端的连接,这么可以达到一个可以连接多个客户端的目的,这个功能先不详细说;连接之后,分别创建子进程来发送数据,和接收数据,这样就可以实现接收和发送两不误。
客户端也是一样的,在connect()之后,在while循环里分别创建两个子进程,来负责接收和发送。
1.服务器发送和接收消息代码:
while(1){
accept_num=accept(socket_fd,(struct sockaddr *)&a_addr,&addrlen);
if(accept_num==-1){
perror("accept");
}
printf("Connect client ip=%s\n",inet_ntoa(a_addr.sin_addr));
//write/read
if(fork()==0){//创建子进程负责读取
while(1){
memset(readBUf,0,sizeof(readBUf));
s_read=read(accept_num,readBUf,sizeof(readBUf));
if(s_read==-1){
perror("read");
}else{
printf("read:%s\n",readBUf);
}
}
}
if(fork()==0){//创建子进程 负责发送
while(1){
memset(msg,0,sizeof(msg));
printf("input:");
fgets(msg,strlen(msg),stdin);//输入发送内容
write(accept_num,msg,strlen(msg));//发送
}
}
}
2.客户端发送和接收消息代码:
while(1){
if(fork()==0){//创建子进程发送
while(1){
printf("input:");
memset(msg,0,sizeof(msg));//每次都清空消息,防止内容重复
fgets(msg,sizeof(msg),stdin);
write(socket_fd,msg,strlen(msg));
}
}
if(fork()==0){//创建子进程不断接收
while(1){
memset(readBUf,0,sizeof(readBUf));//每次都清空消息,防止内容重复
s_read=read(socket_fd,readBUf,128);//父进程不断读入
if(s_read==-1){
perror("read");
}else{
printf("Read buf=%s\n",readBUf);
}
}
}
}
3.运行结果
总结
以这种思路的来做,就可以做到多方消息收发,但是目前还很难决定服务器在众多客服端中指定的发送,就是我们还无法控制服务器该给谁发送。现在的情况时,服务器在发送一个消息之后,众多客户端会争夺这个消息,就像进程一样,会争夺资源。这个问题需要更深入的学习,但是学习的重点不在此处,没必要去浪费学习时间,以后有时间可以去试着让服务端指定给某个客户端发消息。
网络的编程会在这里告一段落,明天开始就是整个Linux初学的检验项目,类FTP文件云盘,实现客户端下载或上传服务器的文件。