代码在下面已经进行了详细注释!(往后翻)
具体效果怎么看呢?
1.首先打开Linux虚拟机
2.用命令mkdir建立一个socket目录
3.在目录里面用 vi server.c 建立一个.c文件
4.把服务器的代码输进去(按i输入)
5. :wq保存退出
6. gcc server.c -o sever -Wall -g 对这个文件进行编译
-Wall 是输出警告信息
-g代表可执行程序包含调试信息
这样生成的可执行程序 sever,./sever 运行它就是在运行一个服务器。
7.利用alt+ctrl+t再打开一个命令框
8.输入:nc 127.0.0.1 9527(程序中的端口号)(前两个字母代表网络连接)
这样就和你写的服务器建立了连接
9.这时输入一个小写hello,就会返回一个大写的HELLO。另一个命令框会返回客户端IP和端口号!
10.实验成功!
在客户端那里:
1.在目录里面用 vi server.c 建立一个client.c文件
2.把客户端的代码输进去(按i输入)
3. :wq保存退出
4. gcc client.c -o client -Wall -g 对这个文件进行编译
最后:
利用两个窗口,一个./server运行服务器程序
一个./client运行客户端程序
最后就会在两端同时出现大写小写的hello出现9次。
实验成功!
服务器程序:
#include<stdio.h>//输入输出
#include<stdlib.h>//动态内存分配
#include<string.h>
//linux下有这个头文件,vc下没有
//它包含很多UNIX系统服务的函数原型,eg:read write函数等
#include<unistd.h>
#include<errno.h>
#include<pthread.h>//包含有关多线程的函数
#include <sys/socket.h>//套接字函数、bind函数头文件
#include<ctype.h>//转小写转大写函数的头文件,在第二个地址结构的时候用
#include<arpa/inet.h>//bind的头文件
#define SERV_PORT 9527 //bind函数中的端口号
char buf[BUFSIZ], clint_IP[1024];//用来保存读到的客户端的数组,BUFSIZ默认4096
//上面第二个数组用来接收客户端IP地址
void sys_err(const char *str)
{
//perror是用来输出错误的,如果某些函数调用不正确的话,调用perror会先输出错误号,然后输出你在perror()参数中指定的内容
perror(str);//包含在stdlib.h这个头文件中
exit(1);
}
int main(int argc, char *argv[])
{
int ifd =0,cfd=0,ret;//保存文件描述符,用于后期和服务器建立连接
socklen_t clit_addr_len;
//定义bind函数中用到的ip地址结构体
struct sockaddr_in serv_addr, clit_addr;
serv_addr.sin_family = AF_INET;//协议簇地址,和第三个参数的区别在于,这个是地址类型,第三个是有效实际地址
//TCP/IP是一个网络通讯协bai议群,它包含了很多通信协议。这些协du议制定了网zhi络设备、计算机连入网络以及数据是如何dao在它们之间进行传输的标准。
//TCP协议又叫传输控制协议,作用于传输层。IP协议又叫网络间互联协议,工作在网络层。它们都是TCP/IP协议簇中非常重要的协议。
serv_addr.sin_port = htons(SERV_PORT);//端口这里需要字节序的转换!需要来回传的都需要转换!
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//取出系统中任意有效的IP地址
//sin_family和sin_addr.s_addr的地址有什么区别?
//socket目的:创建一个套接字
//第一个参数是协议簇,代表网络层协议
//第二个参数是个协议,数据传输协议,代表传输层协议
//第三个参数是:你所选用的数据传输协议的代表协议,比如流式数据传输协议(SOCK_STREAM)的代表协议是tcp
//成功:返回指向新创建的socket的文件描述符,失败:返回-1,
ifd = socket(AF_INET, SOCK_STREAM, 0);
if(ifd==-1)
{
sys_err("socket error");
}
//bind作用:将socket和bind的第二个参数中的地址、端口号捆绑起来,以便监听
//第一个参数服务器端套接字描述符、 第二个参数是个结构体指针,里面包含服务器的IP地址+端口号、第三个参数是这个地址的长度
bind(ifd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));//和上面返回描述符不同,这个返回的是0
listen(ifd, 128);//第二个参数是它所能监听的最大上限数
clit_addr_len=sizeof(clit_addr);
//第二个参数是接收链接客户端地址信息,含IP地址和端口号,所以前面不需要专门定义
//返回一个套接字
cfd=accept(ifd, (struct sockaddr*)&clit_addr ,&clit_addr_len);//注意第三个参数类型,相比第bind多一个指针
if(cfd == -1)
{
sys_err("accept error");
}
//把接收到的客户端IP和端口号打印出来,都在上面的clit_addr里面
//inet_ntop目的是将二进制转换为10进制输出,第二个参数是客户的IP地址,第三个参数是用来接收的数组,第四个是长度
printf("clint IP:%s port:%d\n",inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,clint_IP,sizeof(clint_IP)),
ntohs(clit_addr.sin_port));
while(1)
{
ret=read(cfd,buf,sizeof(buf));//返回字节数
write(STDOUT_FILENO,buf,ret);标准输出到屏幕
for(int i=0; i<ret;i++)
{
buf[i]=toupper(buf[i]);
}
write(cfd,buf,ret);//把buf的内容写回到客户端
}
close(ifd);
close(cfd);
return 0;
}
客户端编程:
#include<stdio.h>
#include<stdlib.h>//动态内存分配
#include<string.h>
//linux下有这个头文件,vc下没有
//它包含很多UNIX系统服务的函数原型,eg:read write函数等
#include<unistd.h>
#include<errno.h>
#include<pthread.h>//包含有关多线程的函数
#include <sys/types.h> //connect函数中第二个参数用
#include <sys/socket.h>//套接字的函数
#include<arpa/inet.h>//bind的头文件、inet_pton函数头文件
#define SERV_PORT 9527 //bind函数中的端口号
char buf[BUFSIZ];
void sys_err(const char *str)
{
//perror是用来输出错误的,如果某些函数调用不正确的话,调用perror会先输出错误号,然后输出你在perror()参数中指定的内容
perror(str);//包含在stdlib.h这个头文件中
exit(1);
}
int main(int argc, char *argv[])
{
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;//协议簇地址,和第三个参数的区别在于,这个是地址类型,第三个是有效实际地址
serv_addr.sin_port = htons(SERV_PORT);//一定要和服务器的相同
//第二个参数是服务器的IP地址,要把这个参数传到第三个里面,然后就不用写下面这句了
//客户端绑定服务器的时候用inet_pton这个函数,服务器绑定的时候一定要用二进制的绑
inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr.s_addr);
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//取出系统中任意有效的IP地址
//创建套接字
int cfd=socket(AF_INET, SOCK_STREAM, 0);
if(cfd==-1)
{
sys_err("socket error");
}
//第二个参数:传入参数,指定服务器端地址信息,含IP地址和端口号
//成功返回0.失败返回-1
int ret= connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
if(ret==-1)
sys_err("connect error");
int conter=10;
while(conter--)
{
//第二个参数是写给服务器的,第三个是hello的长度
write(cfd,"hello\n",6);
//服务器会返回信息,把小写hello变为大写返回
ret = read(cfd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,ret);//标准输出到屏幕
sleep(1);
close(cfd);
return 0;
}