简单socket的尝试
参考博客 linux下socket编程归纳记录 这篇博客是我学长的学长写的,进过两天简单的学习后,我决定写一篇博客归纳一下暂时学到的知识。
双向通信
原博客简单地将socket的功能基本实现一遍,但是是单向通信的。我在看了个半懂不懂的情况下,觉得可以尝试着在源程序的基础上进行改造,在一些周折之后,如愿实现了一个可以双向通信的程序。
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int st = socket(AF_INET, SOCK_STREAM, 0); //初始化socket,
struct sockaddr_in addr; //定义一个IP地址的结构
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; //设置结构地址类型为TCP/IP地址
addr.sin_port = htons(8080); //指定一个端口号:8080,htons:将short类型从host字节类型到net字节类型转化
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //将字符串类型的IP地址转化为int,赋给addr结构成员.
// 调用connect连接到结构addr(即服务器)指定的IP地址和端口号
if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
printf("connect failed %s\n", strerror(errno));
return EXIT_FAILURE;
}
char s[1024]; // 通信时传输的文本
memset(s, 0, sizeof(1024));
while (strcmp(s, "exit")) // 当自身输入exit后将终止通信,随后关闭用于连接服务器的套接字
{
printf("myself: "); // 输入提示
scanf("%s", s); // 输入文本
printf("\n");
s[strlen(s)] = '\0'; // 在文本末尾加入终止符
if (send(st, s, strlen(s)+1, 0) == -1)//发送buf的数据
{
printf("send failed %s\n", strerror(errno));
return EXIT_FAILURE;
}
if (!strcmp(s, "exit")) // 如果服务器发来exit的文本内容,则终止通信
{
break;
}
recv(st, s, sizeof(s), 0);
printf("server: %s\n\n", s);
}
close(st); //关闭socket
return EXIT_SUCCESS;
}
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int arg, char *args[])
{
int st = socket(AF_INET, SOCK_STREAM, 0);//初始化一个socket
int on = 1;
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
{
printf("setsockopt failed %s\n", strerror(errno));
return EXIT_FAILURE;
}
struct sockaddr_in addr; //定义一个IP地址结构
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; //将addr结构的属性定位为TCP/IP地址
addr.sin_port = htons(8080); //将本地字节顺序转化为网络字节顺序,网络字节序是大端字节序,x86是小端字节序
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //INADDR_ANY代表这个server上所有的地址
//将IP与server程序绑定
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
printf("bind failed %s\n", strerror(errno));
return EXIT_FAILURE;
}
//server端开始listen,
if (listen(st, 20) == -1)
{
printf("listen failed %s\n", strerror(errno));
return EXIT_FAILURE;
}
char s[1024];
int client_st = 0;//client端socket
//socklen_t len = 0;
struct sockaddr_in client_addr;//表示client端的IP地址
//void *p = &client_addr;
int i;
memset(&client_addr, 0, sizeof(client_addr));
socklen_t len = sizeof(client_addr);
client_st = accept(st, (struct sockaddr *)&client_addr, &len);
while (1)
{
if (client_st == -1)
{
printf("accept failed %s\n", strerror(errno));
return EXIT_FAILURE;
}
memset(s, 0, sizeof(1024));
int rc = recv(client_st, s, sizeof(s), 0);
printf("client: %s\n\n", s);
if (!strcmp(s, "exit"))
{
break;
}
printf("myslef: ");
scanf("%s", s);
printf("\n");
// 这里需要注意一点,不能将服务器本身的嵌套字作为媒介,为是要将接收客户端的嵌套字发送回去,否则会造成程序终止
send(client_st, s, strlen(s)+1, 0);
}
close(client_st);
close(st);//关闭server端listen的socket
return EXIT_SUCCESS;
}
改造好后的程序的局限
这个程序虽然能进行双向通信,但是还是具有很多局限性,比如说,当服务器端(客户端)在向对方发送给了一条文本消息后,必须等待对方的回应的文本消息,才能继续输入条文本消息发送给对方,如果对方不发送消息回应的话,服务器端(客户端)就不能继续输入文本并发送。
我个人的理解是需要用到多线程编程,比如将接收消息和发送消息放到两个不同的线程中,这样的话就可以将两者相对独立开来。我前段日子有简单地接触过多线程编程,但是是在windows平台下编程的,下等我进一步理解完socket的工作原理后,我准备结合多线程完善一下这个小聊天程序。