一、服务端代码
因为端口号容易被占用的原因,所以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); //将从服务器读到的数据打印
}
}
}
这样一个简单的聊天软件就写好了,虽然没有页面,但是实现了服务器和客户端通过网络进行通信的功能,