照着书上的代码写了一个简单的socket程序,实现的功能是客户端向服务器发送一个字符串,服务器向客户端返回字符串的大小。写这个程序主要目的是熟悉基本的socket函数的使用,以及学习使用makefile。
测试时发现了一个有趣的现象:关闭服务器程序后,如果马上再次运行服务器程序就会返回bind error错误。这是由于关闭程序后TCP连接并没有马上终止,而是要执行TCP“四次挥手”,这时的服务器端处于TIME_WAIT状态,所以此时的端口仍然被占用。这时候如果立即执行服务器程序,bind函数就会因为端口被占用而失败。所以要等待几分钟直到上一个TCP连接完全终止,然后再运行新的服务端程序。
下面贴上代码
tcp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8888 //侦听端口地址
#define BACKLOG 2 //侦听队列长度
void process_conn_server(int s);
int main(int argc, char *argv[])
{
int ss, sc;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int err;
pid_t pid;
//建立一个流式套接字
ss = socket(AF_INET, SOCK_STREAM, 0);
if(ss < 0){
printf("socket error\n");
return -1;
}
//设置服务器地址
bzero(&server_addr, sizeof(server_addr)); //清零
server_addr.sin_family = AF_INET; //地址族
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址
server_addr.sin_port = htons(PORT); //服务器端口
//绑定地址到套接字
err = bind(ss, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(err < 0){
printf("bind error\n");
return -1;
}
//设置侦听
err = listen(ss, BACKLOG);
if(err < 0){
printf("listen error\n");
return -1;
}
//主循环
for(;;){
socklen_t addrlen = sizeof(struct sockaddr);
sc = accept(ss, (struct sockaddr*)&client_addr, &addrlen);
if(sc < 0){
continue;
}
//建立新进程处理到来的连接
pid = fork();
if(pid == 0){
close(ss);
process_conn_server(sc);
}else{
close(sc);
}
}
}
tvp_client.c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8888
void process_conn_client(int s);
int main(int argc, char* argv[])
{
int s;
struct sockaddr_in server_addr; //服务器地址结构
s = socket(AF_INET, SOCK_STREAM, 0); //建立流式套接字
if(s < 0){
printf("socket error");
return -1;
}
//设置服务器地址
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
//将用户输入的点分十进制地址转换为二进制值
inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
//连接服务器
connect(s, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));
process_conn_client(s); //客户端处理过程
close(s); //关闭连接
return 0;
}
tcp_process.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
//服务器对客户端的处理
void process_conn_server(int s)
{
ssize_t size = 0;
char buffer[1024]; //数据缓冲区
for(;;){
size = read(s, buffer, 1024); //从套接字读取数据到缓冲区
if(size == 0)
return;
//构建响应字符,为接收到客户端字节的数量
sprintf(buffer, "%ld bytes altogether\n", size);
write(s, buffer, strlen(buffer)+1); //发送给客户端
}
}
//客户端的处理过程
void process_conn_client(int s)
{
ssize_t size = 0;
char buffer[1024]; //数据缓冲区
for(;;){
size = read(0, buffer, 1024); //从标准输入读取数据到缓冲区
if(size > 0){
write(s, buffer, size); //发送给服务器
size = read(s, buffer, 1024); //从服务器读取数据
write(1, buffer, size); //写入到标准输出
}
}
}
Makefile文件
all:client server
client:tcp_process.o tcp_client.o
gcc -o client tcp_process.o tcp_client.o
server:tcp_process.o tcp_server.o
gcc -o server tcp_process.o tcp_server.o
tcp_process.o:
gcc -c tcp_process.c -o tcp_process.o
clean:
rm -f client server *.o