最近在学习《深入理解计算机系统》,看到linux系统级的IO以及,socket通信,所以干脆参考书上例程,自己着手写一个客户端与服务器的小程序。
并未使用csapp封装好的open_clientfd与open_listenfd还有RIO包
主要功能就两个:
1.服务器读客户端送入的字符串,并通知客户端已经收到。
2.客户端能主动退出连接。
写这个小程序是为了练习socket通信的步骤。
服务器:
socket函数创建套接字
bind将socket创建的套接字与服务器套接字绑定
listen打开监听套接字
accept等待客户端connect //会阻塞
客户端:
socket创建套接字
connect将套接字与服务器ip地址绑定,建立Internet连接
之后客户端与服务器就能用各自的套接字,通过linux系统IO通信了!
运行的时候,客户端后需加服务器ip与端口,服务器需加开启的端口,分别为main函数参数
值得注意的是linux系统IO read默认情况下是阻塞的
服务器代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int echo(int connfd){
char *exit = "exit is getted";
write(connfd, "Please input", 100);
while(1){
char client_word[100];
read(connfd, client_word, 100);
printf("%s \n", client_word);
strcat(client_word, " is getted");
write(connfd, client_word, 100);
if(strcmp(client_word, exit) == 0){
printf("client exit\n");
return 1;
}else{
continue;
}
}
}
int main(int argc, char **argv){
int port, listenfd, connfd, clientlen;
struct sockaddr_in clientadder;
struct hostent *hp;
port = atoi(argv[1]);
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){//创建服务器套接字
printf( "socket error\n" );
}
clientadder.sin_family = AF_INET;//填充信息
clientadder.sin_addr.s_addr = htonl(INADDR_ANY);
clientadder.sin_port = htons(port);
if(bind(listenfd, (struct sockaddr *)&clientadder, sizeof(clientadder)) < 0){//绑定
printf( "bind error\n" );
}
if (listen(listenfd, 1024) < 0){//通知内核此套接字为服务器使用
printf( "listen error\n" );
}
while(1){
printf( "wait for client\n" );
clientlen = sizeof(clientadder);
if((connfd = accept(listenfd, (struct sockaddr *)&clientadder, &clientlen)) < 0){//监听
printf( "accept error\n" );
}else{
printf("connect successful!\n");
if (echo(connfd)){
printf("exit from echo\n");
}else{
printf("error from echo\n");
}
}
write(connfd, "exit", 100);
close(connfd);
}
printf("gg\n");
}
客户端代码:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv){
int port,clientfd;
struct hostent *hp;
char *EXIT = "exit is getted";
struct sockaddr_in serveraddr;
port = atoi(argv[2]);
if((hp = gethostbyname(argv[1])) == NULL){ //通过ip地址获取目的主机信息
printf("ip error\n");
}
if((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ //创建客户端套接字
printf("socket error\n");
}
bcopy((char *)hp->h_addr_list[0], (char *)&serveraddr.sin_addr.s_addr, hp->h_length);//填充信息
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
if(connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){//创建连接
printf("connect error\n");
}else{
printf("connect successful!\n");
while(1){
char buf[100];
char get[100];
read(clientfd, get, 100);
if(strcmp(get, EXIT) == 0){
printf("server exit\n");
close(clientfd);
return 0;
}
printf("%s \n", get);
scanf("%s", buf);
write(clientfd, buf, 100);
}
}
return 0;
}
通过查阅其他资料得知:
struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。
二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。
这样就稳了~