您的位置:首页 > 理论基础 > 计算机网络

socket服务端、开源网络库和线程池

2015-11-04 09:30 453 查看
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.htmlhttp://blog.csdn.net/a574258039/article/details/19549407

6.在多线程编程中,如果有一个子线程出现了段错误,那么整个进程都会挂掉,因此,要想稳定运行多线程,最好使用线程池的思想,即初始化时创建固定数目的足够用的线程,主循环中轮流使用这些线程,程序结束时退出所有线程,如果在主循环中动态创建、销毁线程,很难维持稳定的7x24小时服务。C语言版的多线程可参考:简单Linux
C线程池 和 C语言实现简单线程池

7.跟大家分享几个http的client库,在Java里面主要使用httpclient,在C/C++里面类似的库有:

curl - too heavyweight
poco - too heavyweight
neon - GPL
qlibc - relies on POSIX libraries
cpp-netlib - relies on Boost libraries
serf- relies on the Apache Portable Runtime library
urdl - relies on Boost libraries
HTTP Client C API - promising but requires a C++ wrapper
HttpClient - HTTPClient on mbed

Simple HTTP client in C   

关于以上几个开源库,可参考:

http://stackoverflow.com/questions/23842394/c-c-http-client-library-for-embedded-projects


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: