C语言能干什么?手把手教你写一个简单的聊天软件

一、服务端代码

因为端口号容易被占用的原因,所以IP地址和端口号采用参数传递的方法,即

int main(int argc,char **argv)

1、头文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

这些头文件全都是所调用函数需要的,比如read(),socket()等等,没有就会报错。

2、创建套接字(Socket)

int s_fd;    
s_fd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
if(s_fd == -1){
    perror("socket"); //打印错误信息
    exit(-1);
}

s_fd 表示套接字描述符,AF_INET表示IPv4因特网协议,SOCK_STREAM表示使用TCP协议,0表示选择对应默认协议。

3、绑定IP地址和端口号到Socket

struct sockaddr_in s_addr;
s_addr.sin_family = AF_INET;//IPv4协议
s_addr.sin_port = htons(atoi(argv[2]));//配置端口号,
    							//htons函数为网络字节序和主机字节序的转化
inet_aton(argv[1],&s_addr.sin_addr);//将IP地址配置到s_addr.sin_addr          
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//绑定

inet_aton: 把字符串形式的IP地址转为网络能识别的格式
htons: 网络字节序和主机字节序的转化

也就是说sockaddr_in里面有三个成员,有地址协议,监听端口号和IP地址,端口号是随便设置的,只要在端口号范围内就可以,

bind函数是绑定套接字和sockaddr_in结构体,这里应该要注意,bind函数里面第二个参数它需要的是sockaddr 但是我们用的sockaddr _in,所以要强制转化为sockaddr 类型,不然会报错,即(struct sockaddr *)&s_addr。

4、监听

绑定好IP地址和端口号后,服务器就需要监听来自客户端的请求

listen(s_fd,5);

s_fd 表示套接字返回的套接字描述符,5表示最大监听 5个。

5、连接

当服务器监听到客户端的请求时,就需要连接了,即

int c_fd;
struct sockaddr_in c_addr; //用来返回已经连接的客户端的协议地址
int clen = sizeof(struct sockaddr_in); //结构体长度
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen); //连接客户端
if(c_fd == -1){
 	perror("accept"); //打印错误信息
}

accept函数里面第二个参数它需要的是sockaddr ,但是我们用的sockaddr _in,所以要强制转化为sockaddr 类型c_fd 是一个新的套接字描述符,连接成功后对数据的操作,使用的都是这个新的套接字描述符

6、交换数据

char readBuf[1024];
char msg[1024];
int n_read;
memset(msg,0,sizeof(msg)); //将msg清空
memset(readBuf,0,sizeof(readBuf));//将readBuf清空
printf("input: "); //提示输入数据
gets(msg);         //数据放入到msg中
write(c_fd,msg,strlen(msg)); //将输入的数据写入到套接字描述符c_fd中的客户端
n_read = read(c_fd,readBuf,1024);//将套接字描述符c_fd中的数据读1024个到readBuf中
if(n_read == -1){
   perror("read"); //打印错误信息
}else{
   printf("get message:%s\n",readBuf); //将从客户端读到的数据打印
}

上面代码只能交换一次数据,不能循环的发送数据,所以需要死循环。在网络进程中,父进程一直等待客户端的服务请求,当收到客户端的请求时,父进程调用fork函数创建子进程,使子进程来处理,父进程则继续等待下一个客户端请求到达。

while(1){   //一直进行连接客户端
	c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
	if(c_fd == -1){
	 	perror("accept");
	}
	printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));//打印客户端的IP地址
	if(fork() == 0){ //连接到一个客户端后创建一个子进程来进行数据的交换
	    if(fork() == 0){ //再创建一个子进程来进行数据的写入
	        while(1){ //可以一直写入数据
	            memset(msg,0,sizeof(msg));
	            printf("input: ");
	            gets(msg);
	            write(c_fd,msg,strlen(msg));
	        }
		}
			
		while(1){ //可以一直读取数据
		    memset(readBuf,0,sizeof(readBuf));
		    n_read = read(c_fd,readBuf,128);
		    if(n_read == -1){
		     	perror("read");
		    }else{
		        printf("get message:%s\n",readBuf);
		    }
        }
	}
}

二、客户端代码

客户端IP地址和端口号也采用参数传递的方法,即

int main(int argc,char **argv)

1、头文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

全部都是所用函数头文件,没有则报错

2、创建套接字(Socket)

int c_fd;    
c_fd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
if(c_fd == -1){
    perror("socket"); //打印错误信息
    exit(-1);
}

c_fd 表示套接字描述符,AF_INET表示IPv4因特网协议,SOCK_STREAM表示使用TCP协议,0表示选择对应默认协议。

3、绑定IP地址和端口号到Socket

struct sockaddr_in c_addr;

c_addr.sin_family = AF_INET;//IPv4协议
c_addr.sin_port = htons(atoi(argv[2]));//配置端口号,
    							//htons函数为网络字节序和主机字节序的转化
inet_aton(argv[1],&c_addr.sin_addr);//将IP地址配置到c_addr.sin_addr          
bind(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));//绑定

和服务端一样,bind函数是绑定套接字和sockaddr_in结构体,bind函数里面第二个参数它需要的是sockaddr ,但是我们用的sockaddr _in,所以要强制转化为sockaddr 类型

4、客户端的连接(connect函数)

int cont;
cont = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr));
if(cont == -1){
	perror("connect"); //打印错误信息
    exit(-1);  //退出进程
}

connect函数用于绑定之后客户端,与服务器建立连接参数

5、交换数据

char readBuf[1024];
char msg[1024];
int n_read;
while(1){
    if(fork() == 0){ //创建子进程来进行数据的交换
    	while(1){  //一直向服务器写入数据
        	printf("input : "); //提示输入
            memset(msg,0,sizeof(msg)); //清空
            gets(msg);       //获取字符
            write(c_fd,msg,strlen(msg)); //将输入的内容写入到c_fd中
        }
    }
    while(1){
    	memset(readBuf,0,sizeof(readBuf)); //将readBuf清空
        n_read = read(c_fd,readBuf,128);  //将c_fd的内容读到readBuf中
        if(n_read == -1){
        	perror("read");      //打印错误信息
        }else{
        	printf("get message:%s\n",readBuf); //将从服务器读到的数据打印
        }
    }
}

这样一个简单的聊天软件就写好了,虽然没有页面,但是实现了服务器和客户端通过网络进行通信的功能,

三、运行结果

  • 35
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值