java版本的socket服务端
public class Main { private static ServerSocket serverSocket; private final static ExecutorService exec = Executors.newFixedThreadPool(30); public static void main(String[] args) { try { serverSocket = new ServerSocket(8888); while (true) { Socket socket = serverSocket.accept(); exec.execute(new ServerRunnable(socket)); } } catch (IOException e) { e.printStackTrace(); } } } class ServerRunnable implements Runnable { private Socket socket; private InputStream is; private OutputStream out; private String reqStr; private String resContent; public ServerRunnable(Socket socket) { this.socket = socket; } @Override public void run() { handleSocket(socket); } private void handleSocket(Socket socket) { try { byte[] buffer = new byte[1024]; is = socket.getInputStream(); System.out.println(is); out = socket.getOutputStream(); int len = 0; StringBuilder sb = new StringBuilder(); while ((len = is.read(buffer)) != -1) { String str = new String(buffer, 0, len); sb.append(str); } reqStr = sb.toString(); System.out.println(reqStr); resContent = "Welcome!"; StringBuilder resBuilder = new StringBuilder(); resBuilder.append("HTTP/1.1 200 OK").append("\r\n"). append("Date:").append(new Date()).append("\r\n"). append("Content-Type:").append("text/plain;charset=UTF-8").append("\r\n"). append("Content-Length:").append(resContent.getBytes().length).append("\r\n"). append("\r\n"); resBuilder.append(resContent); out.write(resBuilder.toString().getBytes()); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
C语言版的socket服务端
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> int main( int argc, char *argv[] ) { int sockfd, newsockfd, portno, clilen; char buffer[256]; struct sockaddr_in serv_addr, cli_addr; int n; /* First call to socket() function */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("ERROR opening socket"); exit(1); } /* Initialize socket structure */ bzero((char *) &serv_addr, sizeof(serv_addr)); portno = 5001; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(portno); /* Now bind the host address using bind() call.*/ if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { perror("ERROR on binding"); exit(1); } /* Now start listening for the clients, here * process will go in sleep mode and will wait * for the incoming connection */ listen(sockfd,5); clilen = sizeof(cli_addr); while (1) { newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) { perror("ERROR on accept"); exit(1); } /* Create child process */ pid = fork(); if (pid < 0) { perror("ERROR on fork"); exit(1); } if (pid == 0) { /* This is the client process */ close(sockfd); doprocessing(newsockfd); exit(0); } else { close(newsockfd);//shutdown(newsockfd);或许会更好 } } /* end of while */ } void doprocessing (int sock) { int n; char buffer[256]; //bzero(buffer,256); while(1){ printf("waiting for request...\n"); //Reset data. memset(data_recv,0,256); i_recvBytes = read(sock,buffer,255); if(i_recvBytes == 0){ printf("Maybe the client has closed\n"); break; } if(i_recvBytes == -1){ fprintf(stderr,"read error!\n"); break; } if(strcmp(data_recv,"quit")==0){ printf("Quit command!\n"); break; //Break the while loop. } printf("read from client : %s\n",data_recv); if(write(fd,data_send,strlen(data_send)) == -1){ break; } } }
参考1:http://m.blog.csdn.net/blog/onlyonename/7080955
参考2:http://blog.csdn.net/yutianzuijin/article/details/24807417
注意:
1.accept是阻塞函数,会阻塞当前的进程直到有连接到来为止,一旦有客户端的连接accept就返回一个连接socket,即newsockfd,之后处理连接的进程会读取此newsockfd,获取客户端的数据。
2.服务端为了支持多个客户端的访问连接,需要将accept放在一个循环中,每当accept返回一个连接socket后就继续等待下一个客户端的访问。
3.tcp数据包在网络传输的过程中存在分割数据包的情况,即一个数据包分多次发送,因此在doprocessing函数内的read函数需要在一个while循环读里面。
4.fork出多进程来,返回值如果为0则为子进程,返回值为如果不是0,则是父进程,并且此返回值是子进程的PID。子进程和父进程只共享代码段,以及父进程的所有打开的文件描述符(慎用),不共享数据段、栈和堆,因此,在子进程中要关闭监听socket,在父进程中要关闭连接socket。
作为与fork的一个区别, POSIX通过pthread_create()函数创建线程,API定义如下:
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线 程)同样的执行序列,而是使其运行start_routine(arg)函数。thread返回创建的线程ID,而attr是创建线程时设置的线程属性 (见下)。pthread_create()的返回值表示线程创建是否成功。尽管arg是void *类型的变量,但它同样可以作为任意类型的参数传给start_routine()函数;同时,start_routine()可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由pthread_join()获取。
5.跟大家分享几个C语言的开源网络库:spserver(c++), libevent(c语言), libev(c语言),libuv(c语言),ace(c++)简单调用这几个库的API即可实现可用于生产环境上的socket服务器,作为服务器性能压力测试的一款开源软件是jmeter,其方便好用性堪比loadrunner。现有版本的jmeter暂不支持对socket的压力测试,需要额外开发扩展插件,方法如下:http://www.cnblogs.com/linglingyuese/p/linglingyuese_sex.html或http://blog.csdn.net/a574258039/article/details/19549407
6.在多线程编程中,如果有一个子线程出现了段错误,那么整个进程都会挂掉,因此,要想稳定运行多线程,最好使用线程池的思想,即初始化时创建固定数目的足够用的线程,主循环中轮流使用这些线程,程序结束时退出所有线程,如果在主循环中动态创建、销毁线程,很难维持稳定的7x24小时服务。C语言版的多线程可参考:简单Linux C线程池 和 C语言实现简单线程池。
7.跟大家分享几个http的client库,在Java里面主要使用httpclient,在C/C++里面类似的库有:
关于以上几个开源库,可参考:
http://stackoverflow.com/questions/23842394/c-c-http-client-library-for-embedded-projects